iMSTK
Interactive Medical Simulation Toolkit
PbdTissueGraspingExample.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 "imstkCapsule.h"
9 #include "imstkDeviceManager.h"
10 #include "imstkDeviceManagerFactory.h"
11 #include "imstkDirectionalLight.h"
12 #include "imstkGeometryUtilities.h"
13 #include "imstkKeyboardDeviceClient.h"
14 #include "imstkKeyboardSceneControl.h"
15 #include "imstkLaparoscopicToolController.h"
16 #include "imstkMeshIO.h"
17 #include "imstkMouseDeviceClient.h"
18 #include "imstkMouseSceneControl.h"
19 #include "imstkPbdModel.h"
20 #include "imstkPbdModelConfig.h"
21 #include "imstkPbdObject.h"
22 #include "imstkPbdObjectCollision.h"
23 #include "imstkPbdObjectGrasping.h"
24 #include "imstkPointwiseMap.h"
25 #include "imstkRenderMaterial.h"
26 #include "imstkScene.h"
27 #include "imstkSceneManager.h"
28 #include "imstkSimulationManager.h"
29 #include "imstkSimulationUtils.h"
30 #include "imstkVisualModel.h"
31 #include "imstkVTKViewer.h"
32 
33 using namespace imstk;
34 
38 static void
39 setSphereTexCoords(std::shared_ptr<SurfaceMesh> surfMesh, const double uvScale)
40 {
41  Vec3d min, max;
42  surfMesh->computeBoundingBox(min, max);
43  const Vec3d size = max - min;
44  const Vec3d center = (max + min) * 0.5;
45 
46  const double radius = (size * 0.5).norm();
47 
48  auto uvCoordsPtr = std::make_shared<VecDataArray<float, 2>>(surfMesh->getNumVertices());
49  VecDataArray<float, 2>& uvCoords = *uvCoordsPtr.get();
50  for (int i = 0; i < surfMesh->getNumVertices(); i++)
51  {
52  Vec3d vertex = surfMesh->getVertexPosition(i) - center;
53 
54  // Compute phi and theta on the sphere
55  const double theta = asin(vertex[0] / radius);
56  const double phi = atan2(vertex[1], vertex[2]);
57  uvCoords[i] = Vec2f(phi / (PI * 2.0) + 0.5, theta / (PI * 2.0) + 0.5) * uvScale;
58  }
59  surfMesh->setVertexTCoords("tcoords", uvCoordsPtr);
60 }
61 
69 static std::shared_ptr<PbdObject>
70 makeTissueObj(const std::string& name,
71  const Vec3d& size, const Vec3i& dim, const Vec3d& center)
72 {
73  // Setup the Geometry
74  std::shared_ptr<TetrahedralMesh> tissueMesh = GeometryUtils::toTetGrid(center, size, dim);
75  std::shared_ptr<SurfaceMesh> surfMesh = tissueMesh->extractSurfaceMesh();
76  setSphereTexCoords(surfMesh, 6.0);
77 
78  // Setup the Parameters
79  auto pbdParams = std::make_shared<PbdModelConfig>();
80  const bool useFem = true;
81  if (useFem)
82  {
83  // Actual skin young's modulus, 0.42MPa to 0.85Mpa, as reported in papers
84  // Actual skin possion ratio, 0.48, as reported in papers
85  pbdParams->m_femParams->m_YoungModulus = 40000.0;
86  pbdParams->m_femParams->m_PoissonRatio = 0.48;
87  // FYI:
88  // - Poisson ratio gives shear to bulk, with 0.5 being complete shear
89  // where everything is like a fluid and can slide past each other. 0.0
90  // gives complete bulk where its rigid
91  // - Youngs modulus then gives the scaling of the above in pressure
92  // (pascals).
93  pbdParams->enableFemConstraint(PbdFemConstraint::MaterialType::NeoHookean);
94  }
95  else
96  {
97  pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Distance, 100000.0);
98  pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Volume, 100000.0);
99  }
100  pbdParams->m_doPartitioning = false;
101  pbdParams->m_dt = 0.001; // realtime used in update calls later in main
102  pbdParams->m_iterations = 5;
103 
104  // Due to poor boundary conditions turning off gravity is useful. But then makes
105  // your tissue look like it's in space (springy and no resistance). So viscous
106  // damping is introduced to approximate these conditions.
107  //
108  // Ultimately this is a result of not modelling everything around the tissue.
109  // and poor/hard to model boundary conditions.
110  pbdParams->m_gravity = Vec3d::Zero();
111  pbdParams->m_linearDampingCoeff = 0.03; // Removed from velocity
112 
113  // Setup the Model
114  auto pbdModel = std::make_shared<PbdModel>();
115  pbdModel->configure(pbdParams);
116 
117  // Setup the material
118  auto material = std::make_shared<RenderMaterial>();
119  material->setDisplayMode(RenderMaterial::DisplayMode::Surface);
120  material->setShadingModel(RenderMaterial::ShadingModel::PBR);
121  material->addTexture(std::make_shared<Texture>(iMSTK_DATA_ROOT "/textures/fleshDiffuse.jpg",
122  Texture::Type::Diffuse));
123  material->addTexture(std::make_shared<Texture>(iMSTK_DATA_ROOT "/textures/fleshNormal.jpg",
124  Texture::Type::Normal));
125  material->addTexture(std::make_shared<Texture>(iMSTK_DATA_ROOT "/textures/fleshORM.jpg",
126  Texture::Type::ORM));
127  material->setNormalStrength(0.3);
128 
129  // Add a visual model to render the surface of the tet mesh
130  auto visualModel = std::make_shared<VisualModel>();
131  visualModel->setGeometry(surfMesh);
132  visualModel->setRenderMaterial(material);
133 
134  // Setup the Object
135  auto tissueObj = std::make_shared<PbdObject>(name);
136  tissueObj->addVisualModel(visualModel);
137  tissueObj->setPhysicsGeometry(tissueMesh);
138  tissueObj->setCollidingGeometry(surfMesh);
139  tissueObj->setPhysicsToCollidingMap(std::make_shared<PointwiseMap>(tissueMesh, surfMesh));
140  tissueObj->setDynamicalModel(pbdModel);
141  tissueObj->getPbdBody()->uniformMassValue = 100.0;
142  // Fix the borders
143  for (int z = 0; z < dim[2]; z++)
144  {
145  for (int y = 0; y < dim[1]; y++)
146  {
147  for (int x = 0; x < dim[0]; x++)
148  {
149  if (x == 0 || /*z == 0 ||*/ x == dim[0] - 1 /*|| z == dim[2] - 1*/)
150  {
151  tissueObj->getPbdBody()->fixedNodeIds.push_back(x + dim[0] * (y + dim[1] * z));
152  }
153  }
154  }
155  }
156 
157  return tissueObj;
158 }
159 
164 int
165 main()
166 {
167  // Setup logger (write to file and stdout)
169 
170  // Scene
171  auto scene = std::make_shared<Scene>("PbdTissueGrasping");
172  scene->getActiveCamera()->setPosition(0.001, 0.05, 0.15);
173  scene->getActiveCamera()->setFocalPoint(0.0, 0.0, 0.0);
174  scene->getActiveCamera()->setViewUp(0.0, 0.96, -0.28);
175 
176  auto geomShaft = std::make_shared<Capsule>();
177  geomShaft->setLength(1.0);
178  geomShaft->setRadius(0.005);
179  geomShaft->setOrientation(Quatd(Rotd(PI_2, Vec3d(1.0, 0.0, 0.0))));
180  geomShaft->setTranslation(Vec3d(0.0, 0.0, 0.5));
181  auto objShaft = std::make_shared<CollidingObject>("objShaft");
182  objShaft->setVisualGeometry(MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/LapTool/pivot.obj"));
183  objShaft->setCollidingGeometry(geomShaft);
184  scene->addSceneObject(objShaft);
185 
186  auto geomUpperJaw = std::make_shared<Capsule>();
187  geomUpperJaw->setLength(0.05);
188  geomUpperJaw->setTranslation(Vec3d(0.0, 0.0013, -0.016));
189  geomUpperJaw->setRadius(0.004);
190  geomUpperJaw->setOrientation(Quatd(Rotd(PI_2, Vec3d(1.0, 0.0, 0.0))));
191  auto objUpperJaw = std::make_shared<CollidingObject>("objUpperJaw");
192  objUpperJaw->setVisualGeometry(MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/LapTool/upper.obj"));
193  objUpperJaw->setCollidingGeometry(geomUpperJaw);
194  scene->addSceneObject(objUpperJaw);
195 
196  auto geomLowerJaw = std::make_shared<Capsule>();
197  geomLowerJaw->setLength(0.05);
198  geomLowerJaw->setTranslation(Vec3d(0.0, -0.0013, -0.016));
199  geomLowerJaw->setRadius(0.004);
200  geomLowerJaw->setOrientation(Quatd(Rotd(PI_2, Vec3d(1.0, 0.0, 0.0))));
201  auto objLowerJaw = std::make_shared<CollidingObject>("objLowerJaw");
202  objLowerJaw->setVisualGeometry(MeshIO::read<SurfaceMesh>(iMSTK_DATA_ROOT "/Surgical Instruments/LapTool/lower.obj"));
203  objLowerJaw->setCollidingGeometry(geomLowerJaw);
204  scene->addSceneObject(objLowerJaw);
205 
206  auto pickGeom = std::make_shared<Capsule>();
207  pickGeom->setLength(0.05);
208  pickGeom->setTranslation(Vec3d(0.0, 0.0, -0.016));
209  pickGeom->setRadius(0.006);
210  pickGeom->setOrientation(Quatd(Rotd(PI_2, Vec3d(1.0, 0.0, 0.0))));
211 
212  // ~4in x 4in patch of tissue
213  std::shared_ptr<PbdObject> tissueObj = makeTissueObj("PbdTissue",
214  Vec3d(0.1, 0.025, 0.1), Vec3i(6, 3, 6), Vec3d(0.0, -0.03, 0.0));
215  scene->addSceneObject(tissueObj);
216 
217  // Setup default haptics manager
218  std::shared_ptr<DeviceManager> hapticManager = DeviceManagerFactory::makeDeviceManager();
219  std::shared_ptr<DeviceClient> deviceClient = hapticManager->makeDeviceClient();
220 
221  // Create and add virtual coupling object controller in the scene
222  auto controller = std::make_shared<LaparoscopicToolController>();
223  controller->setParts(objShaft, objUpperJaw, objLowerJaw, pickGeom);
224  controller->setDevice(deviceClient);
225  controller->setJawAngleChange(1.0);
226  scene->addControl(controller);
227 
228  // Add collision for both jaws of the tool
229  auto upperJawCollision = std::make_shared<PbdObjectCollision>(tissueObj, objUpperJaw);
230  auto lowerJawCollision = std::make_shared<PbdObjectCollision>(tissueObj, objLowerJaw);
231  scene->addInteraction(upperJawCollision);
232  scene->addInteraction(lowerJawCollision);
233 
234  // Add picking interaction for both jaws of the tool
235  auto jawPicking = std::make_shared<PbdObjectGrasping>(tissueObj);
236  // Pick the surface instead of the tetrahedral mesh
237  jawPicking->setGeometryToPick(tissueObj->getVisualGeometry(),
238  std::dynamic_pointer_cast<PointwiseMap>(tissueObj->getPhysicsToCollidingMap()));
239  scene->addInteraction(jawPicking);
240 
241  // Light
242  auto light = std::make_shared<DirectionalLight>();
243  light->setFocalPoint(Vec3d(0.0, -1.0, -1.0));
244  light->setIntensity(1.0);
245  scene->addLight("light", light);
246 
247  // Run the simulation
248  {
249  // Setup a viewer to render
250  auto viewer = std::make_shared<VTKViewer>();
251  viewer->setActiveScene(scene);
252  viewer->setDebugAxesLength(0.01, 0.01, 0.01);
253 
254  // Setup a scene manager to advance the scene
255  auto sceneManager = std::make_shared<SceneManager>();
256  sceneManager->setActiveScene(scene);
257  sceneManager->pause(); // Start simulation paused
258 
259  auto driver = std::make_shared<SimulationManager>();
260  driver->addModule(hapticManager);
261  driver->addModule(viewer);
262  driver->addModule(sceneManager);
263  driver->setDesiredDt(0.001);
264 
265  // Add default mouse and keyboard controls to the viewer
266  std::shared_ptr<Entity> mouseAndKeyControls =
267  SimulationUtils::createDefaultSceneControl(driver);
268  scene->addSceneObject(mouseAndKeyControls);
269 
270  connect<Event>(sceneManager, &SceneManager::preUpdate,
271  [&](Event*)
272  {
273  // Simulate the cloth in real time
274  tissueObj->getPbdModel()->getConfig()->m_dt = sceneManager->getDt();
275  });
276 
277  connect<Event>(controller, &LaparoscopicToolController::JawClosed,
278  [&](Event*)
279  {
280  LOG(INFO) << "Jaw Closed!";
281 
282  upperJawCollision->setEnabled(false);
283  lowerJawCollision->setEnabled(false);
284  jawPicking->beginRayPointGrasp(pickGeom, pickGeom->getPosition(),
285  -pickGeom->getOrientation().toRotationMatrix().col(1), 0.03);
286  //jawPicking->beginCellGrasp(pickGeom, "SurfaceMeshToCapsuleCD");
287  });
288  connect<Event>(controller, &LaparoscopicToolController::JawOpened,
289  [&](Event*)
290  {
291  LOG(INFO) << "Jaw Opened!";
292 
293  upperJawCollision->setEnabled(true);
294  lowerJawCollision->setEnabled(true);
295  jawPicking->endGrasp();
296  });
297 
298  driver->start();
299  }
300 
301  return 0;
302 }
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
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.
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 ...
PointwiseMap can compute & apply a mapping between parent and child PointSet geometries.
Physically based rendering.
static LoggerG3 & startLogger()
Starts logger with default sinks, use getInstance to create a logger with no sinks.