iMSTK
Interactive Medical Simulation Toolkit
OctreeIntersectionExample.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 "imstkCamera.h"
8 #include "imstkDirectionalLight.h"
9 #include "imstkKeyboardDeviceClient.h"
10 #include "imstkKeyboardSceneControl.h"
11 #include "imstkLooseOctree.h"
12 #include "imstkMouseDeviceClient.h"
13 #include "imstkMouseSceneControl.h"
14 #include "imstkNew.h"
15 #include "imstkRenderMaterial.h"
16 #include "imstkScene.h"
17 #include "imstkSceneManager.h"
18 #include "imstkSceneObject.h"
19 #include "imstkSimulationManager.h"
20 #include "imstkSimulationUtils.h"
21 #include "imstkSurfaceMesh.h"
22 #include "imstkTextVisualModel.h"
23 #include "imstkVisualModel.h"
24 #include "imstkVTKViewer.h"
25 #include "OctreeDebugModel.h"
26 
27 using namespace imstk;
28 
29 //#define NUM_MESHES 4u
30 #define NUM_MESHES 10u
31 
32 // Load bunny mesh data (vertex positions and triangle faces)
33 std::pair<std::shared_ptr<VecDataArray<double, 3>>, std::shared_ptr<VecDataArray<int, 3>>> getBunny();
34 static std::pair<std::shared_ptr<VecDataArray<double, 3>>, std::shared_ptr<VecDataArray<int, 3>>> g_BunnyData = getBunny();
35 
39 std::shared_ptr<SceneObject>
40 createMeshObject(const std::string& objectName,
41  const Color& color)
42 {
43  // Create a surface mesh for the bunny
44  imstkNew<SurfaceMesh> surfMesh;
45  std::shared_ptr<VecDataArray<double, 3>> verticesPtr = std::make_shared<VecDataArray<double, 3>>();
46  *verticesPtr = *g_BunnyData.first;
47  std::shared_ptr<VecDataArray<int, 3>> indicesPtr = std::make_shared<VecDataArray<int, 3>>();
48  *indicesPtr = *g_BunnyData.second;
49  surfMesh->initialize(verticesPtr, indicesPtr);
50 
51  // Create a visual model
52  imstkNew<VisualModel> visualModel;
53  visualModel->setGeometry(surfMesh);
54  imstkNew<RenderMaterial> material;
55  material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
56  material->setColor(color); // Wireframe color
57  material->setLineWidth(1.0);
58  visualModel->setRenderMaterial(material);
59 
60  imstkNew<SceneObject> visualObject(objectName);
61  visualObject->addVisualModel(visualModel);
62 
63  return visualObject;
64 }
65 
69 Color
70 getRandomColor()
71 {
72  Color color(0, 0, 0, 1);
73  while (true)
74  {
75  for (unsigned int i = 0; i < 3; ++i)
76  {
77  color.rgba[i] = static_cast<double>(rand()) / static_cast<double>(RAND_MAX);
78  }
79  if (color.rgba[0] > 0.95
80  || color.rgba[1] > 0.95
81  || color.rgba[2] > 0.95)
82  {
83  break;
84  }
85  }
86 
87  return color;
88 }
89 
93 int
94 main()
95 {
96  // Setup logger (write to file and stdout)
98 
99  imstkNew<Scene> scene("Octree Example");
100 
101  // Setup a viewer to render in its own thread
102  imstkNew<VTKViewer> viewer;
103  viewer->setActiveScene(scene);
104  viewer->setWindowTitle("Octree Example");
105  viewer->setSize(1920, 1080);
106 
107  auto statusText = std::make_shared<TextVisualModel>("StatusText");
108  statusText->setFontSize(30.0);
109  statusText->setTextColor(Color::Orange);
110  statusText->setPosition(TextVisualModel::DisplayPosition::UpperLeft);
111 
112  // Seed based on CPU time for random colors
113  srand(static_cast<unsigned int>(time(nullptr)));
114 
115  // Create meshes
116  std::vector<std::shared_ptr<SurfaceMesh>> triMeshes;
117  for (unsigned int i = 0; i < NUM_MESHES; ++i)
118  {
119  std::shared_ptr<SceneObject> sceneObj = createMeshObject("Mesh-" + std::to_string(triMeshes.size()), getRandomColor());
120  scene->addSceneObject(sceneObj);
121  triMeshes.push_back(std::dynamic_pointer_cast<SurfaceMesh>(sceneObj->getVisualGeometry()));
122  }
123 
124  // Compute the scale factor to scale meshes such that meshes with different sizes are still visualized consistently
125  Vec3d lowerCorner, upperCorner;
126  const auto pointset = std::dynamic_pointer_cast<PointSet>(triMeshes.front());
127  ParallelUtils::findAABB(*pointset->getVertexPositions(), lowerCorner, upperCorner);
128  const auto scaleFactor = 20.0 / (upperCorner - lowerCorner).norm();
129  for (const auto& mesh : triMeshes)
130  {
131  mesh->scale(scaleFactor, Geometry::TransformType::ApplyToData);
132  }
133 
134  StopWatch timer;
135  timer.start();
136 
137  // Create octree
138  imstkNew<LooseOctree> octree(Vec3d(0.0, 0.0, 0.0), 100.0, 0.125, 2.0, "TestOctree");
139 
140  // Add all meshes to the octree
141  for (const auto& mesh : triMeshes)
142  {
143  octree->addTriangleMesh(mesh);
144  }
145 
146  // Build octree after adding all geometries
147  octree->build();
148  LOG(INFO) << "Build octree time: " << timer.getTimeElapsed() << " ms";
149 
150  // Always rebuild tree from scratch in each update (default update is incremental update)
151  // This is significantly slower than incremental update!
152  // octree.setAlwaysRebuild(true);
153 
154  auto debugOctreeObj = std::make_shared<Entity>();
155  auto debugOctreeModel = debugOctreeObj->addComponent<OctreeDebugModel>();
156  debugOctreeModel->setInputOctree(octree);
157  debugOctreeModel->setLineWidth(1.0);
158  debugOctreeModel->setLineColor(Color::Green);
159  scene->addSceneObject(debugOctreeObj);
160 
161  // Data for animation
162  const double translation = 15.0;
163  VecDataArray<double, 3> centers;
165  for (unsigned int i = 0; i < NUM_MESHES; ++i)
166  {
167  centers.push_back(Vec3d(translation, 0, 0));
168  dirs.push_back(Vec3d(-1, 0, 0));
169  }
170 
171  // Transform the mesh objects
172  const double angle = 2.0 * PI / NUM_MESHES;
173  for (unsigned int i = 0; i < NUM_MESHES; ++i)
174  {
175  const auto rotation = angle * static_cast<double>(i);
176  triMeshes[i]->translate(translation, 0, 1, Geometry::TransformType::ApplyToData);
177  triMeshes[i]->rotate(Vec3d(0, 1, 0), rotation, Geometry::TransformType::ApplyToData);
178 
179  auto t = centers[i][0];
180  centers[i][0] = std::cos(rotation) * t;
181  centers[i][2] = -std::sin(rotation) * t;
182 
183  t = dirs[i][0];
184  dirs[i][0] = std::cos(rotation) * t;
185  dirs[i][2] = -std::sin(rotation) * t;
186  }
187 
188  auto updateFunc =
189  [&](Event*) {
190  // Move objects
191  for (size_t i = 0; i < triMeshes.size(); ++i)
192  {
193  triMeshes[i]->translate(dirs[i][0], dirs[i][1], dirs[i][2], Geometry::TransformType::ApplyToData);
194  centers[i] += dirs[i];
195  }
196 
197  Vec3d lowerCorners, upperCorner;
198  ParallelUtils::findAABB(centers, lowerCorners, upperCorner);
199  if ((lowerCorners - upperCorner).norm() > 70.0)
200  {
201  for (size_t i = 0; i < static_cast<size_t>(dirs.size()); i++)
202  {
203  dirs[i] = -dirs[i]; // Change moving direction to move the objects back if they have moved too far
204  }
205  }
206 
207  StopWatch timer;
208  timer.start();
209  octree->update();
210  const auto updateTime = timer.getTimeElapsed();
211 
212  const auto numActiveNodes = octree->getNumActiveNodes();
213  const auto numAllocatedNodes = octree->getNumAllocatedNodes();
214  const auto maxNumPrimitivesInNodes = octree->getMaxNumPrimitivesInNodes();
215 
216  std::stringstream ss;
217  ss << "Octree update time: " << updateTime << " ms\n"
218  << "Active nodes: " << numActiveNodes
219  << " (" << static_cast<double>(numActiveNodes) / static_cast<double>(numAllocatedNodes) * 100.0
220  << " % usage / total allocated nodes: " << numAllocatedNodes << ")\n"
221  << "Max number of primitives in tree nodes: " << maxNumPrimitivesInNodes;
222 
223  statusText->setText(ss.str());
224  };
225 
226  // Set Camera configuration
227  auto cam = scene->getActiveCamera();
228  cam->setPosition(Vec3d(0, 15, 50));
229  cam->setFocalPoint(Vec3d(0, 0, 0));
230 
231  // Lights
232  {
234  light1->setFocalPoint(Vec3d(-1.0, -1.0, -1.0));
235  light1->setIntensity(1.0);
236  scene->addLight("light 1", light1);
237 
239  light2->setFocalPoint(Vec3d(1.0, -1.0, -1.0));
240  light2->setIntensity(1.0);
241  scene->addLight("light 2", light2);
242  }
243 
244  // Run the simulation
245  {
246  // Setup a scene manager to advance the scene in its own thread
247  imstkNew<SceneManager> sceneManager;
248  sceneManager->setActiveScene(scene);
249  sceneManager->pause(); // Start simulation paused
250  connect<Event>(sceneManager, &SceneManager::postUpdate, updateFunc);
251 
253  driver->addModule(viewer);
254  driver->addModule(sceneManager);
255  driver->setDesiredDt(0.05);
256 
257  // Update debug visual representation every render
258  connect<Event>(viewer, &Viewer::preUpdate, [&](Event*)
259  {
260  // Update debug rendering data
261  // Involves a larger buffer update so we only do it before rendering
262  debugOctreeModel->debugUpdate(8, true);
263  });
264 
265  // Add default mouse and keyboard controls to the viewer
266  std::shared_ptr<Entity> mouseAndKeyControls =
267  SimulationUtils::createDefaultSceneControl(driver);
268  mouseAndKeyControls->addComponent(statusText);
269  scene->addSceneObject(mouseAndKeyControls);
270 
271  driver->start();
272  }
273 
274  return 0;
275 }
Base class for all geometries represented by discrete points and elements The pointsets follow a pipe...
Definition: imstkPointSet.h:25
void setActiveScene(std::shared_ptr< Scene > scene) override
Set scene to be rendered.
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
virtual void start()
Start the appropriate timer.
Definition: imstkTimer.cpp:29
Compound Geometry.
void initialize(std::shared_ptr< VecDataArray< double, 3 >> vertices, std::shared_ptr< VecDataArray< int, 3 >> triangleIndices, const bool computeDerivedData=false)
Initializes the rest of the data structures given vertex positions and triangle connectivity.
void push_back(const ValueType &val)
Append the data array to hold the new value, resizes if neccesary.
void setIntensity(const double intensity)
Set the light intensity. This value is unbounded.
Definition: imstkLight.h:71
void scale(const Vec3d &scaling, TransformType type=TransformType::ConcatenateToTransform)
Scale in Cartesian directions.
std::shared_ptr<T> obj = std::make_shared<T>(); equivalent, convenience class for STL shared allocati...
Definition: imstkNew.h:29
void setRenderMaterial(std::shared_ptr< RenderMaterial > renderMaterial)
Set/Get render material.
Color in RGB space.
Definition: imstkColor.h:24
Stop Watch utility class.
Definition: imstkTimer.h:19
void setFocalPoint(const Vec3d &p)
Get/Set the light focal point.
Definition: imstkLight.h:33
void setActiveScene(std::string newSceneName)
Sets the currently updating scene.
OctreeDebugModel for debug visualizing an octree.
virtual double getTimeElapsed(const TimeUnitType unitType=TimeUnitType::milliSeconds)
Returns the time elapsed since calling start.
Definition: imstkTimer.cpp:136
void setSize(const int width, const int height) override
Set the render window size.
static LoggerG3 & startLogger()
Starts logger with default sinks, use getInstance to create a logger with no sinks.
void setWindowTitle(const std::string &title) override
Set the render window title.