Bullet Collision Detection & Physics Library
btDeformableBodySolver.cpp
Go to the documentation of this file.
1/*
2 Written by Xuchen Han <xuchenhan2015@u.northwestern.edu>
3
4 Bullet Continuous Collision Detection and Physics Library
5 Copyright (c) 2019 Google Inc. http://bulletphysics.org
6 This software is provided 'as-is', without any express or implied warranty.
7 In no event will the authors be held liable for any damages arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it freely,
10 subject to the following restrictions:
11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
14 */
15
16#include <stdio.h>
17#include <limits>
19#include "btSoftBodyInternals.h"
21static const int kMaxConjugateGradientIterations = 300;
23 : m_numNodes(0), m_cg(kMaxConjugateGradientIterations), m_cr(kMaxConjugateGradientIterations), m_maxNewtonIterations(1), m_newtonTolerance(1e-4), m_lineSearch(false), m_useProjection(false)
24{
26 m_reducedSolver = false;
27}
28
30{
31 delete m_objective;
32}
33
35{
36 BT_PROFILE("solveDeformableConstraints");
37 if (!m_implicit)
38 {
42 {
44 }
45 else
46 {
47 TVStack rhs, x;
51 computeStep(x, rhs);
52 for (int i = 0; i < m_dv.size(); ++i)
53 {
54 m_dv[i] = x[i];
55 }
56 }
58 }
59 else
60 {
61 for (int i = 0; i < m_maxNewtonIterations; ++i)
62 {
64 // add the inertia term in the residual
65 int counter = 0;
66 for (int k = 0; k < m_softBodies.size(); ++k)
67 {
68 btSoftBody* psb = m_softBodies[k];
69 for (int j = 0; j < psb->m_nodes.size(); ++j)
70 {
71 if (psb->m_nodes[j].m_im > 0)
72 {
73 m_residual[counter] = (-1. / psb->m_nodes[j].m_im) * m_dv[counter];
74 }
75 ++counter;
76 }
77 }
78
81 {
82 break;
83 }
84 // todo xuchenhan@: this really only needs to be calculated once
86 if (m_lineSearch)
87 {
89 btScalar alpha = 0.01, beta = 0.5; // Boyd & Vandenberghe suggested alpha between 0.01 and 0.3, beta between 0.1 to 0.8
90 btScalar scale = 2;
91 btScalar f0 = m_objective->totalEnergy(solverdt) + kineticEnergy(), f1, f2;
92 backupDv();
93 do
94 {
95 scale *= beta;
96 if (scale < 1e-8)
97 {
98 return;
99 }
100 updateEnergy(scale);
101 f1 = m_objective->totalEnergy(solverdt) + kineticEnergy();
102 f2 = f0 - alpha * scale * inner_product;
103 } while (!(f1 < f2 + SIMD_EPSILON)); // if anything here is nan then the search continues
104 revertDv();
105 updateDv(scale);
106 }
107 else
108 {
110 updateDv();
111 }
112 for (int j = 0; j < m_numNodes; ++j)
113 {
114 m_ddv[j].setZero();
115 m_residual[j].setZero();
116 }
117 }
119 }
120}
121
123{
124 btScalar ke = 0;
125 for (int i = 0; i < m_softBodies.size(); ++i)
126 {
127 btSoftBody* psb = m_softBodies[i];
128 for (int j = 0; j < psb->m_nodes.size(); ++j)
129 {
130 btSoftBody::Node& node = psb->m_nodes[j];
131 if (node.m_im > 0)
132 {
133 ke += m_dv[node.index].length2() * 0.5 / node.m_im;
134 }
135 }
136 }
137 return ke;
138}
139
141{
143 for (int i = 0; i < m_backup_dv.size(); ++i)
144 {
145 m_backup_dv[i] = m_dv[i];
146 }
147}
148
150{
151 for (int i = 0; i < m_backup_dv.size(); ++i)
152 {
153 m_dv[i] = m_backup_dv[i];
154 }
155}
156
158{
159 for (int i = 0; i < m_dv.size(); ++i)
160 {
161 m_dv[i] = m_backup_dv[i] + scale * m_ddv[i];
162 }
163 updateState();
164}
165
167{
168 m_cg.solve(*m_objective, ddv, residual, false);
169 btScalar inner_product = m_cg.dot(residual, m_ddv);
170 btScalar res_norm = m_objective->computeNorm(residual);
171 btScalar tol = 1e-5 * res_norm * m_objective->computeNorm(m_ddv);
172 if (inner_product < -tol)
173 {
174 if (verbose)
175 {
176 std::cout << "Looking backwards!" << std::endl;
177 }
178 for (int i = 0; i < m_ddv.size(); ++i)
179 {
180 m_ddv[i] = -m_ddv[i];
181 }
182 inner_product = -inner_product;
183 }
184 else if (std::abs(inner_product) < tol)
185 {
186 if (verbose)
187 {
188 std::cout << "Gradient Descent!" << std::endl;
189 }
190 btScalar scale = m_objective->computeNorm(m_ddv) / res_norm;
191 for (int i = 0; i < m_ddv.size(); ++i)
192 {
193 m_ddv[i] = scale * residual[i];
194 }
195 inner_product = scale * res_norm * res_norm;
196 }
197 return inner_product;
198}
199
201{
204}
205
207{
208 for (int i = 0; i < m_numNodes; ++i)
209 {
210 m_dv[i] += scale * m_ddv[i];
211 }
212}
213
215{
216 if (m_useProjection)
217 m_cg.solve(*m_objective, ddv, residual, false);
218 else
219 m_cr.solve(*m_objective, ddv, residual, false);
220}
221
223{
224 m_softBodies.copyFromArray(softBodies);
225 bool nodeUpdated = updateNodes();
226
227 if (nodeUpdated)
228 {
229 m_dv.resize(m_numNodes, btVector3(0, 0, 0));
230 m_ddv.resize(m_numNodes, btVector3(0, 0, 0));
233 }
234
235 // need to setZero here as resize only set value for newly allocated items
236 for (int i = 0; i < m_numNodes; ++i)
237 {
238 m_dv[i].setZero();
239 m_ddv[i].setZero();
240 m_residual[i].setZero();
241 }
242
243 if (dt > 0)
244 {
245 m_dt = dt;
246 }
247 m_objective->reinitialize(nodeUpdated, dt);
249}
250
252{
253 BT_PROFILE("setConstraint");
254 m_objective->setConstraints(infoGlobal);
255}
256
257btScalar btDeformableBodySolver::solveContactConstraints(btCollisionObject** deformableBodies, int numDeformableBodies, const btContactSolverInfo& infoGlobal)
258{
259 BT_PROFILE("solveContactConstraints");
260 btScalar maxSquaredResidual = m_objective->m_projection.update(deformableBodies, numDeformableBodies, infoGlobal);
261 return maxSquaredResidual;
262}
263
265{
266 int counter = 0;
267 for (int i = 0; i < m_softBodies.size(); ++i)
268 {
269 btSoftBody* psb = m_softBodies[i];
270 psb->m_maxSpeedSquared = 0;
271 if (!psb->isActive())
272 {
273 counter += psb->m_nodes.size();
274 continue;
275 }
276 for (int j = 0; j < psb->m_nodes.size(); ++j)
277 {
278 // set NaN to zero;
279 if (m_dv[counter] != m_dv[counter])
280 {
281 m_dv[counter].setZero();
282 }
283 if (m_implicit)
284 {
285 psb->m_nodes[j].m_v = m_backupVelocity[counter] + m_dv[counter];
286 }
287 else
288 {
289 psb->m_nodes[j].m_v = m_backupVelocity[counter] + m_dv[counter] - psb->m_nodes[j].m_splitv;
290 }
291 psb->m_maxSpeedSquared = btMax(psb->m_maxSpeedSquared, psb->m_nodes[j].m_v.length2());
292 ++counter;
293 }
294 }
295}
296
298{
299 int counter = 0;
300 for (int i = 0; i < m_softBodies.size(); ++i)
301 {
302 btSoftBody* psb = m_softBodies[i];
303 if (!psb->isActive())
304 {
305 counter += psb->m_nodes.size();
306 continue;
307 }
308 for (int j = 0; j < psb->m_nodes.size(); ++j)
309 {
310 psb->m_nodes[j].m_q = psb->m_nodes[j].m_x + m_dt * (psb->m_nodes[j].m_v + psb->m_nodes[j].m_splitv);
311 ++counter;
312 }
313 psb->updateDeformation();
314 }
315}
316
318{
319 int counter = 0;
320 for (int i = 0; i < m_softBodies.size(); ++i)
321 {
322 btSoftBody* psb = m_softBodies[i];
323 for (int j = 0; j < psb->m_nodes.size(); ++j)
324 {
325 m_backupVelocity[counter++] = psb->m_nodes[j].m_v;
326 }
327 }
328}
329
331{
332 int counter = 0;
333 for (int i = 0; i < m_softBodies.size(); ++i)
334 {
335 btSoftBody* psb = m_softBodies[i];
336 if (!psb->isActive())
337 {
338 counter += psb->m_nodes.size();
339 continue;
340 }
341 for (int j = 0; j < psb->m_nodes.size(); ++j)
342 {
343 if (implicit)
344 {
345 // setting the initial guess for newton, need m_dv = v_{n+1} - v_n for dofs that are in constraint.
346 if (psb->m_nodes[j].m_v == m_backupVelocity[counter])
347 m_dv[counter].setZero();
348 else
349 m_dv[counter] = psb->m_nodes[j].m_v - psb->m_nodes[j].m_vn;
350 m_backupVelocity[counter] = psb->m_nodes[j].m_vn;
351 }
352 else
353 {
354 m_dv[counter] = psb->m_nodes[j].m_v + psb->m_nodes[j].m_splitv - m_backupVelocity[counter];
355 }
356 psb->m_nodes[j].m_v = m_backupVelocity[counter];
357 ++counter;
358 }
359 }
360}
361
363{
364 int counter = 0;
365 for (int i = 0; i < m_softBodies.size(); ++i)
366 {
367 btSoftBody* psb = m_softBodies[i];
368 for (int j = 0; j < psb->m_nodes.size(); ++j)
369 {
370 psb->m_nodes[j].m_v = m_backupVelocity[counter++];
371 }
372 }
373}
374
376{
377 int numNodes = 0;
378 for (int i = 0; i < m_softBodies.size(); ++i)
379 numNodes += m_softBodies[i]->m_nodes.size();
380 if (numNodes != m_numNodes)
381 {
382 m_numNodes = numNodes;
383 return true;
384 }
385 return false;
386}
387
389{
390 // apply explicit forces to velocity
391 if (m_implicit)
392 {
393 for (int i = 0; i < m_softBodies.size(); ++i)
394 {
395 btSoftBody* psb = m_softBodies[i];
396 if (psb->isActive())
397 {
398 for (int j = 0; j < psb->m_nodes.size(); ++j)
399 {
400 psb->m_nodes[j].m_q = psb->m_nodes[j].m_x + psb->m_nodes[j].m_v * solverdt;
401 }
402 }
403 }
404 }
406 for (int i = 0; i < m_softBodies.size(); ++i)
407 {
408 btSoftBody* psb = m_softBodies[i];
409 if (psb->isActive())
410 {
411 /* Clear contacts when softbody is active*/
412 psb->m_nodeRigidContacts.resize(0);
413 psb->m_faceRigidContacts.resize(0);
414 psb->m_faceNodeContacts.resize(0);
415 psb->m_faceNodeContactsCCD.resize(0);
416 // predict motion for collision detection
417 predictDeformableMotion(psb, solverdt);
418 }
419 }
420}
421
423{
424 BT_PROFILE("btDeformableBodySolver::predictDeformableMotion");
425 int i, ni;
426
427 /* Update */
428 if (psb->m_bUpdateRtCst)
429 {
430 psb->m_bUpdateRtCst = false;
431 psb->updateConstants();
432 psb->m_fdbvt.clear();
434 {
435 psb->initializeFaceTree();
436 }
437 }
438
439 /* Prepare */
440 psb->m_sst.sdt = dt * psb->m_cfg.timescale;
441 psb->m_sst.isdt = 1 / psb->m_sst.sdt;
442 psb->m_sst.velmrg = psb->m_sst.sdt * 3;
443 psb->m_sst.radmrg = psb->getCollisionShape()->getMargin();
444 psb->m_sst.updmrg = psb->m_sst.radmrg * (btScalar)0.25;
445 /* Bounds */
446 psb->updateBounds();
447
448 /* Integrate */
449 // do not allow particles to move more than the bounding box size
450 btScalar max_v = (psb->m_bounds[1] - psb->m_bounds[0]).norm() / dt;
451 for (i = 0, ni = psb->m_nodes.size(); i < ni; ++i)
452 {
453 btSoftBody::Node& n = psb->m_nodes[i];
454 // apply drag
455 n.m_v *= (1 - psb->m_cfg.drag);
456 // scale velocity back
457 if (m_implicit)
458 {
459 n.m_q = n.m_x;
460 }
461 else
462 {
463 if (n.m_v.norm() > max_v)
464 {
465 n.m_v.safeNormalize();
466 n.m_v *= max_v;
467 }
468 n.m_q = n.m_x + n.m_v * dt;
469 }
470 n.m_splitv.setZero();
471 n.m_constrained = false;
472 }
473
474 /* Nodes */
475 psb->updateNodeTree(true, true);
476 if (!psb->m_fdbvt.empty())
477 {
478 psb->updateFaceTree(true, true);
479 }
480 /* Optimize dbvt's */
481 // psb->m_ndbvt.optimizeIncremental(1);
482 // psb->m_fdbvt.optimizeIncremental(1);
483}
484
486{
487 BT_PROFILE("updateSoftBodies");
488 for (int i = 0; i < m_softBodies.size(); i++)
489 {
491 if (psb->isActive())
492 {
493 psb->updateNormals();
494 }
495 }
496}
497
499{
500 m_implicit = implicit;
501 m_objective->setImplicit(implicit);
502}
503
505{
506 m_lineSearch = lineSearch;
507}
508
510{
512}
513
515{
516 for (int i = 0; i < m_softBodies.size(); ++i)
517 {
518 btSoftBody* psb = m_softBodies[i];
519 for (int j = 0; j < psb->m_nodes.size(); ++j)
520 {
521 btSoftBody::Node& node = psb->m_nodes[j];
522 btScalar maxDisplacement = psb->getWorldInfo()->m_maxDisplacement;
523 btScalar clampDeltaV = maxDisplacement / timeStep;
524 for (int c = 0; c < 3; c++)
525 {
526 if (node.m_v[c] > clampDeltaV)
527 {
528 node.m_v[c] = clampDeltaV;
529 }
530 if (node.m_v[c] < -clampDeltaV)
531 {
532 node.m_v[c] = -clampDeltaV;
533 }
534 }
535 node.m_x = node.m_x + timeStep * (node.m_v + node.m_splitv);
536 node.m_q = node.m_x;
537 node.m_vn = node.m_v;
538 }
539 // enforce anchor constraints
540 for (int j = 0; j < psb->m_deformableAnchors.size(); ++j)
541 {
545
546 // update multibody anchor info
548 {
550 if (multibodyLinkCol)
551 {
552 btVector3 nrm;
553 const btCollisionShape* shp = multibodyLinkCol->getCollisionShape();
554 const btTransform& wtr = multibodyLinkCol->getWorldTransform();
556 wtr.invXform(n->m_x),
557 shp,
558 nrm,
559 0);
560 a.m_cti.m_normal = wtr.getBasis() * nrm;
561 btVector3 normal = a.m_cti.m_normal;
563 btVector3 t2 = btCross(normal, t1);
564 btMultiBodyJacobianData jacobianData_normal, jacobianData_t1, jacobianData_t2;
565 findJacobian(multibodyLinkCol, jacobianData_normal, a.m_node->m_x, normal);
566 findJacobian(multibodyLinkCol, jacobianData_t1, a.m_node->m_x, t1);
567 findJacobian(multibodyLinkCol, jacobianData_t2, a.m_node->m_x, t2);
568
569 btScalar* J_n = &jacobianData_normal.m_jacobians[0];
570 btScalar* J_t1 = &jacobianData_t1.m_jacobians[0];
571 btScalar* J_t2 = &jacobianData_t2.m_jacobians[0];
572
573 btScalar* u_n = &jacobianData_normal.m_deltaVelocitiesUnitImpulse[0];
574 btScalar* u_t1 = &jacobianData_t1.m_deltaVelocitiesUnitImpulse[0];
575 btScalar* u_t2 = &jacobianData_t2.m_deltaVelocitiesUnitImpulse[0];
576
577 btMatrix3x3 rot(normal.getX(), normal.getY(), normal.getZ(),
578 t1.getX(), t1.getY(), t1.getZ(),
579 t2.getX(), t2.getY(), t2.getZ()); // world frame to local frame
580 const int ndof = multibodyLinkCol->m_multiBody->getNumDofs() + 6;
581 btMatrix3x3 local_impulse_matrix = (Diagonal(n->m_im) + OuterProduct(J_n, J_t1, J_t2, u_n, u_t1, u_t2, ndof)).inverse();
582 a.m_c0 = rot.transpose() * local_impulse_matrix * rot;
583 a.jacobianData_normal = jacobianData_normal;
584 a.jacobianData_t1 = jacobianData_t1;
585 a.jacobianData_t2 = jacobianData_t2;
586 a.t1 = t1;
587 a.t2 = t2;
588 }
589 }
590 }
592 }
593}
static const int kMaxConjugateGradientIterations
const T & btMax(const T &a, const T &b)
Definition: btMinMax.h:27
btQuaternion inverse(const btQuaternion &q)
Return the inverse of a quaternion.
Definition: btQuaternion.h:909
#define BT_PROFILE(name)
Definition: btQuickprof.h:198
float btScalar
The btScalar type abstracts floating point numbers, to easily switch between double and single floati...
Definition: btScalar.h:314
#define SIMD_EPSILON
Definition: btScalar.h:543
static btMatrix3x3 OuterProduct(const btScalar *v1, const btScalar *v2, const btScalar *v3, const btScalar *u1, const btScalar *u2, const btScalar *u3, int ndof)
static btVector3 generateUnitOrthogonalVector(const btVector3 &u)
static void findJacobian(const btMultiBodyLinkCollider *multibodyLinkCol, btMultiBodyJacobianData &jacobianData, const btVector3 &contact_point, const btVector3 &dir)
btSoftBody implementation by Nathanael Presson
static btMatrix3x3 Diagonal(btScalar x)
btVector3 btCross(const btVector3 &v1, const btVector3 &v2)
Return the cross product of two vectors.
Definition: btVector3.h:918
virtual void reinitialize(bool nodeUpdated)=0
int size() const
return the number of elements in the array
void copyFromArray(const btAlignedObjectArray &otherArray)
void resize(int newsize, const T &fillData=T())
btCollisionObject can be used to manage collision detection objects.
btTransform & getWorldTransform()
int getInternalType() const
reserved for Bullet internal usage
const btCollisionShape * getCollisionShape() const
The btCollisionShape class provides an interface for collision shapes that can be shared among btColl...
virtual btScalar getMargin() const =0
int solve(MatrixX &A, TVStack &x, const TVStack &b, bool verbose=false)
int solve(MatrixX &A, TVStack &x, const TVStack &b, bool verbose=false)
void setConstraints(const btContactSolverInfo &infoGlobal)
void computeResidual(btScalar dt, TVStack &residual)
void addLagrangeMultiplier(const TVStack &vec, TVStack &extended_vec)
btScalar computeNorm(const TVStack &residual) const
void reinitialize(bool nodeUpdated, btScalar dt)
void addLagrangeMultiplierRHS(const TVStack &residual, const TVStack &m_dv, TVStack &extended_residual)
virtual void updateSoftBodies()
Perform necessary per-step updates of soft bodies such as recomputing normals and bounding boxes.
virtual void solveDeformableConstraints(btScalar solverdt)
void updateEnergy(btScalar scale)
virtual void setConstraints(const btContactSolverInfo &infoGlobal)
btDeformableBackwardEulerObjective * m_objective
virtual void applyTransforms(btScalar timeStep)
btScalar computeDescentStep(TVStack &ddv, const TVStack &residual, bool verbose=false)
void predictDeformableMotion(btSoftBody *psb, btScalar dt)
btConjugateResidual< btDeformableBackwardEulerObjective > m_cr
btConjugateGradient< btDeformableBackwardEulerObjective > m_cg
virtual void reinitialize(const btAlignedObjectArray< btSoftBody * > &softBodies, btScalar dt)
virtual void setupDeformableSolve(bool implicit)
void setLineSearch(bool lineSearch)
btAlignedObjectArray< btSoftBody * > m_softBodies
virtual btScalar solveContactConstraints(btCollisionObject **deformableBodies, int numDeformableBodies, const btContactSolverInfo &infoGlobal)
virtual void predictMotion(btScalar solverdt)
Predict motion of soft bodies into next timestep.
void computeStep(TVStack &ddv, const TVStack &residual)
void updateDv(btScalar scale=1)
virtual btScalar update(btCollisionObject **deformableBodies, int numDeformableBodies, const btContactSolverInfo &infoGlobal)
virtual btScalar dot(const TVStack &a, const TVStack &b)
The btMatrix3x3 class implements a 3x3 rotation matrix, to perform linear algebra in combination with...
Definition: btMatrix3x3.h:50
btMatrix3x3 transpose() const
Return the transpose of the matrix.
Definition: btMatrix3x3.h:1049
static btMultiBodyLinkCollider * upcast(btCollisionObject *colObj)
int getNumDofs() const
Definition: btMultiBody.h:167
btMultiBodyJacobianData jacobianData_t1
Definition: btSoftBody.h:372
btMultiBodyJacobianData jacobianData_normal
Definition: btSoftBody.h:371
btMultiBodyJacobianData jacobianData_t2
Definition: btSoftBody.h:373
The btSoftBody is an class to simulate cloth and volumetric soft bodies.
Definition: btSoftBody.h:75
bool m_bUpdateRtCst
Definition: btSoftBody.h:834
void interpolateRenderMesh()
void updateFaceTree(bool use_velocity, bool margin)
Definition: btSoftBody.h:1300
SolverState m_sst
Definition: btSoftBody.h:809
void updateNodeTree(bool use_velocity, bool margin)
Definition: btSoftBody.h:1257
btAlignedObjectArray< DeformableFaceNodeContact > m_faceNodeContactsCCD
Definition: btSoftBody.h:828
btSoftBodyWorldInfo * m_worldInfo
Definition: btSoftBody.h:812
btAlignedObjectArray< DeformableFaceNodeContact > m_faceNodeContacts
Definition: btSoftBody.h:826
Config m_cfg
Definition: btSoftBody.h:808
void updateDeformation()
btVector3 m_bounds[2]
Definition: btSoftBody.h:833
btScalar m_maxSpeedSquared
Definition: btSoftBody.h:842
btAlignedObjectArray< DeformableFaceRigidContact > m_faceRigidContacts
Definition: btSoftBody.h:827
btAlignedObjectArray< DeformableNodeRigidAnchor > m_deformableAnchors
Definition: btSoftBody.h:823
btAlignedObjectArray< DeformableNodeRigidContact > m_nodeRigidContacts
Definition: btSoftBody.h:825
void updateNormals()
btDbvt m_fdbvt
Definition: btSoftBody.h:836
btSoftBodyWorldInfo * getWorldInfo()
Definition: btSoftBody.h:881
tNodeArray m_nodes
Definition: btSoftBody.h:814
void updateConstants()
void updateBounds()
void initializeFaceTree()
The btTransform class supports rigid transforms with only translation and rotation and no scaling/she...
Definition: btTransform.h:30
btVector3 invXform(const btVector3 &inVec) const
Definition: btTransform.h:216
btMatrix3x3 & getBasis()
Return the basis matrix for the rotation.
Definition: btTransform.h:109
btVector3 can be used to represent 3D points and vectors.
Definition: btVector3.h:82
const btScalar & getZ() const
Return the z value.
Definition: btVector3.h:565
btVector3 & safeNormalize()
Definition: btVector3.h:286
btScalar norm() const
Return the norm (length) of the vector.
Definition: btVector3.h:263
const btScalar & getY() const
Return the y value.
Definition: btVector3.h:563
void setZero()
Definition: btVector3.h:671
const btScalar & getX() const
Return the x value.
Definition: btVector3.h:561
bool empty() const
Definition: btDbvt.h:314
void clear()
Definition: btDbvt.cpp:477
btAlignedObjectArray< btScalar > m_deltaVelocitiesUnitImpulse
btAlignedObjectArray< btScalar > m_jacobians
btSparseSdf< 3 > m_sparsesdf
Definition: btSoftBody.h:57
btScalar m_maxDisplacement
Definition: btSoftBody.h:52
btScalar timescale
Definition: btSoftBody.h:730
btVector3 m_x
Definition: btSoftBody.h:269
btVector3 m_splitv
Definition: btSoftBody.h:281
btVector3 m_vn
Definition: btSoftBody.h:272
btVector3 m_v
Definition: btSoftBody.h:271
btVector3 m_q
Definition: btSoftBody.h:270
@ SDF_RD
Cluster vs convex rigid vs soft.
Definition: btSoftBody.h:166
const btCollisionObject * m_colObj
Definition: btSoftBody.h:226
btVector3 m_normal
Definition: btSoftBody.h:227
btScalar Evaluate(const btVector3 &x, const btCollisionShape *shape, btVector3 &normal, btScalar margin)
Definition: btSparseSDF.h:196