iMSTK
Interactive Medical Simulation Toolkit
PBDTissueContactExample.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 "imstkControllerForceText.h"
9 #include "imstkDirectionalLight.h"
10 #include "imstkGeometryUtilities.h"
11 #include "imstkImageData.h"
12 #include "imstkKeyboardDeviceClient.h"
13 #include "imstkKeyboardSceneControl.h"
14 #include "imstkMeshIO.h"
15 #include "imstkMouseDeviceClient.h"
16 #include "imstkMouseSceneControl.h"
17 #include "imstkObjectControllerGhost.h"
18 #include "imstkPbdCollisionHandling.h"
19 #include "imstkPbdModel.h"
20 #include "imstkPbdModelConfig.h"
21 #include "imstkPbdObject.h"
22 #include "imstkPbdObjectCollision.h"
23 #include "imstkPbdObjectController.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 "imstkTextVisualModel.h"
31 #include "imstkVTKViewer.h"
32 
33 #ifdef iMSTK_USE_HAPTICS
34 #include "imstkDeviceManager.h"
35 #include "imstkDeviceManagerFactory.h"
36 #else
37 #include "imstkDummyClient.h"
38 #endif
39 
40 using namespace imstk;
41 
45 static void
46 setSphereTexCoords(std::shared_ptr<SurfaceMesh> surfMesh, const double uvScale)
47 {
48  Vec3d min, max;
49  surfMesh->computeBoundingBox(min, max);
50  const Vec3d size = max - min;
51  const Vec3d center = (max + min) * 0.5;
52 
53  const double radius = (size * 0.5).norm();
54 
55  auto uvCoordsPtr = std::make_shared<VecDataArray<float, 2>>(surfMesh->getNumVertices());
56  VecDataArray<float, 2>& uvCoords = *uvCoordsPtr.get();
57  for (int i = 0; i < surfMesh->getNumVertices(); i++)
58  {
59  Vec3d vertex = surfMesh->getVertexPosition(i) - center;
60 
61  // Compute phi and theta on the sphere
62  const double theta = asin(vertex[0] / radius);
63  const double phi = atan2(vertex[1], vertex[2]);
64  uvCoords[i] = Vec2f(phi / (PI * 2.0) + 0.5, theta / (PI * 2.0) + 0.5) * uvScale;
65  }
66  surfMesh->setVertexTCoords("tcoords", uvCoordsPtr);
67 }
68 
77 static std::shared_ptr<PbdObject>
78 makeTissueObj(const std::string& name,
79  const Vec3d& size, const Vec3i& dim, const Vec3d& center,
80  std::shared_ptr<PbdModel> model)
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, 4.0);
86 
87  // Setup the material
88  auto material = std::make_shared<RenderMaterial>();
89  material->setShadingModel(RenderMaterial::ShadingModel::PBR);
90  auto diffuseTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshDiffuse.jpg");
91  material->addTexture(std::make_shared<Texture>(diffuseTex, Texture::Type::Diffuse));
92  auto normalTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshNormal.jpg");
93  material->addTexture(std::make_shared<Texture>(normalTex, Texture::Type::Normal));
94  auto ormTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshORM.jpg");
95  material->addTexture(std::make_shared<Texture>(ormTex, Texture::Type::ORM));
96 
97  // Add a visual model to render the surface of the tet mesh
98  auto visualModel = std::make_shared<VisualModel>();
99  visualModel->setGeometry(surfMesh);
100  visualModel->setRenderMaterial(material);
101 
102  // Add a visual model to render the normals of the surface
103  auto normalsVisualModel = std::make_shared<VisualModel>();
104  normalsVisualModel->setGeometry(surfMesh);
105  normalsVisualModel->getRenderMaterial()->setDisplayMode(RenderMaterial::DisplayMode::SurfaceNormals);
106  normalsVisualModel->getRenderMaterial()->setPointSize(0.5);
107 
108  // Setup the Object
109  auto tissueObj = std::make_shared<PbdObject>(name);
110  tissueObj->addVisualModel(visualModel);
111  tissueObj->addVisualModel(normalsVisualModel);
112  tissueObj->setPhysicsGeometry(tissueMesh);
113  tissueObj->setCollidingGeometry(surfMesh);
114  tissueObj->setPhysicsToCollidingMap(std::make_shared<PointwiseMap>(tissueMesh, surfMesh));
115  tissueObj->setDynamicalModel(model);
116  tissueObj->getPbdBody()->uniformMassValue = 0.05;
117  // Fix the borders
118  for (int z = 0; z < dim[2]; z++)
119  {
120  for (int y = 0; y < dim[1]; y++)
121  {
122  for (int x = 0; x < dim[0]; x++)
123  {
124  if (x == 0 || /*z == 0 ||*/ x == dim[0] - 1 /*|| z == dim[2] - 1*/)
125  {
126  tissueObj->getPbdBody()->fixedNodeIds.push_back(x + dim[0] * (y + dim[1] * z));
127  }
128  }
129  }
130  }
131  model->getConfig()->m_femParams->m_YoungModulus = 50.0;
132  model->getConfig()->m_femParams->m_PoissonRatio = 0.4;
133  model->getConfig()->enableFemConstraint(PbdFemConstraint::MaterialType::NeoHookean);
134  model->getConfig()->setBodyDamping(tissueObj->getPbdBody()->bodyHandle, 0.001);
135 
136  return tissueObj;
137 }
138 
143 static std::shared_ptr<PbdObject>
144 makeToolObj(std::shared_ptr<PbdModel> model)
145 {
146  auto toolGeometry = std::make_shared<LineMesh>();
147  VecDataArray<double, 3> vertices = { Vec3d(0.0, 0.0, 0.0), Vec3d(0.0, 2.0, 0.0) };
148  VecDataArray<int, 2> indices = { Vec2i(0, 1) };
149  toolGeometry->initialize(std::make_shared<VecDataArray<double, 3>>(vertices),
150  std::make_shared<VecDataArray<int, 2>>(indices));
151 
152  auto toolObj = std::make_shared<PbdObject>("Tool");
153  toolObj->setVisualGeometry(toolGeometry);
154  toolObj->setCollidingGeometry(toolGeometry);
155  toolObj->setPhysicsGeometry(toolGeometry);
156  toolObj->setDynamicalModel(model);
157  toolObj->getVisualModel(0)->getRenderMaterial()->setColor(Color::Blue);
158  toolObj->getVisualModel(0)->getRenderMaterial()->setDisplayMode(RenderMaterial::DisplayMode::Wireframe);
159  toolObj->getVisualModel(0)->getRenderMaterial()->setBackFaceCulling(false);
160  toolObj->getVisualModel(0)->getRenderMaterial()->setLineWidth(10.0);
161 
162  model->getConfig()->setBodyDamping(toolObj->getPbdBody()->bodyHandle, 0.05, 0.0);
163 
164  toolObj->getPbdBody()->setRigid(Vec3d(0.0, 0.8, 0.0), // Position
165  0.2, // Mass
166  Quatd::Identity(), // Orientation
167  Mat3d::Identity() * 10.0); // Inertia
168 
169  // Add a component for controlling via a device
170  auto controller = toolObj->addComponent<PbdObjectController>();
171  controller->setControlledObject(toolObj);
172  controller->setLinearKs(5000.0);
173  controller->setAngularKs(10000.0);
174  controller->setUseCritDamping(true);
175  controller->setForceScaling(0.0025);
176  controller->setUseForceSmoothening(true);
177 
178  // Add extra component to tool for the ghost
179  auto controllerGhost = toolObj->addComponent<ObjectControllerGhost>();
180  controllerGhost->setController(controller);
181 
182  return toolObj;
183 }
184 
192 int
193 main()
194 {
195  // Setup logger (write to file and stdout)
197 
198  // Setup the scene
199  auto scene = std::make_shared<Scene>("PbdTissueContact");
200  scene->getActiveCamera()->setPosition(0.12, 4.51, 16.51);
201  scene->getActiveCamera()->setFocalPoint(0.0, 0.0, 0.0);
202  scene->getActiveCamera()->setViewUp(0.0, 0.96, -0.28);
203 
204  // Setup the Model/System
205  auto pbdModel = std::make_shared<PbdModel>();
206  pbdModel->getConfig()->m_doPartitioning = false;
207  pbdModel->getConfig()->m_gravity = Vec3d(0.0, 0.0, 0.0);
208  pbdModel->getConfig()->m_dt = 0.05;
209  pbdModel->getConfig()->m_iterations = 5;
210 
211  // Setup a tissue
212  std::shared_ptr<PbdObject> tissueObj = makeTissueObj("Tissue",
213  Vec3d(8.0, 2.0, 8.0), Vec3i(6, 5, 6), Vec3d(0.0, -1.0, 0.0), pbdModel);
214  scene->addSceneObject(tissueObj);
215 
216  // Setup a tool
217  std::shared_ptr<PbdObject> toolObj = makeToolObj(pbdModel);
218  scene->addSceneObject(toolObj);
219 
220  // Setup a collision
221  auto collision = std::make_shared<PbdObjectCollision>(tissueObj, toolObj);
222  //std::dynamic_pointer_cast<ClosedSurfaceMeshToMeshCD>(collision->getCollisionDetection())->setGenerateEdgeEdgeContacts(true);
223  scene->addInteraction(collision);
224 
225  // Light
226  auto light = std::make_shared<DirectionalLight>();
227  light->setFocalPoint(Vec3d(5.0, -8.0, -5.0));
228  light->setIntensity(1.0);
229  scene->addLight("Light", light);
230 
231  // Run the simulation
232  {
233  // Setup a viewer to render
234  auto viewer = std::make_shared<VTKViewer>();
235  viewer->setActiveScene(scene);
236  viewer->setVtkLoggerMode(VTKViewer::VTKLoggerMode::MUTE);
237 
238  // Setup a scene manager to advance the scene
239  auto sceneManager = std::make_shared<SceneManager>();
240  sceneManager->setActiveScene(scene);
241  sceneManager->pause(); // Start simulation paused
242 
243  auto driver = std::make_shared<SimulationManager>();
244  driver->addModule(viewer);
245  driver->addModule(sceneManager);
246  driver->setDesiredDt(0.001);
247 
248  auto controller = toolObj->getComponent<PbdObjectController>();
249 #ifdef iMSTK_USE_HAPTICS
250  // Setup default haptics manager
251  std::shared_ptr<DeviceManager> hapticManager = DeviceManagerFactory::makeDeviceManager();
252  std::shared_ptr<DeviceClient> deviceClient = hapticManager->makeDeviceClient();
253  driver->addModule(hapticManager);
254 
255  controller->setTranslationScaling(50.0);
256  if (hapticManager->getTypeName() == "HaplyDeviceManager")
257  {
258  controller->setTranslationOffset(Vec3d(5.0, -5.0, 0.0));
259  }
260 #else
261  auto deviceClient = std::make_shared<DummyClient>();
262  connect<Event>(sceneManager, &SceneManager::postUpdate, [&](Event*)
263  {
264  const Vec2d mousePos = viewer->getMouseDevice()->getPos();
265  const Vec3d worldPos = Vec3d(mousePos[0] - 0.5, mousePos[1] - 0.5, 0.0) * 10.0;
266 
267  deviceClient->setPosition(worldPos);
268  });
269 
270  controller->setTranslationScaling(1.0);
271 #endif
272  controller->setDevice(deviceClient);
273 
274  connect<Event>(sceneManager, &SceneManager::preUpdate, [&](Event*)
275  {
276  // Keep the tool moving in real time
277  pbdModel->getConfig()->m_dt = sceneManager->getDt();
278  });
279 
280  // Add default mouse and keyboard controls to the viewer
281  std::shared_ptr<Entity> mouseAndKeyControls =
282  SimulationUtils::createDefaultSceneControl(driver);
283  // Add something to display controller force
284  auto controllerForceTxt = mouseAndKeyControls->addComponent<ControllerForceText>();
285  controllerForceTxt->setController(controller);
286  controllerForceTxt->setCollision(collision);
287  scene->addSceneObject(mouseAndKeyControls);
288 
289  driver->start();
290  }
291 
292  return 0;
293 }
Base class for events which contain a type, priority, and data priority defaults to 0 and uses a grea...
This class uses the provided device to control the provided rigid object via virtual coupling...
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 ...
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.
void setController(std::shared_ptr< PbdObjectController > controller)
Get/Set the controller to display the device force of.
Displays virtual coupling force text in the top right.