iMSTK
Interactive Medical Simulation Toolkit
imstkVTKPointSetRenderDelegate.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 "imstkVTKPointSetRenderDelegate.h"
8 #include "imstkPointSet.h"
9 #include "imstkVisualModel.h"
10 #include "imstkGeometryUtilities.h"
11 #include "imstkRenderMaterial.h"
12 
13 #include <array>
14 #include <vtkActor.h>
15 #include <vtkDoubleArray.h>
16 #include <vtkOpenGLPolyDataMapper.h>
17 #include <vtkOpenGLVertexBufferObject.h>
18 #include <vtkPointData.h>
19 #include <vtkTransform.h>
20 #include <vtkVertexGlyphFilter.h>
21 
22 namespace imstk
23 {
24 VTKPointSetRenderDelegate::VTKPointSetRenderDelegate() :
25  m_geometry(nullptr),
26  m_vertices(nullptr),
27  m_vertexScalars(nullptr),
28  m_polydata(vtkSmartPointer<vtkPolyData>::New()),
29  m_mappedVertexArray(vtkSmartPointer<vtkDoubleArray>::New()),
30  m_mappedVertexScalarArray(vtkSmartPointer<vtkDoubleArray>::New())
31 {
32 }
33 
34 void
35 VTKPointSetRenderDelegate::init()
36 {
37  m_geometry = std::dynamic_pointer_cast<PointSet>(m_visualModel->getGeometry());
38  CHECK(m_geometry != nullptr) << "VTKPointSetRenderDelegate only works with PointSet geometry";
39 
40  // Get our own handles to these in case the geometry changes them
41  m_vertices = m_geometry->getVertexPositions();
42 
43  // Map vertices to VTK point data
44  if (m_vertices != nullptr)
45  {
46  m_mappedVertexArray = vtkDoubleArray::SafeDownCast(GeometryUtils::coupleVtkDataArray(m_vertices));
47  auto points = vtkSmartPointer<vtkPoints>::New();
48  points->SetNumberOfPoints(m_geometry->getNumVertices());
49  points->SetData(m_mappedVertexArray);
50  m_polydata->SetPoints(points);
51  }
52 
53  // Map vertex scalars if it has them
54  if (m_geometry->getVertexScalars() != nullptr)
55  {
56  setVertexScalarBuffer(m_geometry->getVertexScalars());
57  }
58 
59  // \todo: Slow, replace with opengl hardware instancing which is actually an OpenGLMapper
60  vtkNew<vtkVertexGlyphFilter> glyphFilter;
61  glyphFilter->SetInputData(m_polydata);
62  glyphFilter->Update();
63 
64  // When geometry is modified, update data source, mostly for when an entirely new array/buffer was set
65  queueConnect<Event>(m_geometry, &Geometry::modified,
66  std::static_pointer_cast<VTKPointSetRenderDelegate>(shared_from_this()),
68 
69  // When the vertex buffer internals are modified, ie: a single or N elements
70  queueConnect<Event>(m_geometry->getVertexPositions(), &VecDataArray<double, 3>::modified,
71  std::static_pointer_cast<VTKPointSetRenderDelegate>(shared_from_this()),
73 
74  // Setup mapper
75  {
76  vtkNew<vtkPolyDataMapper> mapper;
77  mapper->SetInputConnection(glyphFilter->GetOutputPort());
78  vtkNew<vtkActor> actor;
79  actor->SetMapper(mapper);
80  actor->SetUserTransform(m_transform);
81  m_mapper = mapper;
82  m_actor = actor;
83  if (auto glMapper = vtkOpenGLPolyDataMapper::SafeDownCast(m_mapper.GetPointer()))
84  {
85  glMapper->SetVBOShiftScaleMethod(vtkOpenGLVertexBufferObject::DISABLE_SHIFT_SCALE);
86  }
87  }
88 
89  update();
91 }
92 
93 void
95 {
96  // Custom handling of events
97  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = m_geometry->getVertexPositions();
98  std::shared_ptr<AbstractDataArray> vertexScalarsPtr = m_geometry->getVertexScalars();
99 
100  // Only use the most recent event from respective sender
101  std::array<Command, 5> cmds;
102  std::array<bool, 5> contains = { false, false, false, false, false };
103  rforeachEvent([&](Command cmd)
104  {
105  if (cmd.m_event->m_sender == m_visualModel.get() && !contains[0])
106  {
107  cmds[0] = cmd;
108  contains[0] = true;
109  }
110  else if (cmd.m_event->m_sender == m_material.get() && !contains[1])
111  {
112  cmds[1] = cmd;
113  contains[1] = true;
114  }
115  else if (cmd.m_event->m_sender == m_geometry.get() && !contains[2])
116  {
117  cmds[2] = cmd;
118  contains[2] = true;
119  }
120  else if (cmd.m_event->m_sender == verticesPtr.get() && !contains[3])
121  {
122  cmds[3] = cmd;
123  contains[3] = true;
124  }
125  else if (cmd.m_event->m_sender == vertexScalarsPtr.get() && !contains[4])
126  {
127  cmds[4] = cmd;
128  contains[4] = true;
129  }
130  });
131 
132  // Now do all the commands
133  cmds[0].invoke(); // Update VisualModel
134  cmds[1].invoke(); // Update RenderMaterial
135  cmds[3].invoke(); // Update vertices
136  cmds[4].invoke(); // Update vertex scalars
137  cmds[2].invoke(); // Update geometry as a whole
138 }
139 
140 void
142 {
143  setVertexBuffer(m_geometry->getVertexPositions());
144 }
145 
146 void
147 VTKPointSetRenderDelegate::vertexScalarsModified(Event* imstkNotUsed(e))
148 {
149  setVertexScalarBuffer(m_geometry->getVertexScalars());
150 }
151 
152 void
154 {
155  // If the vertices were reallocated
156  if (m_vertices != m_geometry->getVertexPositions())
157  {
158  setVertexBuffer(m_geometry->getVertexPositions());
159  }
160 
161  // Assume vertices are always changed
162  m_mappedVertexArray->Modified();
163 
164  if (m_vertexScalars != m_geometry->getVertexScalars())
165  {
166  setVertexScalarBuffer(m_geometry->getVertexScalars());
167  }
168 }
169 
170 void
171 VTKPointSetRenderDelegate::setVertexBuffer(std::shared_ptr<VecDataArray<double, 3>> vertices)
172 {
173  // If the buffer changed
174  if (m_vertices != vertices)
175  {
176  // If previous buffer exist
177  if (m_vertices != nullptr)
178  {
179  // stop observing its changes
180  disconnect(m_vertices,
181  std::static_pointer_cast<VTKPointSetRenderDelegate>(shared_from_this()),
183  }
184  // Set new buffer and observe
185  m_vertices = vertices;
186  queueConnect<Event>(m_vertices, &VecDataArray<double, 3>::modified,
187  std::static_pointer_cast<VTKPointSetRenderDelegate>(shared_from_this()),
189  }
190 
191  // Couple the buffer
192  m_mappedVertexArray->SetNumberOfComponents(3);
193  m_mappedVertexArray->SetArray(reinterpret_cast<double*>(m_vertices->getPointer()), m_vertices->size() * 3, 1);
194  m_mappedVertexArray->Modified();
195  m_polydata->GetPoints()->SetNumberOfPoints(m_vertices->size());
196 }
197 
198 void
199 VTKPointSetRenderDelegate::setVertexScalarBuffer(std::shared_ptr<AbstractDataArray> scalars)
200 {
201  // If the buffer changed
202  if (m_vertexScalars != scalars)
203  {
204  // If previous buffer exist
205  if (m_vertexScalars != nullptr)
206  {
207  // stop observing its changes
208  disconnect(m_vertexScalars,
209  std::static_pointer_cast<VTKPointSetRenderDelegate>(shared_from_this()),
210  &AbstractDataArray::modified);
211  }
212  // Set new buffer and observe
213  m_vertexScalars = scalars;
214  queueConnect<Event>(m_vertexScalars, &AbstractDataArray::modified,
215  std::static_pointer_cast<VTKPointSetRenderDelegate>(shared_from_this()),
216  &VTKPointSetRenderDelegate::vertexScalarsModified);
217  m_mappedVertexScalarArray = GeometryUtils::coupleVtkDataArray(m_vertexScalars);
218  m_polydata->GetPointData()->SetScalars(m_mappedVertexScalarArray);
219  }
220  m_mappedVertexScalarArray->SetNumberOfComponents(m_vertexScalars->getNumberOfComponents());
221  m_mappedVertexScalarArray->SetVoidArray(m_vertexScalars->getVoidPointer(),
222  static_cast<vtkIdType>(m_vertexScalars->size()), 1);
223  m_mappedVertexScalarArray->Modified();
224 }
225 } // namespace imstk
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 for 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 vertexDataModified(Event *e)
Callback for when vertex data changes.
Delegates rendering of PointSet to VTK from VisualModel.
void updateRenderProperties() override
Updates the actor and mapper properties from the currently set VisualModel.
void processEvents() override
Update polydata source based on the mesh geometry.
Stores everything needed to invoke an event A call may not be present, in which case invoke doesn&#39;t d...