iMSTK
Interactive Medical Simulation Toolkit
PBDTissueSurfaceNeedleContactExample.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 "imstkCollisionDetectionAlgorithm.h"
9 #include "imstkDirectionalLight.h"
10 #include "imstkGeometryUtilities.h"
11 #include "imstkImageData.h"
12 #include "imstkIsometricMap.h"
13 #include "imstkKeyboardDeviceClient.h"
14 #include "imstkKeyboardSceneControl.h"
15 #include "imstkMeshIO.h"
16 #include "imstkMouseDeviceClient.h"
17 #include "imstkMouseSceneControl.h"
18 #include "imstkNeedle.h"
19 #include "imstkObjectControllerGhost.h"
20 #include "imstkPbdModel.h"
21 #include "imstkPbdModelConfig.h"
22 #include "imstkPbdObject.h"
23 #include "imstkPointwiseMap.h"
24 #include "imstkRenderMaterial.h"
25 #include "imstkRigidObject2.h"
26 #include "imstkRigidObjectController.h"
27 #include "imstkScene.h"
28 #include "imstkSceneManager.h"
29 #include "imstkSimulationManager.h"
30 #include "imstkSimulationUtils.h"
31 #include "imstkVisualModel.h"
32 #include "imstkVTKViewer.h"
33 #include "NeedleInteraction.h"
34 
35 #ifdef iMSTK_USE_HAPTICS
36 #include "imstkDeviceManager.h"
37 #include "imstkDeviceManagerFactory.h"
38 #else
39 #include "imstkDummyClient.h"
40 #endif
41 
42 using namespace imstk;
43 
47 static void
48 setSphereTexCoords(std::shared_ptr<SurfaceMesh> surfMesh, const double uvScale)
49 {
50  Vec3d min, max;
51  surfMesh->computeBoundingBox(min, max);
52  const Vec3d size = max - min;
53  const Vec3d center = (max + min) * 0.5;
54 
55  const double radius = (size * 0.5).norm();
56 
57  auto uvCoordsPtr = std::make_shared<VecDataArray<float, 2>>(surfMesh->getNumVertices());
58  VecDataArray<float, 2>& uvCoords = *uvCoordsPtr.get();
59  for (int i = 0; i < surfMesh->getNumVertices(); i++)
60  {
61  const Vec3d vertex = surfMesh->getVertexPosition(i) - center;
62 
63  // Compute phi and theta on the sphere
64  const double theta = asin(vertex[0] / radius);
65  const double phi = atan2(vertex[1], vertex[2]);
66  uvCoords[i] = Vec2f(phi / (PI * 2.0) + 0.5, theta / (PI * 2.0) + 0.5) * uvScale;
67  }
68  surfMesh->setVertexTCoords("tcoords", uvCoordsPtr);
69 }
70 
78 static std::shared_ptr<PbdObject>
79 makeTissueObj(const std::string& name,
80  const Vec3d& size, const Vec3i& dim, const Vec3d& center)
81 {
82  // Setup the Geometry
83  std::shared_ptr<TetrahedralMesh> tissueMesh = GeometryUtils::toTetGrid(center, size, dim);
84  std::shared_ptr<SurfaceMesh> surfMesh = tissueMesh->extractSurfaceMesh();
85  setSphereTexCoords(surfMesh, 6.0);
86 
87  // Setup the Parameters
88  auto pbdParams = std::make_shared<PbdModelConfig>();
89  // Use FEMTet constraints
90  pbdParams->m_femParams->m_YoungModulus = 5.0;
91  pbdParams->m_femParams->m_PoissonRatio = 0.4;
92  pbdParams->enableFemConstraint(PbdFemConstraint::MaterialType::StVK);
93  pbdParams->m_doPartitioning = true;
94  pbdParams->m_gravity = Vec3d(0.0, 0.0, 0.0);
95  pbdParams->m_dt = 0.05;
96  pbdParams->m_iterations = 9;
97  pbdParams->m_linearDampingCoeff = 0.05;
98 
99  // Setup the Model
100  auto pbdModel = std::make_shared<PbdModel>();
101  pbdModel->configure(pbdParams);
102 
103  // Setup the material
104  auto material = std::make_shared<RenderMaterial>();
105  material->setShadingModel(RenderMaterial::ShadingModel::PBR);
106  auto diffuseTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshDiffuse.jpg");
107  material->addTexture(std::make_shared<Texture>(diffuseTex, Texture::Type::Diffuse));
108  auto normalTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshNormal.jpg");
109  material->addTexture(std::make_shared<Texture>(normalTex, Texture::Type::Normal));
110  auto ormTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshORM.jpg");
111  material->addTexture(std::make_shared<Texture>(ormTex, Texture::Type::ORM));
112  material->setNormalStrength(0.3);
113 
114  // Add a visual model to render the surface of the tet mesh
115  auto visualModel = std::make_shared<VisualModel>();
116  visualModel->setGeometry(surfMesh);
117  visualModel->setRenderMaterial(material);
118 
119  // Add a visual model to render the normals of the surface
120  /*imstkNew<VisualModel> normalsVisualModel(surfMesh);
121  normalsVisualModel->getRenderMaterial()->setDisplayMode(RenderMaterial::DisplayMode::SurfaceNormals);
122  normalsVisualModel->getRenderMaterial()->setPointSize(0.5);
123  clothObj->addVisualModel(normalsVisualModel);*/
124 
125  // Setup the Object
126  auto tissueObj = std::make_shared<PbdObject>(name);
127  tissueObj->addVisualModel(visualModel);
128  tissueObj->setPhysicsGeometry(tissueMesh);
129  tissueObj->setCollidingGeometry(surfMesh);
130  tissueObj->setPhysicsToCollidingMap(std::make_shared<PointwiseMap>(tissueMesh, surfMesh));
131  tissueObj->setDynamicalModel(pbdModel);
132  tissueObj->getPbdBody()->uniformMassValue = 0.1;
133  // Fix the borders
134  for (int z = 0; z < dim[2]; z++)
135  {
136  for (int y = 0; y < dim[1]; y++)
137  {
138  for (int x = 0; x < dim[0]; x++)
139  {
140  if (x == 0 || /*z == 0 ||*/ x == dim[0] - 1 /*|| z == dim[2] - 1*/)
141  {
142  tissueObj->getPbdBody()->fixedNodeIds.push_back(x + dim[0] * (y + dim[1] * z));
143  }
144  }
145  }
146  }
147 
148  tissueObj->addComponent<Puncturable>();
149 
150  return tissueObj;
151 }
152 
153 static std::shared_ptr<RigidObject2>
154 makeToolObj()
155 {
156  auto toolGeom = std::make_shared<LineMesh>();
157  VecDataArray<double, 3> vertices = { Vec3d(0.0, -1.0, 0.0), Vec3d(0.0, 1.0, 0.0) };
158  VecDataArray<int, 2> cells = { Vec2i(0, 1) };
159  toolGeom->initialize(std::make_shared<VecDataArray<double, 3>>(vertices),
160  std::make_shared<VecDataArray<int, 2>>(cells));
161 
162  auto syringeMesh = MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/Syringes/Disposable_Syringe.stl");
163  syringeMesh->scale(0.4, Geometry::TransformType::ApplyToData);
164  syringeMesh->rotate(Vec3d(1.0, 0.0, 0.0), -PI_2, Geometry::TransformType::ApplyToData);
165  syringeMesh->translate(Vec3d(0.0, 4.4, 0.0), Geometry::TransformType::ApplyToData);
166 
167  auto toolObj = std::make_shared<RigidObject2>("NeedleRbdTool");
168  toolObj->setVisualGeometry(syringeMesh);
169  toolObj->setCollidingGeometry(toolGeom);
170  toolObj->setPhysicsGeometry(toolGeom);
171  toolObj->setPhysicsToVisualMap(std::make_shared<IsometricMap>(toolGeom, syringeMesh));
172  toolObj->getVisualModel(0)->getRenderMaterial()->setColor(Color(0.9, 0.9, 0.9));
173  toolObj->getVisualModel(0)->getRenderMaterial()->setShadingModel(RenderMaterial::ShadingModel::PBR);
174  toolObj->getVisualModel(0)->getRenderMaterial()->setRoughness(0.5);
175  toolObj->getVisualModel(0)->getRenderMaterial()->setMetalness(1.0);
176  toolObj->getVisualModel(0)->getRenderMaterial()->setIsDynamicMesh(false);
177 
178  std::shared_ptr<RigidBodyModel2> rbdModel = std::make_shared<RigidBodyModel2>();
179  rbdModel->getConfig()->m_gravity = Vec3d::Zero();
180  toolObj->setDynamicalModel(rbdModel);
181 
182  toolObj->getRigidBody()->m_mass = 0.1;
183  toolObj->getRigidBody()->m_intertiaTensor = Mat3d::Identity() * 10000.0;
184  toolObj->getRigidBody()->m_initPos = Vec3d(0.0, 2.0, 0.0);
185 
186  // Add a component for needle puncturing
187  auto needle = toolObj->addComponent<StraightNeedle>();
188  needle->setNeedleGeometry(toolGeom);
189 
190  // Add a component for controlling via another device
191  auto controller = toolObj->addComponent<RigidObjectController>();
192  controller->setControlledObject(toolObj);
193  controller->setTranslationScaling(50.0);
194  controller->setLinearKs(1000.0);
195  controller->setAngularKs(10000000.0);
196  controller->setUseCritDamping(true);
197  controller->setForceScaling(0.0045);
198  controller->setSmoothingKernelSize(15);
199  controller->setUseForceSmoothening(true);
200 
201  // Add extra component to tool for the ghost
202  auto controllerGhost = toolObj->addComponent<ObjectControllerGhost>();
203  controllerGhost->setController(controller);
204 
205  return toolObj;
206 }
207 
212 int
213 main()
214 {
215  // Setup logger (write to file and stdout)
217 
218  // Setup the scene
219  auto scene = std::make_shared<Scene>("PbdTissueSurfaceNeedleContact");
220  scene->getActiveCamera()->setPosition(-0.06, 7.29, 11.69);
221  scene->getActiveCamera()->setFocalPoint(0.0, 0.0, 0.0);
222  scene->getActiveCamera()->setViewUp(0.0, 1.0, 0.0);
223 
224  // Setup a tissue
225  std::shared_ptr<PbdObject> tissueObj = makeTissueObj("Tissue",
226  Vec3d(10.0, 3.0, 10.0), Vec3i(7, 3, 6), Vec3d(0.1, -1.0, 0.0));
227  scene->addSceneObject(tissueObj);
228 
229  std::shared_ptr<RigidObject2> toolObj = makeToolObj();
230  scene->addSceneObject(toolObj);
231 
232  scene->addInteraction(std::make_shared<NeedleInteraction>(tissueObj, toolObj));
233 
234  // Light
235  auto light = std::make_shared<DirectionalLight>();
236  light->setDirection(Vec3d(5.0, -8.0, -5.0));
237  light->setIntensity(1.0);
238  scene->addLight("Light", light);
239 
240  // Run the simulation
241  {
242  // Setup a viewer to render
243  auto viewer = std::make_shared<VTKViewer>();
244  viewer->setActiveScene(scene);
245  viewer->setVtkLoggerMode(VTKViewer::VTKLoggerMode::MUTE);
246  viewer->setDebugAxesLength(0.1, 0.1, 0.1);
247 
248  // Setup a scene manager to advance the scene
249  auto sceneManager = std::make_shared<SceneManager>();
250  sceneManager->setActiveScene(scene);
251  sceneManager->pause(); // Start simulation paused
252 
253  auto driver = std::make_shared<SimulationManager>();
254  driver->addModule(viewer);
255  driver->addModule(sceneManager);
256  driver->setDesiredDt(0.001);
257 
258 #ifdef iMSTK_USE_HAPTICS
259  // Setup default haptics manager
260  std::shared_ptr<DeviceManager> hapticManager = DeviceManagerFactory::makeDeviceManager();
261  std::shared_ptr<DeviceClient> deviceClient = hapticManager->makeDeviceClient();
262  driver->addModule(hapticManager);
263 #else
264  auto deviceClient = std::make_shared<DummyClient>();
265  connect<Event>(sceneManager, &SceneManager::postUpdate, [&](Event*)
266  {
267  const Vec2d mousePos = viewer->getMouseDevice()->getPos();
268  const Vec3d desiredPos = Vec3d(mousePos[0] - 0.5, mousePos[1] - 0.5, 0.0) * 0.2 + Vec3d(0.0, 0.025, 0.0);
269  const Quatd desiredOrientation = Quatd(Rotd(0.0, Vec3d(1.0, 0.0, 0.0)));
270 
271  deviceClient->setPosition(desiredPos);
272  });
273 #endif
274 
275  auto controller = toolObj->getComponent<RigidObjectController>();
276  controller->setDevice(deviceClient);
277 
278  connect<Event>(sceneManager, &SceneManager::preUpdate,
279  [&](Event*)
280  {
281  // Keep the tool moving in real time
282  toolObj->getRigidBodyModel2()->getConfig()->m_dt = sceneManager->getDt();
283  //tissueObj->getPbdModel()->getParameters()->m_dt = sceneManager->getDt();
284  });
285 
286  // Add default mouse and keyboard controls to the viewer
287  std::shared_ptr<Entity> mouseAndKeyControls =
288  SimulationUtils::createDefaultSceneControl(driver);
289  scene->addSceneObject(mouseAndKeyControls);
290 
291  driver->start();
292  }
293 
294  return 0;
295 }
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
Place this on an object to make it puncturable by a needle. This allows puncturables to know they&#39;ve ...
Compound Geometry.
std::shared_ptr< TetrahedralMesh > toTetGrid(const Vec3d &center, const Vec3d &size, const Vec3i &divisions, const Quatd orientation=Quatd::Identity())
Produces a tetrahedral grid given the OrientedBox with the given divisions.
Definition of straight, single segment needle.
Simple dynamic array implementation that also supports event posting and viewing/facade.
static std::shared_ptr< DeviceManager > makeDeviceManager()
Attempts to create a new DeviceManager by whichever is default If multiple haptic managers are built ...
This class uses the provided device to control the provided rigid object via virtual coupling...
Color in RGB space.
Definition: imstkColor.h:24
A behaviour that renders a second copy of the controlled object at a lower opacity in the physical po...
Physically based rendering.
static LoggerG3 & startLogger()
Starts logger with default sinks, use getInstance to create a logger with no sinks.