iMSTK
Interactive Medical Simulation Toolkit
InflatableObject.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 "InflatableObject.h"
8 #include "imstkCollisionUtils.h"
9 #include "imstkGeometryUtilities.h"
10 #include "imstkImageData.h"
11 #include "imstkMeshIO.h"
12 #include "imstkNew.h"
13 #include "imstkPbdConstraintContainer.h"
14 #include "imstkPbdInflatableDistanceConstraint.h"
15 #include "imstkPbdInflatableVolumeConstraint.h"
16 #include "imstkPbdModel.h"
17 #include "imstkPbdModelConfig.h"
18 #include "imstkPointwiseMap.h"
19 #include "imstkRenderMaterial.h"
20 #include "imstkVisualModel.h"
21 
22 InflatableObject::InflatableObject(const std::string& name, const Vec3d& tissueSize,
23  const Vec3i& tissueDim, const Vec3d& tissueCenter) : PbdObject(name)
24 {
25  // Setup the Geometry
26  m_objectTetMesh = GeometryUtils::toTetGrid(tissueCenter, tissueSize, tissueDim);
27  m_objectSurfMesh = m_objectTetMesh->extractSurfaceMesh();
28  setSphereTexCoords(4.0);
29 
30  // Setup the Parameters
31  imstkNew<PbdModelConfig> pbdParams;
32  pbdParams->m_doPartitioning = false;
33  pbdParams->m_gravity = Vec3d(0.0, 0.0, 0.0);
34  pbdParams->m_dt = 0.05;
35  pbdParams->m_iterations = 2;
36  pbdParams->m_linearDampingCoeff = 0.05;
37 
38  // Add custom constraint generation functors
39  auto distanceFunctor = std::make_shared<PbdInflatableDistanceConstraintFunctor>();
40  distanceFunctor->setStiffness(0.95);
41  auto volumeFunctor = std::make_shared<PbdInflatableVolumeConstraintFunctor>();
42  volumeFunctor->setStiffness(0.9);
43 
44  pbdParams->addPbdConstraintFunctor(distanceFunctor);
45  pbdParams->addPbdConstraintFunctor(volumeFunctor);
46 
47  // Setup the Model
48  imstkNew<PbdModel> pbdModel;
49  pbdModel->configure(pbdParams);
50 
51  // Setup the material
52  imstkNew<RenderMaterial> material;
53  material->setShadingModel(RenderMaterial::ShadingModel::PBR);
54  auto diffuseTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshDiffuse.jpg");
55  material->addTexture(std::make_shared<Texture>(diffuseTex, Texture::Type::Diffuse));
56  auto normalTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshNormal.jpg");
57  material->addTexture(std::make_shared<Texture>(normalTex, Texture::Type::Normal));
58  auto ormTex = MeshIO::read<ImageData>(iMSTK_DATA_ROOT "/textures/fleshORM.jpg");
59  material->addTexture(std::make_shared<Texture>(ormTex, Texture::Type::ORM));
60 
61  // Add a visual model to render the surface of the tet mesh
62  imstkNew<VisualModel> visualModel;
63  visualModel->setGeometry(m_objectSurfMesh);
64  visualModel->setRenderMaterial(material);
65  addVisualModel(visualModel);
66 
67  // Setup the Object
68  setPhysicsGeometry(m_objectTetMesh);
69  setCollidingGeometry(m_objectSurfMesh);
70  setPhysicsToCollidingMap(std::make_shared<PointwiseMap>(m_objectTetMesh, m_objectSurfMesh));
71  setDynamicalModel(pbdModel);
72 
73  distanceFunctor->setBodyIndex(getPbdBody()->bodyHandle);
74  volumeFunctor->setBodyIndex(getPbdBody()->bodyHandle);
75 
76  // Fix the borders
77  getPbdBody()->uniformMassValue = 0.1;
78  for (int z = 0; z < tissueDim[2]; z++)
79  {
80  for (int y = 0; y < tissueDim[1]; y++)
81  {
82  for (int x = 0; x < tissueDim[0]; x++)
83  {
84  if (x == 0 || z == 0 || x == tissueDim[0] - 1 || z == tissueDim[2] - 1 || y == 0)
85  {
86  getPbdBody()->fixedNodeIds.push_back(x + tissueDim[0] * (y + tissueDim[1] * z));
87  }
88  }
89  }
90  }
91 }
92 
93 void
95 {
96  Vec3d min, max;
97  m_objectSurfMesh->computeBoundingBox(min, max);
98  const Vec3d size = max - min;
99  const Vec3d center = (max + min) * 0.5;
100 
101  const double radius = (size * 0.5).norm();
102 
103  imstkNew<VecDataArray<float, 2>> uvCoordsPtr(m_objectSurfMesh->getNumVertices());
104  VecDataArray<float, 2>& uvCoords = *uvCoordsPtr.get();
105  for (int i = 0; i < m_objectSurfMesh->getNumVertices(); i++)
106  {
107  Vec3d vertex = m_objectSurfMesh->getVertexPosition(i) - center;
108 
109  // Compute phi and theta on the sphere
110  const double theta = asin(vertex[0] / radius);
111  const double phi = atan2(vertex[1], vertex[2]);
112  uvCoords[i] = Vec2f(phi / (PI * 2.0) + 0.5, theta / (PI * 2.0) + 0.5) * uvScale;
113  }
114  m_objectSurfMesh->setVertexTCoords("tcoords", uvCoordsPtr);
115 }
116 
117 void
118 InflatableObject::findAffectedConstraint(const Vec3d& toolTip, const double radius)
119 {
120  m_constraintIDandWeight.clear();
121 
122  Vec3d min, max;
123  m_objectTetMesh->computeBoundingBox(min, max);
124  if (!CollisionUtils::testAabbToAabb(toolTip[0], toolTip[0], toolTip[1], toolTip[1], toolTip[2], toolTip[2],
125  min[0], max[0], min[1], max[1], min[2], max[2]))
126  {
127  return;
128  }
129 
130  const auto& vertices = m_objectTetMesh->getVertexPositions();
131 
132  std::vector<int> constraintIDs;
133  std::vector<double> weights;
134  int counter = 0;
135  double minDistance = LONG_MAX;
136  for (auto& c : m_pbdModel->getConstraints()->getConstraints())
137  {
138  const std::vector<PbdParticleId>& ids = c->getParticles();
139 
140  Vec3d center(0.0, 0.0, 0.0);
141  for (auto i : ids)
142  {
143  center += (*vertices)[i.second];
144  }
145 
146  double distance = (center / ids.size() - toolTip).norm();
147 
148  if (distance < radius)
149  {
150  constraintIDs.push_back(counter);
151  weights.push_back(computeGaussianWeight(distance));
152  }
153 
154  if (distance < minDistance)
155  {
156  minDistance = distance;
157  }
158 
159  counter++;
160  }
161 
162  if (minDistance > 0.5)
163  {
164  return;
165  }
166  else
167  {
168  for (size_t i = 0; i < constraintIDs.size(); i++)
169  {
170  m_constraintIDandWeight.push_back(std::make_pair(constraintIDs[i], weights[i]));
171  }
172 
173  m_affectedAreaUpdated = true;
174  }
175 }
176 
177 void
178 InflatableObject::inject(const Vec3d& toolTip, const double radius, double dx)
179 {
180  if (!m_affectedAreaUpdated)
181  {
182  findAffectedConstraint(toolTip, radius);
183  }
184 
185  double de = 0.0;
186  if (m_inflationType == InflationType::Exponential)
187  {
188  de = std::exp(dx);
189  }
190  else if (m_inflationType == InflationType::Linear)
191  {
192  de = dx;
193  }
194 
195  for (auto& id : m_constraintIDandWeight)
196  {
197  const double dv = id.second * de;
198 
199  auto& c = (m_pbdModel->getConstraints()->getConstraints())[id.first];
200  if (auto volConstraint = std::dynamic_pointer_cast<PbdInflatableVolumeConstraint>(c))
201  {
202  volConstraint->setRestVolume(dv + volConstraint->getRestVolume());
203  volConstraint->setStiffness(1.0);
204  }
205  else if (auto distConstraint = std::dynamic_pointer_cast<PbdInflatableDistanceConstraint>(c))
206  {
207  distConstraint->setRestLength(0.00001 * cbrt(dv) + distConstraint->getRestLength());
208  distConstraint->setStiffness(0.1);
209  }
210  }
211 }
212 
213 void
215 {
216  if (m_inflationType == InflationType::Linear)
217  {
218  m_inflationType = InflationType::Exponential;
219  std::cout << "Inflation Type: Exponential." << std::endl;
220  }
221  else if (m_inflationType == InflationType::Exponential)
222  {
223  m_inflationType = InflationType::Linear;
224  std::cout << "Inflation Type: Linear." << std::endl;
225  }
226 }
227 
228 void
230 {
231  m_affectedAreaUpdated = false;
232  m_inflationRatio = 1.0;
233 }
234 
235 void
236 InflatableObject::reset()
237 {
239 
240  m_inflationRatio = 1.0;
241 
242  for (auto& c : m_pbdModel->getConstraints()->getConstraints())
243  {
244  if (auto volConstraint = std::dynamic_pointer_cast<PbdInflatableVolumeConstraint>(c))
245  {
246  volConstraint->resetRestVolume();
247  }
248  else if (auto distConstraint = std::dynamic_pointer_cast<PbdInflatableDistanceConstraint>(c))
249  {
250  distConstraint->resetRestLength();
251  }
252  }
253 }
double m_linearDampingCoeff
Damping coefficient applied to linear velocity [0, 1].
void addTexture(std::shared_ptr< Texture > texture)
Add/Remove/Get texture.
void configure(std::shared_ptr< PbdModelConfig > params)
Set simulation parameters.
void inject(const Vec3d &toolTip, const double radius, double rate)
Perform injection on the tissue given tool tip position.
void reset() override
Reset the dynamic object by reseting the respective DynamicalModel and Geometry.
void setSphereTexCoords(const double uvScale)
Spherically project the texture coordinates.
void addPbdConstraintFunctor(std::shared_ptr< PbdConstraintFunctor > functor)
Adds a functor to generate constraints.
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.
void findAffectedConstraint(const Vec3d &toolTip, const double radius)
find affected constraints id and distance in the injection area
unsigned int m_iterations
Internal constraints pbd solver iterations.
Simple dynamic array implementation that also supports event posting and viewing/facade.
std::shared_ptr<T> obj = std::make_shared<T>(); equivalent, convenience class for STL shared allocati...
Definition: imstkNew.h:29
void setUpdateAffectedConstraint()
set update affected constraints flag
Base class for scene objects that move and/or deform under position based dynamics formulation...
void setRenderMaterial(std::shared_ptr< RenderMaterial > renderMaterial)
Set/Get render material.
bool m_doPartitioning
Does graph coloring to solve in parallel.
double m_dt
Time step size.
Physically based rendering.
void switchInflationType()
Switch between linear and exponential inflation type.
Vec3d m_gravity
Gravity acceleration.