iMSTK
Interactive Medical Simulation Toolkit
imstkVTKLineMeshRenderDelegate.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkVTKLineMeshRenderDelegate.h"
8 #include "imstkLineMesh.h"
9 #include "imstkVisualModel.h"
10 #include "imstkRenderMaterial.h"
11 #include "imstkGeometryUtilities.h"
12 
13 #include <vtkActor.h>
14 #include <vtkCellData.h>
15 #include <vtkDoubleArray.h>
16 #include <vtkLineSource.h>
17 #include <vtkOpenGLPolyDataMapper.h>
18 #include <vtkOpenGLVertexBufferObject.h>
19 #include <vtkPointData.h>
20 #include <vtkTransform.h>
21 
22 namespace imstk
23 {
24 VTKLineMeshRenderDelegate::VTKLineMeshRenderDelegate() :
25  m_polydata(vtkSmartPointer<vtkPolyData>::New()),
26  m_mappedVertexArray(vtkSmartPointer<vtkDoubleArray>::New())
27 {
28 }
29 
30 void
31 VTKLineMeshRenderDelegate::init()
32 {
33  m_geometry = std::dynamic_pointer_cast<LineMesh>(m_visualModel->getGeometry());
34  CHECK(m_geometry != nullptr) << "VTKLineMeshRenderDelegate only works with LineMesh geometry";
35 
36  // Get our own handles to these in case the geometry changes them
37  m_vertices = m_geometry->getVertexPositions();
38  m_indices = m_geometry->getCells();
39 
40  // Map vertices to VTK point data
41  if (m_vertices != nullptr)
42  {
43  m_mappedVertexArray = vtkDoubleArray::SafeDownCast(GeometryUtils::coupleVtkDataArray(m_vertices));
44  auto points = vtkSmartPointer<vtkPoints>::New();
45  points->SetNumberOfPoints(m_geometry->getNumVertices());
46  points->SetData(m_mappedVertexArray);
47  m_polydata->SetPoints(points);
48  }
49 
50  // Map indices to VTK cell data (copied)
51  if (m_indices != nullptr)
52  {
53  m_cellArray = vtkSmartPointer<vtkCellArray>::New();
54  vtkIdType cell[2];
55  for (const auto& t : *m_indices)
56  {
57  for (size_t i = 0; i < 2; i++)
58  {
59  cell[i] = t[i];
60  }
61  m_cellArray->InsertNextCell(2, cell);
62  }
63  m_polydata->SetLines(m_cellArray);
64  }
65 
66  // Map vertex scalars if it has them
67  if (m_geometry->getVertexScalars() != nullptr)
68  {
69  setVertexScalarBuffer(m_geometry->getVertexScalars());
70  }
71 
72  // Map cell scalars if it has them
73  if (m_geometry->getCellScalars() != nullptr)
74  {
75  setCellScalarBuffer(m_geometry->getCellScalars());
76  }
77 
78  // When geometry is modified, update data source, mostly for when an entirely new array/buffer was set
79  queueConnect<Event>(m_geometry, &Geometry::modified,
80  std::static_pointer_cast<VTKLineMeshRenderDelegate>(shared_from_this()),
82 
83  // When the vertex buffer internals are modified, ie: a single or N elements
84  queueConnect<Event>(m_geometry->getVertexPositions(), &VecDataArray<double, 3>::modified,
85  std::static_pointer_cast<VTKLineMeshRenderDelegate>(shared_from_this()),
87 
88  // When index buffer internals are modified
89  queueConnect<Event>(m_geometry->getCells(), &VecDataArray<int, 2>::modified,
90  std::static_pointer_cast<VTKLineMeshRenderDelegate>(shared_from_this()),
91  &VTKLineMeshRenderDelegate::indexDataModified);
92 
93  // Setup mapper
94  {
95  vtkNew<vtkPolyDataMapper> mapper;
96  mapper->SetInputData(m_polydata);
97  vtkNew<vtkActor> actor;
98  actor->SetMapper(mapper);
99  actor->SetUserTransform(m_transform);
100  m_mapper = mapper;
101  m_actor = actor;
102  if (auto glMapper = vtkOpenGLPolyDataMapper::SafeDownCast(m_mapper.GetPointer()))
103  {
104  glMapper->SetVBOShiftScaleMethod(vtkOpenGLVertexBufferObject::DISABLE_SHIFT_SCALE);
105  }
106  }
107 
108  update();
110 }
111 
112 void
114 {
115  // Custom handling of events
116  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = m_geometry->getVertexPositions();
117  std::shared_ptr<VecDataArray<int, 2>> indicesPtr = m_geometry->getCells();
118  std::shared_ptr<AbstractDataArray> cellScalarsPtr = m_geometry->getCellScalars();
119  std::shared_ptr<AbstractDataArray> vertexScalarsPtr = m_geometry->getVertexScalars();
120 
121  // Only use the most recent event from respective sender
122  std::array<Command, 7> cmds;
123  std::array<bool, 7> contains = { false, false, false, false, false, false, false };
124  rforeachEvent([&](Command cmd)
125  {
126  if (cmd.m_event->m_sender == m_visualModel.get() && !contains[0])
127  {
128  cmds[0] = cmd;
129  contains[0] = true;
130  }
131  else if (cmd.m_event->m_sender == m_material.get() && !contains[1])
132  {
133  cmds[1] = cmd;
134  contains[1] = true;
135  }
136  else if (cmd.m_event->m_sender == m_geometry.get() && !contains[2])
137  {
138  cmds[2] = cmd;
139  contains[2] = true;
140  }
141  else if (cmd.m_event->m_sender == verticesPtr.get() && !contains[3])
142  {
143  cmds[3] = cmd;
144  contains[3] = true;
145  }
146  else if (cmd.m_event->m_sender == cellScalarsPtr.get() && !contains[4])
147  {
148  cmds[4] = cmd;
149  contains[4] = true;
150  }
151  else if (cmd.m_event->m_sender == vertexScalarsPtr.get() && !contains[5])
152  {
153  cmds[5] = cmd;
154  contains[5] = true;
155  }
156  else if (cmd.m_event->m_sender == indicesPtr.get() && !contains[6])
157  {
158  cmds[6] = cmd;
159  contains[6] = true;
160  }
161  });
162 
163  // Now do all the commands
164  cmds[0].invoke(); // Update VisualModel
165  cmds[1].invoke(); // Update RenderMaterial
166  cmds[3].invoke(); // Update vertices
167  cmds[4].invoke(); // Update cell scalars
168  cmds[5].invoke(); // Update vertex scalars
169  cmds[6].invoke(); // Update indices
170  cmds[2].invoke(); // Update geometry as a whole
171 }
172 
173 void
175 {
176  setVertexBuffer(m_geometry->getVertexPositions());
177 }
178 
179 void
180 VTKLineMeshRenderDelegate::indexDataModified(Event* imstkNotUsed(e))
181 {
182  setIndexBuffer(m_geometry->getCells());
183 }
184 
185 void
186 VTKLineMeshRenderDelegate::vertexScalarsModified(Event* imstkNotUsed(e))
187 {
188  setVertexScalarBuffer(m_geometry->getVertexScalars());
189 }
190 
191 void
192 VTKLineMeshRenderDelegate::cellScalarsModified(Event* imstkNotUsed(e))
193 {
194  setCellScalarBuffer(m_geometry->getCellScalars());
195 }
196 
197 void
199 {
200  // If the vertices were reallocated
201  if (m_vertices != m_geometry->getVertexPositions())
202  {
203  setVertexBuffer(m_geometry->getVertexPositions());
204  }
205 
206  // Assume vertices are always changed
207  m_mappedVertexArray->Modified();
208 
209  // Only update index buffer when reallocated
210  if (m_indices != m_geometry->getCells())
211  {
212  setIndexBuffer(m_geometry->getCells());
213  }
214  if (m_vertexScalars != m_geometry->getVertexScalars())
215  {
216  setVertexScalarBuffer(m_geometry->getVertexScalars());
217  }
218  if (m_cellScalars != m_geometry->getCellScalars())
219  {
220  setCellScalarBuffer(m_geometry->getCellScalars());
221  }
222 }
223 
224 void
225 VTKLineMeshRenderDelegate::setVertexBuffer(std::shared_ptr<VecDataArray<double, 3>> vertices)
226 {
227  // If the buffer changed
228  if (m_vertices != vertices)
229  {
230  // If previous buffer exist
231  if (m_vertices != nullptr)
232  {
233  // stop observing its changes
234  disconnect(m_vertices,
235  std::static_pointer_cast<VTKLineMeshRenderDelegate>(shared_from_this()),
237  }
238  // Set new buffer and observe
239  m_vertices = vertices;
240  queueConnect<Event>(m_vertices, &VecDataArray<double, 3>::modified,
241  std::static_pointer_cast<VTKLineMeshRenderDelegate>(shared_from_this()),
243  }
244 
245  // Couple the buffer
246  m_mappedVertexArray->SetNumberOfComponents(3);
247  m_mappedVertexArray->SetArray(reinterpret_cast<double*>(m_vertices->getPointer()), m_vertices->size() * 3, 1);
248  m_mappedVertexArray->Modified();
249  m_polydata->GetPoints()->SetNumberOfPoints(m_vertices->size());
250 }
251 
252 void
253 VTKLineMeshRenderDelegate::setIndexBuffer(std::shared_ptr<VecDataArray<int, 2>> indices)
254 {
255  // If the buffer changed
256  if (m_indices != indices)
257  {
258  // If previous buffer exist
259  if (m_indices != nullptr)
260  {
261  // stop observing its changes
262  disconnect(m_indices,
263  std::static_pointer_cast<VTKLineMeshRenderDelegate>(shared_from_this()),
265  }
266  // Set new buffer and observe
267  m_indices = indices;
268  queueConnect<Event>(m_indices, &VecDataArray<int, 2>::modified,
269  std::static_pointer_cast<VTKLineMeshRenderDelegate>(shared_from_this()),
270  &VTKLineMeshRenderDelegate::indexDataModified);
271  }
272 
273  // Copy the buffer
274  // Copy cells
275  m_cellArray->Reset();
276  vtkIdType cell[3];
277  for (const auto& t : *m_indices)
278  {
279  for (size_t i = 0; i < 2; i++)
280  {
281  cell[i] = t[i];
282  }
283  m_cellArray->InsertNextCell(2, cell);
284  }
285  m_cellArray->Modified();
286 }
287 
288 void
289 VTKLineMeshRenderDelegate::setVertexScalarBuffer(std::shared_ptr<AbstractDataArray> scalars)
290 {
291  // If the buffer changed
292  if (m_vertexScalars != scalars)
293  {
294  // If previous buffer exist
295  if (m_vertexScalars != nullptr)
296  {
297  // stop observing its changes
298  disconnect(m_vertexScalars,
299  std::static_pointer_cast<VTKLineMeshRenderDelegate>(shared_from_this()),
300  &AbstractDataArray::modified);
301  }
302  // Set new buffer and observe
303  m_vertexScalars = scalars;
304  queueConnect<Event>(m_vertexScalars, &AbstractDataArray::modified,
305  std::static_pointer_cast<VTKLineMeshRenderDelegate>(shared_from_this()),
306  &VTKLineMeshRenderDelegate::vertexScalarsModified);
307  m_mappedVertexScalarArray = GeometryUtils::coupleVtkDataArray(m_vertexScalars);
308  m_polydata->GetPointData()->SetScalars(m_mappedVertexScalarArray);
309  }
310  m_mappedVertexScalarArray->SetNumberOfComponents(m_vertexScalars->getNumberOfComponents());
311  m_mappedVertexScalarArray->SetVoidArray(m_vertexScalars->getVoidPointer(),
312  static_cast<vtkIdType>(m_vertexScalars->size()), 1);
313  m_mappedVertexScalarArray->Modified();
314 }
315 
316 void
317 VTKLineMeshRenderDelegate::setCellScalarBuffer(std::shared_ptr<AbstractDataArray> scalars)
318 {
319  // If the buffer changed
320  if (m_cellScalars != scalars)
321  {
322  // If previous buffer exist
323  if (m_cellScalars != nullptr)
324  {
325  // stop observing its changes
326  disconnect(m_cellScalars,
327  std::static_pointer_cast<VTKLineMeshRenderDelegate>(shared_from_this()),
328  &AbstractDataArray::modified);
329  }
330  // Set new buffer and observe
331  m_cellScalars = scalars;
332  queueConnect<Event>(m_cellScalars, &AbstractDataArray::modified,
333  std::static_pointer_cast<VTKLineMeshRenderDelegate>(shared_from_this()),
334  &VTKLineMeshRenderDelegate::cellScalarsModified);
335  m_mappedCellScalarArray = GeometryUtils::coupleVtkDataArray(m_cellScalars);
336  m_polydata->GetCellData()->SetScalars(m_mappedCellScalarArray);
337  }
338 
339  m_mappedCellScalarArray->SetNumberOfComponents(m_cellScalars->getNumberOfComponents());
340  m_mappedCellScalarArray->SetVoidArray(m_cellScalars->getVoidPointer(),
341  static_cast<vtkIdType>(m_cellScalars->size()), 1);
342  m_mappedCellScalarArray->Modified();
343 }
344 } // namespace imstk
Delegates rendering of LineMesh to VTK from VisualModel.
void rforeachEvent(std::function< void(Command cmd)> func)
thread safe reverse loop over all event commands, one can implement a custom handler ...
void geometryModified(Event *e)
Callback when geometry changes.
void update()
Update render delegate.
friend void disconnect(std::shared_ptr< EventObject >, std::shared_ptr< EventObject >, std::string(*)())
Remove an observer from the sender.
vtkSmartPointer< vtkDataArray > coupleVtkDataArray(std::shared_ptr< AbstractDataArray > imstkArray)
Coupling functions, these create vtk data objects that point to our data objects thus no copying is d...
std::shared_ptr< VisualModel > m_visualModel
imstk visual model (contains data (geometry) and render specification (render material)) ...
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
Compound Geometry.
void updateRenderProperties() override
Updates the actor and mapper properties from the currently set VisualModel.
void vertexDataModified(Event *e)
Callback for when vertex values are modified.
Stores everything needed to invoke an event A call may not be present, in which case invoke doesn&#39;t d...