iMSTK
Interactive Medical Simulation Toolkit
imstkPbdCollisionHandling.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 "imstkPbdCollisionHandling.h"
8 #include "imstkPbdContactConstraint.h"
9 #include "imstkPbdEdgeEdgeCCDConstraint.h"
10 #include "imstkPbdEdgeEdgeConstraint.h"
11 #include "imstkPbdModel.h"
12 #include "imstkPbdModelConfig.h"
13 #include "imstkPbdObject.h"
14 #include "imstkPbdPointEdgeConstraint.h"
15 #include "imstkPbdPointPointConstraint.h"
16 #include "imstkPbdPointTriangleConstraint.h"
17 #include "imstkPbdSolver.h"
18 #include "imstkPointSet.h"
19 #include "imstkPointwiseMap.h"
20 
21 namespace imstk
22 {
23 #define REGISTER_CASE(case0, case1, case2, func) \
24  m_funcTable[{ case0, case1, case2 }] = [this](const ColElemSide& sideA, const ColElemSide& sideB) \
25  { func(sideA, sideB); }
26 
27 std::pair<PbdParticleId, Vec3d>
29 {
30  if (elem.m_type == CollisionElementType::PointDirection)
31  {
32  return { { data.bodyId, 0 }, elem.m_element.m_PointDirectionElement.pt };
33  }
34  else
35  {
36  const PbdParticleId ptBv = getVertex(elem, data)[0];
37  return { { data.bodyId, 0 }, (*data.vertices)[ptBv.second] };
38  }
39 }
40 
46 template<typename ArrType, int cellType>
47 static std::array<PbdParticleId, ArrType::NumComponents>
48 getElementVertIds(const CollisionElement& elem, const PbdCollisionHandling::CollisionSideData& side)
49 {
50  // Note: The unrolling of this functions loops could be important to performance
51  typename ArrType::ValueType cell = -ArrType::ValueType::Ones();
52  if (elem.m_type == CollisionElementType::CellIndex && elem.m_element.m_CellIndexElement.cellType == cellType)
53  {
54  // If one index it refers to the cell
55  if (elem.m_element.m_CellIndexElement.idCount == 1)
56  {
57  cell = (*dynamic_cast<ArrType*>(side.indicesPtr))[elem.m_element.m_CellIndexElement.ids[0]];
58  }
59  else if (elem.m_element.m_CellIndexElement.idCount == ArrType::NumComponents)
60  {
61  for (int i = 0; i < ArrType::NumComponents; i++)
62  {
63  cell[i] = elem.m_element.m_CellIndexElement.ids[i];
64  }
65  }
66  }
67 
68  std::array<PbdParticleId, ArrType::NumComponents> results;
69  for (int i = 0; i < ArrType::NumComponents; i++)
70  {
71  results[i] = { -1, -1 };
72  }
73  if (cell[0] != -1)
74  {
75  if (side.mapPtr != nullptr)
76  {
77  for (int i = 0; i < ArrType::NumComponents; i++)
78  {
79  cell[i] = side.mapPtr->getParentVertexId(cell[i]);
80  }
81  }
82  for (int i = 0; i < ArrType::NumComponents; i++)
83  {
84  int vid = cell[i];
85  if (side.bodyId == 0)
86  {
87  vid = side.model->addVirtualParticle((*side.vertices)[vid], 0.0).second;
88  }
89  results[i] = { side.bodyId, vid };
90  }
91  }
92  return results;
93 }
94 
95 std::array<PbdParticleId, 2>
96 PbdCollisionHandling::getEdge(const CollisionElement& elem, const CollisionSideData& side)
97 {
98  return getElementVertIds<VecDataArray<int, 2>, IMSTK_EDGE>(elem, side);
99 }
100 
101 std::array<PbdParticleId, 3>
102 PbdCollisionHandling::getTriangle(const CollisionElement& elem, const CollisionSideData& side)
103 {
104  return getElementVertIds<VecDataArray<int, 3>, IMSTK_TRIANGLE>(elem, side);
105 }
106 
107 std::array<PbdParticleId, 1>
109 {
110  std::array<PbdParticleId, 1> results = { PbdParticleId(-1, -1) };
111  int ptId = -1;
112  if (elem.m_type == CollisionElementType::CellIndex && elem.m_element.m_CellIndexElement.cellType == IMSTK_VERTEX)
113  {
114  ptId = elem.m_element.m_CellIndexElement.ids[0];
115  }
116  else if (elem.m_type == CollisionElementType::PointIndexDirection)
117  {
118  ptId = elem.m_element.m_PointIndexDirectionElement.ptIndex;
119  }
120  if (ptId != -1)
121  {
122  if (side.mapPtr != nullptr)
123  {
124  ptId = side.mapPtr->getParentVertexId(ptId);
125  }
126  if (side.bodyId == 0)
127  {
128  ptId = side.model->addVirtualParticle((*side.vertices)[ptId], 0.0).second;
129  }
130  results[0] = { side.bodyId, ptId };
131  }
132  else
133  {
134  if (elem.m_type == CollisionElementType::PointDirection)
135  {
136  results[0] = { side.model->addVirtualParticle(elem.m_element.m_PointDirectionElement.pt, 0.0) };
137  }
138  }
139  return results;
140 }
141 
142 template<int N>
143 static std::array<Vec3d*, N>
144 getElementVertIdsPrev(const std::array<PbdParticleId, N>& ids,
146 {
147  std::array<Vec3d*, N> results;
148  for (int i = 0; i < N; i++)
149  {
150  // Add all vertices as virtual, 0 mass
151  std::shared_ptr<VecDataArray<double, 3>> vertices =
152  dynamic_cast<PointSet*>(side.prevGeometry)->getVertexPositions();
153  const int vertexId = ids[i].second;
154  results[i] = &(*vertices)[vertexId];
155  }
156  return results;
157 }
158 
159 std::ostream&
160 operator<<(std::ostream& os, const PbdCHTableKey& key)
161 {
162  os << getContactCaseStr(key.elemAType) << " vs " <<
163  getContactCaseStr(key.elemBType) << ", CCD: " << key.ccd;
164  return os;
165 }
166 
167 PbdCollisionHandling::PbdCollisionHandling()
168 {
169  REGISTER_CASE(PbdContactCase::Vertex, PbdContactCase::Vertex, false, addConstraint_V_V);
170  REGISTER_CASE(PbdContactCase::Vertex, PbdContactCase::Edge, false, addConstraint_V_E);
171  REGISTER_CASE(PbdContactCase::Edge, PbdContactCase::Edge, false, addConstraint_E_E);
172  REGISTER_CASE(PbdContactCase::Vertex, PbdContactCase::Triangle, false, addConstraint_V_T);
173 
174  REGISTER_CASE(PbdContactCase::Body, PbdContactCase::Triangle, false, addConstraint_Body_T);
175  REGISTER_CASE(PbdContactCase::Body, PbdContactCase::Edge, false, addConstraint_Body_E);
176  REGISTER_CASE(PbdContactCase::Body, PbdContactCase::Vertex, false, addConstraint_Body_V);
177  REGISTER_CASE(PbdContactCase::Body, PbdContactCase::Primitive, false, addConstraint_Body_V);
178  REGISTER_CASE(PbdContactCase::Body, PbdContactCase::Body, false, addConstraint_Body_Body);
179 
180  // If swap occurs the colliding object could be on the LHS causing issues
181  REGISTER_CASE(PbdContactCase::Primitive, PbdContactCase::Triangle, false, addConstraint_V_T);
182  REGISTER_CASE(PbdContactCase::Primitive, PbdContactCase::Edge, false, addConstraint_V_E);
183  REGISTER_CASE(PbdContactCase::Primitive, PbdContactCase::Vertex, false, addConstraint_V_V);
184 
185  // One way point direction resolution
186  REGISTER_CASE(PbdContactCase::Vertex, PbdContactCase::None, false, addConstraint_V_V);
187  REGISTER_CASE(PbdContactCase::Body, PbdContactCase::None, false, addConstraint_Body_V);
188 
189  // CCD cases
190  REGISTER_CASE(PbdContactCase::Edge, PbdContactCase::Edge, true, addConstraint_E_E_CCD);
191 }
192 
193 PbdCollisionHandling::~PbdCollisionHandling()
194 {
195  deleteCollisionConstraints();
196 }
197 
199 PbdCollisionHandling::getDataFromObject(std::shared_ptr<CollidingObject> obj)
200 {
201  // Pack info into struct, gives some contextual hints as well
202  CollisionSideData side;
203  auto pbdObj = std::dynamic_pointer_cast<PbdObject>(obj);
204  side.pbdObj = pbdObj.get(); // Garunteed
205  side.colObj = obj.get();
206  side.objType = ObjType::Colliding;
207  std::shared_ptr<Geometry> collidingGeometry = side.colObj->getCollidingGeometry();
208  side.geometry = collidingGeometry.get();
209 
210  if (side.pbdObj != nullptr)
211  {
212  if (side.pbdObj->getPbdBody()->bodyType == PbdBody::Type::RIGID)
213  {
214  side.objType = ObjType::PbdRigid;
215  }
216  else
217  {
218  side.objType = ObjType::PbdDeformable;
219  }
220  std::shared_ptr<PbdModel> model = side.pbdObj->getPbdModel();
221  side.model = model.get();
222  // If a physics geometry is provided always use that because
223  // Either:
224  // A.) Physics geometry == Collision Geometry
225  // B.) A PointwiseMap is used and map should refer us back to physics geometry
226  std::shared_ptr<Geometry> physicsGeometry = side.pbdObj->getPhysicsGeometry();
227  side.geometry = physicsGeometry.get();
228  side.bodyId = side.pbdObj->getPbdBody()->bodyHandle;
229 
230  auto map = std::dynamic_pointer_cast<PointwiseMap>(side.pbdObj->getPhysicsToCollidingMap());
231  side.mapPtr = map.get();
232  }
233  side.pointSet = dynamic_cast<PointSet*>(side.geometry);
234  side.vertices = (side.pointSet == nullptr) ? nullptr : side.pointSet->getVertexPositions().get();
235  if (side.objType == ObjType::PbdRigid)
236  {
237  if (auto pointSet = std::dynamic_pointer_cast<PointSet>(side.colObj->getCollidingGeometry()))
238  {
239  std::shared_ptr<VecDataArray<double, 3>> vertices = pointSet->getVertexPositions();
240  side.vertices = vertices.get();
241  }
242  //side.vertices = side.colObj->getCollidingGeometry()
243  }
244  side.indicesPtr = nullptr;
245  if (auto cellMesh = std::dynamic_pointer_cast<AbstractCellMesh>(side.colObj->getCollidingGeometry()))
246  {
247  std::shared_ptr<AbstractDataArray> indicesPtr = cellMesh->getAbstractCells();
248  side.indicesPtr = indicesPtr.get();
249  }
250 
251  return side;
252 }
253 
254 PbdContactCase
256 {
257  if (elem.data->objType == ObjType::PbdRigid)
258  {
259  return PbdContactCase::Body;
260  }
261  else
262  {
263  if (elem.elem->m_type == CollisionElementType::PointDirection)
264  {
265  return PbdContactCase::Primitive;
266  }
267  else if (elem.elem->m_type == CollisionElementType::CellIndex)
268  {
269  // 0 - vertex, 1 - edge, 2 - triangle
270  return static_cast<PbdContactCase>(
271  elem.elem->m_element.m_CellIndexElement.cellType);
272  }
273  else if (elem.elem->m_type == CollisionElementType::CellVertex)
274  {
275  return static_cast<PbdContactCase>(
276  elem.elem->m_element.m_CellVertexElement.size);
277  }
278  // If not rigid, this must be asking to resolve the vertex
279  else if (elem.elem->m_type == CollisionElementType::PointIndexDirection)
280  {
281  return PbdContactCase::Vertex;
282  }
283  }
284  return PbdContactCase::None;
285 }
286 
287 template<class T>
288 T*
289 PbdCollisionHandling::getCachedConstraint(ConstraintType type)
290 {
291  if (!m_constraintCache[type].empty())
292  {
293  T* result = static_cast<T*>(m_constraintCache[type].back());
294  m_constraintCache[type].pop_back();
295  return result;
296  }
297  else
298  {
299  return new T;
300  }
301 }
302 
303 void
305  const std::vector<CollisionElement>& elementsA,
306  const std::vector<CollisionElement>& elementsB)
307 {
308  if (m_clearData)
309  {
310  // Clear constraints vectors
311  m_collisionConstraints.clear();
312  }
313 
314  // Break early if no collision elements
315  if (elementsA.size() != 0 || elementsB.size() != 0)
316  {
317  // Pack all the data needed for a particular side into a struct so we can
318  // swap it with the contact & pass it around
320  dataSideA.compliance = m_compliance;
321  dataSideA.stiffness = m_stiffness[0];
322  CollisionSideData dataSideB = getDataFromObject(getInputObjectB());
323  dataSideB.compliance = m_compliance;
324  dataSideB.stiffness = (dataSideB.pbdObj == nullptr) ? 0.0 : m_stiffness[1];
325 
326  // Share the model with both sides even if B is not pbd, which makes it easier
327  // to acquire the model without knowing which object is pbd
328  if (dataSideB.model == nullptr)
329  {
330  dataSideB.model = dataSideA.model;
331  }
332 
333  // If obj B is also pbd simulated, make sure they share the same model
334  CHECK(dataSideB.pbdObj == nullptr || dataSideA.model == dataSideB.model) <<
335  "PbdCollisionHandling input objects must share PbdModel";
336 
337  // For CCD (store if available)
338  dataSideA.prevGeometry = m_colData->prevGeomA.get();
339  dataSideB.prevGeometry = m_colData->prevGeomB.get();
340 
341  if (elementsA.size() == elementsB.size())
342  {
343  // Deal with two way contacts
344  for (size_t i = 0; i < elementsA.size(); i++)
345  {
347  { &elementsA[i], &dataSideA },
348  { &elementsB[i], &dataSideB });
349  }
350  }
351  else
352  {
353  // Deal with one way contacts (only one side is needed)
354  for (size_t i = 0; i < elementsA.size(); i++)
355  {
357  { &elementsA[i], &dataSideA },
358  { nullptr, nullptr });
359  }
360  for (size_t i = 0; i < elementsB.size(); i++)
361  {
363  { &elementsB[i], &dataSideB },
364  { nullptr, nullptr });
365  }
366  }
367 
368  size_t constraints = 0;
369  for (int i = 0; i < NumTypes; ++i)
370  {
371  constraints += m_constraintBins[i].size();
372  }
373  }
374 
375  if (m_processConstraints)
376  {
377  orderCollisionConstraints();
378 
379  if (m_collisionConstraints.size() == 0)
380  {
381  return;
382  }
383  // ObjA guaranteed to be PbdObject
384  auto pbdObjectA = std::dynamic_pointer_cast<PbdObject>(getInputObjectA());
385  pbdObjectA->getPbdModel()->getSolver()->addConstraints(&m_collisionConstraints);
386  }
387 }
388 
389 void
391 {
392  PbdCHTableKey key;
393  key.elemAType = getCaseFromElement(sideA);
394  key.elemBType = PbdContactCase::None;
395  key.ccd = sideA.elem->m_ccdData;
396 
397  // Data for sideB may not be present if handling one-way
398  if (sideB.data != nullptr)
399  {
400  key.elemBType = getCaseFromElement(sideB);
401  if (sideB.elem->m_ccdData)
402  {
403  key.ccd = true;
404  }
405 
406  // Avoid a couple cases by swapping
407  // Only V_T, no TV
408  // Only V_E, no EV
409  // Always Body on the right
410  // Always Primitive on left
411  if ((key.elemAType == PbdContactCase::Triangle && key.elemBType == PbdContactCase::Vertex)
412  || (key.elemAType == PbdContactCase::Edge && key.elemBType == PbdContactCase::Vertex)
413  || (key.elemBType == PbdContactCase::Body && key.elemAType != PbdContactCase::Body)
414  || (key.elemAType != PbdContactCase::Primitive && key.elemBType == PbdContactCase::Primitive))
415  {
416  if (!(key.elemAType == PbdContactCase::Body && key.elemBType == PbdContactCase::Primitive))
417  {
418  std::swap(key.elemAType, key.elemBType);
419  std::swap(sideA, sideB);
420  }
421  }
422  }
423 
424  auto iter = m_funcTable.find(key);
425  if (iter != m_funcTable.end())
426  {
427  iter->second(sideA, sideB);
428  }
429  else
430  {
431  // CH's may not handle all CollisionElements types
432  LOG(INFO) << "Could not find handling case " << key;
433  }
434 }
435 
436 void
437 PbdCollisionHandling::addConstraint_Body_V(const ColElemSide& sideA, const ColElemSide& sideB)
438 {
439  const std::pair<PbdParticleId, Vec3d>& ptAAndContact = getBodyAndContactPoint(*sideA.elem, *sideA.data);
440  PbdParticleId ptB;
441 
442  // If one-way/sideB doesn't exist
443  if (sideB.data == nullptr)
444  {
445  // Try to resolve with PointDirection
446  const CollisionElement::Element& elem = sideA.elem->m_element;
447  Vec3d resolvePos;
448  if (sideA.elem->m_type == CollisionElementType::PointIndexDirection)
449  {
450  const Vec3d& pos = ptAAndContact.second;
451  const Vec3d& dir = elem.m_PointIndexDirectionElement.dir;
452  const double depth = elem.m_PointIndexDirectionElement.penetrationDepth;
453  resolvePos = pos + dir * depth;
454  }
455  else if (sideA.elem->m_type == CollisionElementType::PointDirection)
456  {
457  const Vec3d& pos = elem.m_PointDirectionElement.pt;
458  const Vec3d& dir = elem.m_PointDirectionElement.dir;
459  const double depth = elem.m_PointDirectionElement.penetrationDepth;
460  resolvePos = pos + dir * depth;
461  }
462  else
463  {
464  return;
465  }
466  ptB = sideA.data->model->addVirtualParticle(resolvePos, 0.0);
467  }
468  else
469  {
470  ptB = getVertex(*sideB.elem, *sideB.data)[0];
471  }
472 
473  PbdVertexToBodyConstraint* constraint = getCachedConstraint<PbdVertexToBodyConstraint>(BodyVertex);
474  constraint->initConstraint(sideA.data->model->getBodies(),
475  ptAAndContact.first,
476  ptAAndContact.second,
477  ptB,
478  sideA.data->compliance);
479  constraint->setFriction(m_friction);
480  constraint->setRestitution(m_restitution);
481  constraint->setCorrectVelocity(m_useCorrectVelocity);
482  m_constraintBins[BodyVertex].push_back(constraint);
483 }
484 
485 void
486 PbdCollisionHandling::addConstraint_Body_E(const ColElemSide& sideA, const ColElemSide& sideB)
487 {
488  const std::pair<PbdParticleId, Vec3d>& ptAAndContact = getBodyAndContactPoint(*sideA.elem, *sideA.data);
489  std::array<PbdParticleId, 2> ptsB = getEdge(*sideB.elem, *sideB.data);
490 
491  PbdEdgeToBodyConstraint* constraint = getCachedConstraint<PbdEdgeToBodyConstraint>(BodyEdge);
492  constraint->initConstraint(sideB.data->model->getBodies(),
493  ptAAndContact.first,
494  ptAAndContact.second,
495  ptsB[0], ptsB[1],
496  sideA.data->compliance);
497  constraint->setFriction(m_friction);
498  constraint->setRestitution(m_restitution);
499  constraint->setCorrectVelocity(m_useCorrectVelocity);
500  m_constraintBins[BodyEdge].push_back(constraint);
501 }
502 
503 void
504 PbdCollisionHandling::addConstraint_Body_T(const ColElemSide& sideA, const ColElemSide& sideB)
505 {
506  const std::pair<PbdParticleId, Vec3d>& ptAAndContact = getBodyAndContactPoint(*sideA.elem, *sideA.data);
507  std::array<PbdParticleId, 3> ptsB = getTriangle(*sideB.elem, *sideB.data);
508 
509  PbdTriangleToBodyConstraint* constraint = getCachedConstraint<PbdTriangleToBodyConstraint>(BodyTriangle);
510  constraint->initConstraint(sideB.data->model->getBodies(),
511  ptAAndContact.first,
512  ptAAndContact.second,
513  ptsB[0], ptsB[1], ptsB[2],
514  sideA.data->compliance);
515  constraint->setFriction(m_friction);
516  constraint->setRestitution(m_restitution);
517  constraint->setCorrectVelocity(m_useCorrectVelocity);
518  m_constraintBins[BodyTriangle].push_back(constraint);
519 }
520 
521 void
522 PbdCollisionHandling::addConstraint_Body_Body(const ColElemSide& sideA, const ColElemSide& sideB)
523 {
524  const std::pair<PbdParticleId, Vec3d>& ptAAndContact = getBodyAndContactPoint(*sideA.elem, *sideA.data);
525  const std::pair<PbdParticleId, Vec3d>& ptBAndContact = getBodyAndContactPoint(*sideB.elem, *sideB.data);
526 
527  Vec3d normal = Vec3d::Zero();
528  if (sideA.elem->m_type == CollisionElementType::PointDirection)
529  {
530  normal = sideA.elem->m_element.m_PointDirectionElement.dir;
531  }
532 
533  PbdBodyToBodyNormalConstraint* constraint = getCachedConstraint<PbdBodyToBodyNormalConstraint>(BodyBody);
534 
535  constraint->initConstraint(
536  sideA.data->model->getBodies(),
537  ptAAndContact.first,
538  ptAAndContact.second,
539  ptBAndContact.first,
540  ptBAndContact.second,
541  normal,
542  sideA.data->compliance);
543  constraint->setFriction(m_friction);
544  constraint->setRestitution(m_restitution);
545  constraint->setCorrectVelocity(m_useCorrectVelocity);
546  m_constraintBins[BodyBody].push_back(constraint);
547 }
548 
549 void
550 PbdCollisionHandling::addConstraint_V_T(const ColElemSide& sideA, const ColElemSide& sideB)
551 {
552  const PbdParticleId ptA = getVertex(*sideA.elem, *sideA.data)[0];
553  std::array<PbdParticleId, 3> ptsB = getTriangle(*sideB.elem, *sideB.data);
554 
555  PbdPointTriangleConstraint* constraint = getCachedConstraint<PbdPointTriangleConstraint>(VertexTriangle);
556  constraint->initConstraint(ptA, ptsB[0], ptsB[1], ptsB[2],
557  sideA.data->stiffness, sideB.data->stiffness);
558  constraint->setFriction(m_friction);
559  constraint->setRestitution(m_restitution);
560  constraint->setEnableBoundaryCollisions(m_enableBoundaryCollisions);
561  constraint->setCorrectVelocity(m_useCorrectVelocity);
562  m_constraintBins[VertexTriangle].push_back(constraint);
563 }
564 
565 void
566 PbdCollisionHandling::addConstraint_E_E(const ColElemSide& sideA, const ColElemSide& sideB)
567 {
568  std::array<PbdParticleId, 2> ptsA = getEdge(*sideA.elem, *sideA.data);
569  std::array<PbdParticleId, 2> ptsB = getEdge(*sideB.elem, *sideB.data);
570 
571  PbdEdgeEdgeConstraint* constraint = getCachedConstraint<PbdEdgeEdgeConstraint>(EdgeEdge);
572  constraint->initConstraint(ptsA[0], ptsA[1], ptsB[0], ptsB[1],
573  sideA.data->stiffness, sideB.data->stiffness);
574  constraint->setFriction(m_friction);
575  constraint->setRestitution(m_restitution);
576  constraint->setEnableBoundaryCollisions(m_enableBoundaryCollisions);
577  constraint->setCorrectVelocity(m_useCorrectVelocity);
578  m_constraintBins[EdgeEdge].push_back(constraint);
579 }
580 
581 void
582 PbdCollisionHandling::addConstraint_E_E_CCD(
583  const ColElemSide& sideA,
584  const ColElemSide& sideB)
585 {
586  std::array<PbdParticleId, 2> ptsA = getEdge(*sideA.elem, *sideA.data);
587  std::array<PbdParticleId, 2> ptsB = getEdge(*sideB.elem, *sideB.data);
588 
589  // We want to refer to the same vertices but from a different geometry
590  // We want to ensure the vertex is added as virtual
591  std::array<Vec3d*, 2> prevPtsA = getElementVertIdsPrev<2>(ptsA, *sideA.data);
592  std::array<Vec3d*, 2> prevPtsB = getElementVertIdsPrev<2>(ptsB, *sideB.data);
593 
594  PbdEdgeEdgeCCDConstraint* constraint = getCachedConstraint<PbdEdgeEdgeCCDConstraint>(EdgeEdgeCCD);
595  constraint->initConstraint(
596  prevPtsA[0], prevPtsA[1], prevPtsB[0], prevPtsB[1],
597  ptsA[0], ptsA[1], ptsB[0], ptsB[1],
598  sideA.data->stiffness, sideB.data->stiffness,
599  m_ccdSubsteps);
600  constraint->setEnableBoundaryCollisions(m_enableBoundaryCollisions);
601  constraint->setCorrectVelocity(m_useCorrectVelocity);
602  m_constraintBins[EdgeEdgeCCD].push_back(constraint);
603 }
604 
605 void
606 PbdCollisionHandling::addConstraint_V_E(const ColElemSide& sideA, const ColElemSide& sideB)
607 {
608  const PbdParticleId ptA = getVertex(*sideA.elem, *sideA.data)[0];
609  std::array<PbdParticleId, 2> ptsB = getEdge(*sideB.elem, *sideB.data);
610 
611  PbdPointEdgeConstraint* constraint = getCachedConstraint<PbdPointEdgeConstraint>(VertexEdge);
612  constraint->initConstraint(ptA, ptsB[0], ptsB[1],
613  sideA.data->stiffness, sideB.data->stiffness);
614  constraint->setFriction(m_friction);
615  constraint->setRestitution(m_restitution);
616  constraint->setEnableBoundaryCollisions(m_enableBoundaryCollisions);
617  constraint->setCorrectVelocity(m_useCorrectVelocity);
618  m_constraintBins[VertexEdge].push_back(constraint);
619 }
620 
621 void
622 PbdCollisionHandling::addConstraint_V_V(const ColElemSide& sideA, const ColElemSide& sideB)
623 {
624  // One special case with one-way
625  const PbdParticleId ptA = getVertex(*sideA.elem, *sideA.data)[0];
626  PbdParticleId ptB;
627 
628  // If one-way/sideB doesn't exist
629  if (sideB.data == nullptr)
630  {
631  // Try to resolve with PointDirection
632  const CollisionElement::Element& elem = sideA.elem->m_element;
633  Vec3d resolvePos;
634  if (sideA.elem->m_type == CollisionElementType::PointIndexDirection)
635  {
636  const Vec3d& pos = sideA.data->model->getBodies().getPosition(ptA);
637  const Vec3d& dir = elem.m_PointIndexDirectionElement.dir;
638  const double depth = elem.m_PointIndexDirectionElement.penetrationDepth;
639  resolvePos = pos + dir * depth;
640  }
641  else if (sideA.elem->m_type == CollisionElementType::PointDirection)
642  {
643  const Vec3d& pos = elem.m_PointDirectionElement.pt;
644  const Vec3d& dir = elem.m_PointDirectionElement.dir;
645  const double depth = elem.m_PointDirectionElement.penetrationDepth;
646  resolvePos = pos + dir * depth;
647  }
648  else
649  {
650  return;
651  }
652  ptB = sideA.data->model->addVirtualParticle(resolvePos, 0.0);
653  }
654  else
655  {
656  ptB = getVertex(*sideB.elem, *sideB.data)[0];
657  }
658 
659  PbdPointPointConstraint* constraint = getCachedConstraint<PbdPointPointConstraint>(VertexVertex);
660  constraint->initConstraint(ptA, ptB,
661  sideA.data->stiffness,
662  (sideB.data == nullptr) ? 0.0 : sideB.data->stiffness);
663  constraint->setFriction(m_friction);
664  constraint->setRestitution(m_restitution);
665  constraint->setEnableBoundaryCollisions(m_enableBoundaryCollisions);
666  constraint->setCorrectVelocity(m_useCorrectVelocity);
667  m_constraintBins[VertexVertex].push_back(constraint);
668 }
669 
670 void
671 PbdCollisionHandling::deleteCollisionConstraints()
672 {
673  // Deletes all collision Constraints
674  // There could be a large variance in constraints/contacts count,
675  // 10s to 100s of constraints changing frequently over few frames.
676  for (int i = 0; i < NumTypes; i++)
677  {
678  for (size_t j = 0; j < m_constraintCache[i].size(); j++)
679  {
680  delete m_constraintCache[i][j];
681  }
682  m_constraintCache[i].resize(0);
683 
684  for (size_t j = 0; j < m_constraintBins[i].size(); j++)
685  {
686  delete m_constraintBins[i][j];
687  }
688  m_constraintBins[i].resize(0);
689  }
690 
691  m_collisionConstraints.resize(0);
692 }
693 
694 void
695 PbdCollisionHandling::orderCollisionConstraints()
696 {
697  // Add constraints from bins to constraints vector for solver
698  for (int i = 0; i < NumTypes; i++)
699  {
700  m_collisionConstraints.insert(m_collisionConstraints.end(), m_constraintBins[i].begin(), m_constraintBins[i].end());
701  m_constraintCache[i].insert(m_constraintCache[i].end(), m_constraintBins[i].begin(), m_constraintBins[i].end());
702  m_constraintBins[i].resize(0);
703  }
704 }
705 } // namespace imstk
Base class for all geometries represented by discrete points and elements The pointsets follow a pipe...
Definition: imstkPointSet.h:25
The PbdPointTriangleConstraint moves a point to a triangle, and the triangle to the point...
void initConstraint(const PbdState &state, const PbdParticleId &bodyId, const Vec3d contactPtOnBody, const PbdParticleId &x0, const PbdParticleId &x1, const double compliance=0.0)
Initialize the constraint.
CollisionSideData getDataFromObject(std::shared_ptr< CollidingObject > obj)
Creates a CollisionSideData struct from the provided object, this gives all the info needed to respon...
std::pair< int, int > PbdParticleId
Index pair that refers to a particle in a PbdState. Index 0 is the body id, Index 1 is the particle i...
std::shared_ptr< const CollisionData > m_colData
Collision data.
Resolves a point on body to a triangle with linear and angular movement.
std::shared_ptr< GeometryMap > getPhysicsToCollidingMap() const
Set/Get the Physics-to-Collision map.
std::shared_ptr< PbdModel > getPbdModel()
void initConstraint(const PbdState &state, const PbdParticleId &bodyId0, const Vec3d contactPt0, const PbdParticleId &bodyId1, const Vec3d contactPt1, const Vec3d contactNormal0To1, const double compliance)
Initialize the constraint ptOnBody is global position.
void initConstraint(const PbdParticleId &ptA1, const PbdParticleId &ptA2, const PbdParticleId &ptB1, const PbdParticleId &ptB2, double stiffnessA, double stiffnessB)
Initialize constraint.
Compound Geometry.
void handle(const std::vector< CollisionElement > &elementsA, const std::vector< CollisionElement > &elementsB) override
Add collision constraints based off contact data.
Resolves a point on body to an edge with linear and angular movement.
void initConstraint(Vec3d *prevPtA0, Vec3d *prevPtA1, Vec3d *prevPtB0, Vec3d *prevPtB1, const PbdParticleId &ptA0, const PbdParticleId &ptA1, const PbdParticleId &ptB0, const PbdParticleId &ptB1, double stiffnessA, double stiffnessB, int ccdSubsteps=25)
Initialize constraint.
void initConstraint(const PbdParticleId &ptA1, const PbdParticleId &ptB1, const PbdParticleId &ptB2, double stiffnessA, double stiffnessB)
Initialize the constraint.
std::array< PbdParticleId, 1 > getVertex(const CollisionElement &elem, const CollisionSideData &side)
getVertex takes slightly differing paths than the others, as the cell vertex directly refers to the v...
int getParentVertexId(const int childVertexId) const
Get the mapped/corresponding parent index, given a child index. returns -1 if no correspondence found...
Union of collision elements. We use a union to avoid polymorphism. There may be many elements and acc...
PbdParticleId addVirtualParticle(const Vec3d &pos, const Quatd &orientation, const double mass, const Mat3d inertia, const Vec3d &velocity=Vec3d::Zero(), const Vec3d &angularVelocity=Vec3d::Zero(), const bool persist=false)
Add a particle to a virtual pool/buffer of particles for quick removal/insertion The persist flag ind...
void initConstraint(const PbdState &state, const PbdParticleId &bodyId, const Vec3d contactPtOnBody, const PbdParticleId &x0, const PbdParticleId &x1, const PbdParticleId &x2, const double compliance=0.0)
Initialize the constraint.
std::shared_ptr< VecDataArray< double, 3 > > getVertexPositions(DataType type=DataType::PostTransform) const
Returns the vector of current positions of the mesh vertices.
void handleElementPair(ColElemSide sideA, ColElemSide sideB)
Handle a single element.
Base class for scene objects that move and/or deform under position based dynamics formulation...
void initConstraint(const PbdState &state, const PbdParticleId &bodyId, const Vec3d contactPtOnBody, const PbdParticleId &x0, const double compliance=0.0)
Initialize the constraint.
void initConstraint(const PbdParticleId &ptA, const PbdParticleId &ptB1, const PbdParticleId &ptB2, const PbdParticleId &ptB3, double stiffnessA, double stiffnessB)
Initialize the constraint.
std::shared_ptr< PbdBody > getPbdBody()
Returns body in the model.
std::shared_ptr< CollidingObject > getInputObjectA() const
Get the input objects.
Resolves an edge given by two pbd particles to coincide with another edge.
void setEnableBoundaryCollisions(const double enableBoundaryCollisions)
Get enableBoundaryCollision.
std::shared_ptr< Geometry > getPhysicsGeometry() const
Set/Get the geometry used for Physics computations.
PointwiseMap can compute & apply a mapping between parent and child PointSet geometries.
Packs the collision element together with the data it will need to process it (for swapping) ...
std::vector< PbdConstraint * > m_collisionConstraints
Vector of all collision constraints.
This constraint resolves two vertices to each other.
Pushes an edge "outside" the other edge.
void initConstraint(const PbdParticleId &ptA, const PbdParticleId &ptB, double stiffnessA, double stiffnessB)
Initialize constraint.
PbdContactCase getCaseFromElement(const ColElemSide &elem)
Get the contact case from the collision element and data as additional context.
std::pair< PbdParticleId, Vec3d > getBodyAndContactPoint(const CollisionElement &elem, const CollisionSideData &data)
Get the body particle id from the collision side as well as the contact point on the body (in global ...
Used as a key in a function table to decide how to handle resulting collision.