iMSTK
Interactive Medical Simulation Toolkit
RbdSurfaceMeshToSphereCDExample.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 "imstkCollisionUtils.h"
9 #include "imstkDirectionalLight.h"
10 #include "imstkGeometryUtilities.h"
11 #include "imstkKeyboardDeviceClient.h"
12 #include "imstkKeyboardSceneControl.h"
13 #include "imstkMouseDeviceClient.h"
14 #include "imstkMouseSceneControl.h"
15 #include "imstkNew.h"
16 #include "imstkRbdConstraint.h"
17 #include "imstkRenderMaterial.h"
18 #include "imstkRigidBodyModel2.h"
19 #include "imstkRigidObject2.h"
20 #include "imstkRigidObjectCollision.h"
21 #include "imstkScene.h"
22 #include "imstkSceneManager.h"
23 #include "imstkSimulationManager.h"
24 #include "imstkSimulationUtils.h"
25 #include "imstkSphere.h"
26 #include "imstkSurfaceMesh.h"
27 #include "imstkVisualModel.h"
28 #include "imstkVTKViewer.h"
29 
30 using namespace imstk;
31 
32 std::shared_ptr<SurfaceMesh>
33 createBowlMesh()
34 {
35  imstkNew<Sphere> sphere(Vec3d::Zero(), 8.0);
36 
37  std::shared_ptr<SurfaceMesh> sphereMesh = GeometryUtils::toUVSphereSurfaceMesh(sphere, 10, 10);
38  auto sphereVerticesPtr = sphereMesh->getVertexPositions();
39  auto sphereIndicesPtr = sphereMesh->getCells();
40 
41  // Cut off the upper half of the sphere
42  auto bowlVerticesPtr = std::make_shared<VecDataArray<double, 3>>();
43  auto bowlIndicesPtr = std::make_shared<VecDataArray<int, 3>>();
44 
45  std::unordered_map<int, int> sphereVertexToBowlVertex;
46  for (int i = 0; i < sphereVerticesPtr->size(); i++)
47  {
48  const Vec3d sphereVertex = (*sphereVerticesPtr)[i];
49  if (sphereVertex[1] < 1.0)
50  {
51  const int vertexId = bowlVerticesPtr->size();
52  sphereVertexToBowlVertex[i] = vertexId;
53  bowlVerticesPtr->push_back(sphereVertex);
54  }
55  }
56 
57  // Add back all triangles with all verts present
58  for (int i = 0; i < sphereIndicesPtr->size(); i++)
59  {
60  const Vec3i& tri = (*sphereIndicesPtr)[i];
61  // If all triangle vertices still present, add triangle
62  if (sphereVertexToBowlVertex.count(tri[0]) != 0
63  && sphereVertexToBowlVertex.count(tri[1]) != 0
64  && sphereVertexToBowlVertex.count(tri[2]) != 0)
65  {
66  Vec3i newTri = Vec3i(
67  sphereVertexToBowlVertex[tri[0]],
68  sphereVertexToBowlVertex[tri[1]],
69  sphereVertexToBowlVertex[tri[2]]);
70  std::swap(newTri[0], newTri[1]);
71  bowlIndicesPtr->push_back(newTri);
72  }
73  }
74  auto results = std::make_shared<SurfaceMesh>();
75  results->initialize(bowlVerticesPtr, bowlIndicesPtr);
76  results->scale(Vec3d(1.0, 0.5, 1.0), Geometry::TransformType::ApplyToData);
77  results->translate(Vec3d(0.0, 0.0, 0.0), Geometry::TransformType::ApplyToData);
78  return results;
79 }
80 
85 int
86 main()
87 {
88  // Write log to stdout and file
90 
91  imstkNew<Scene> scene("RbdMeshMeshCollision");
92 
93  // This model is shared among interacting rigid bodies
95  rbdModel->getConfig()->m_maxNumIterations = 10;
96 
97  // Create the first rbd, plane floor
98  imstkNew<CollidingObject> floorObj("Plane");
99  {
100  std::shared_ptr<SurfaceMesh> bowlMesh = createBowlMesh();
101 
102  // Create the object
103  floorObj->setVisualGeometry(bowlMesh);
104  floorObj->setCollidingGeometry(bowlMesh);
105 
106  auto material = std::make_shared<RenderMaterial>();
107  material->setDisplayMode(RenderMaterial::DisplayMode::WireframeSurface);
108  material->setShadingModel(RenderMaterial::ShadingModel::PBR);
109  material->setDiffuseColor(Color(1.0, 0.8, 0.74));
110  material->setRoughness(0.5);
111  material->setMetalness(0.1);
112  floorObj->getVisualModel(0)->setRenderMaterial(material);
113 
114  scene->addSceneObject(floorObj);
115  }
116 
117  std::array<std::shared_ptr<RigidObject2>, 6> rigidObjects;
118  const int rbdObjCount = static_cast<int>(rigidObjects.size());
119  for (int i = 0; i < rbdObjCount; i++)
120  {
121  rigidObjects[i] = std::make_shared<RigidObject2>("RbdObject" + std::to_string(i));
122  const double radius = 0.8;
123  imstkNew<Sphere> sphere(Vec3d::Zero(), radius);
124 
125  // Create the cube rigid object
126  rigidObjects[i]->setDynamicalModel(rbdModel);
127  rigidObjects[i]->setPhysicsGeometry(sphere);
128  rigidObjects[i]->setCollidingGeometry(sphere);
129  rigidObjects[i]->setVisualGeometry(sphere);
130  rigidObjects[i]->getRigidBody()->m_mass = 1.0;
131  const double t = static_cast<double>(i) / (rbdObjCount - 1);
132  rigidObjects[i]->getRigidBody()->m_initPos = Vec3d((t - 0.5) * rbdObjCount * radius * 2.0, 1.0, 0.0);
133  rigidObjects[i]->getRigidBody()->m_intertiaTensor = Mat3d::Identity();
134 
135  auto material = std::make_shared<RenderMaterial>();
136  material->setDiffuseColor(Color::lerpRgb(Color(1.0, 0.333, 0.259), Color(0.427, 1.0, 0.58), t));
137  material->setShadingModel(RenderMaterial::ShadingModel::PBR);
138  material->setRoughness(0.5);
139  material->setMetalness(0.5);
140  rigidObjects[i]->getVisualModel(0)->setRenderMaterial(material);
141 
142  scene->addSceneObject(rigidObjects[i]);
143  }
144 
145  // Collision Interaction between rigid objects
146  {
147  for (int i = 0; i < rbdObjCount; i++)
148  {
149  auto rbdInteraction = std::make_shared<RigidObjectCollision>(rigidObjects[i], floorObj, "SurfaceMeshToSphereCD");
150  rbdInteraction->setFriction(0.0);
151  rbdInteraction->setBaumgarteStabilization(0.0001);
152  scene->addInteraction(rbdInteraction);
153  }
154 
155  for (int i = 0; i < rbdObjCount; i++)
156  {
157  for (int j = i + 1; j < rbdObjCount; j++)
158  {
159  auto rbdInteraction = std::make_shared<RigidObjectCollision>(rigidObjects[i], rigidObjects[j], "SphereToSphereCD");
160  rbdInteraction->setFriction(0.0);
161  rbdInteraction->setBaumgarteStabilization(0.0001);
162  scene->addInteraction(rbdInteraction);
163  }
164  }
165  }
166 
167  // Camera
168  scene->getActiveCamera()->setPosition(0.0252374, 2.85008, 17.0338);
169  scene->getActiveCamera()->setFocalPoint(0.0, 0.0, 0.0);
170  scene->getActiveCamera()->setViewUp(0.0016057, 0.999996, 0.00220191);
171 
172  // Light
174  light->setIntensity(1.0);
175  scene->addLight("light", light);
176 
177  // Run the simulation
178  {
179  // Setup a viewer to render in its own thread
180  imstkNew<VTKViewer> viewer;
181  viewer->setActiveScene(scene);
182 
183  // Setup a scene manager to advance the scene in its own thread
184  imstkNew<SceneManager> sceneManager;
185  sceneManager->setActiveScene(scene);
186  sceneManager->pause();
187 
189  driver->addModule(viewer);
190  driver->addModule(sceneManager);
191  driver->setDesiredDt(0.001);
192 
193  // Add default mouse and keyboard controls to the viewer
194  std::shared_ptr<Entity> mouseAndKeyControls =
195  SimulationUtils::createDefaultSceneControl(driver);
196  scene->addSceneObject(mouseAndKeyControls);
197 
198  // The following implements pick and drag controls for a sphere, this could be more elegantly
199  // implemented in a subclass of MouseControl
200  LOG(INFO) << "RbdObj Controls:";
201  LOG(INFO) << "----------------------------------------------------------------------";
202  LOG(INFO) << " | click and drag to pick up sphere";
203 
204  // We do picking/dragging of sphere's by implementing movement on the plane whose normal is the
205  // view direction and center is the sphere position when picked up
206  int sphereSelected = -1;
207  Vec3d planePos;
208  // Perform picking on to figure out which sphere was clicked
209  connect<MouseEvent>(viewer->getMouseDevice(), &MouseDeviceClient::mouseButtonPress,
210  [&](MouseEvent* e)
211  {
212  if (e->m_buttonId == 0)
213  {
214  // Get mouse position (0, 1) with origin at bot left of screen
215  const Vec2d mousePos = viewer->getMouseDevice()->getPos();
216  // To NDC coordinates
217  const Vec3d rayDir = scene->getActiveCamera()->getEyeRayDir(
218  Vec2d(mousePos[0] * 2.0 - 1.0, mousePos[1] * 2.0 - 1.0));
219  const Vec3d rayStart = scene->getActiveCamera()->getPosition();
220 
221  double minDist = IMSTK_DOUBLE_MAX; // Use the closest picked sphere
222  for (int i = 0; i < rbdObjCount; i++)
223  {
224  auto sphere = std::dynamic_pointer_cast<Sphere>(rigidObjects[i]->getPhysicsGeometry());
225  Vec3d iPt;
226  if (CollisionUtils::testRayToSphere(rayStart, rayDir,
227  sphere->getCenter(), sphere->getRadius(), iPt))
228  {
229  const double dist = (iPt - rayStart).norm();
230  if (dist < minDist)
231  {
232  minDist = dist;
233  sphereSelected = i;
234  planePos = sphere->getCenter();
235  }
236  }
237  }
238  }
239  });
240  // Unselect/drop the sphere
241  connect<MouseEvent>(viewer->getMouseDevice(), &MouseDeviceClient::mouseButtonRelease,
242  [&](MouseEvent* e)
243  {
244  if (e->m_buttonId == 0)
245  {
246  sphereSelected = -1;
247  }
248  });
249  connect<Event>(sceneManager, &SceneManager::postUpdate,
250  [&](Event*)
251  {
252  // Keep cube updating at real time
253  std::shared_ptr<RigidBodyModel2> rbdModel = rigidObjects[0]->getRigidBodyModel2(); // All bodies share a model
254  rbdModel->getConfig()->m_dt = sceneManager->getDt();
255 
256  if (sphereSelected != -1)
257  {
258  // Get mouses current position
259  const Vec2d mousePos = viewer->getMouseDevice()->getPos();
260  const Vec3d rayDir = scene->getActiveCamera()->getEyeRayDir(
261  Vec2d(mousePos[0] * 2.0 - 1.0, mousePos[1] * 2.0 - 1.0));
262  const Vec3d rayStart = scene->getActiveCamera()->getPosition();
263 
264  // Exert a force to bring it to the mouse position on the plane
265  auto sphere = std::dynamic_pointer_cast<Sphere>(rigidObjects[sphereSelected]->getPhysicsGeometry());
266  Vec3d iPt;
267  CollisionUtils::testRayToPlane(rayStart, rayDir, planePos, scene->getActiveCamera()->getForward(), iPt);
268  const Vec3d fS = (iPt - sphere->getPosition()) * 100.0; // Spring force
269  const Vec3d fD = -rigidObjects[sphereSelected]->getRigidBody()->getVelocity() * 10.0; // Spring damping
270  *rigidObjects[sphereSelected]->getRigidBody()->m_force += (fS + fD);
271  }
272  });
273 
274  driver->start();
275  }
276 
277  return 0;
278 }
void setDesiredDt(const double dt)
Sets the target fixed timestep (may violate), seconds This ultimately effects the number of iteration...
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...
Compound Geometry.
void addModule(std::shared_ptr< Module > module) override
Add a module to run.
void setIntensity(const double intensity)
Set the light intensity. This value is unbounded.
Definition: imstkLight.h:71
std::shared_ptr<T> obj = std::make_shared<T>(); equivalent, convenience class for STL shared allocati...
Definition: imstkNew.h:29
virtual Vec3d getCenter()
Returns the bounding box center.
Definition: imstkGeometry.h:96
Represents a sphere via its position & radius.
Definition: imstkSphere.h:19
std::shared_ptr< SurfaceMesh > toUVSphereSurfaceMesh(std::shared_ptr< Sphere > sphere, const unsigned int phiDivisions, const unsigned int thetaDivisions)
UV sphere from imstkSphere.
Color in RGB space.
Definition: imstkColor.h:24
Provides the information of a mouse event, this includes button presses/releases and scrolling...
void setActiveScene(std::string newSceneName)
Sets the currently updating scene.
Physically based rendering.
std::shared_ptr< MouseDeviceClient > getMouseDevice() const override
Returns the device that emits mouse events.
static LoggerG3 & startLogger()
Starts logger with default sinks, use getInstance to create a logger with no sinks.