Bullet Collision Detection & Physics Library
btMLCPSolver.cpp
Go to the documentation of this file.
1/*
2Bullet Continuous Collision Detection and Physics Library
3Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org
4
5This software is provided 'as-is', without any express or implied warranty.
6In no event will the authors be held liable for any damages arising from the use of this software.
7Permission is granted to anyone to use this software for any purpose,
8including commercial applications, and to alter it and redistribute it freely,
9subject to the following restrictions:
10
111. 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.
122. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
133. This notice may not be removed or altered from any source distribution.
14*/
16
17#include "btMLCPSolver.h"
21
23 : m_solver(solver),
24 m_fallback(0)
25{
26}
27
29{
30}
31
32bool gUseMatrixMultiply = false;
34
36{
38
39 {
40 BT_PROFILE("gather constraint data");
41
43
44 // int numBodies = m_tmpSolverBodyPool.size();
48 // printf("m_limitDependencies.size() = %d\n",m_limitDependencies.size());
49
50 int dindex = 0;
51 for (int i = 0; i < m_tmpSolverNonContactConstraintPool.size(); i++)
52 {
55 }
56
58
60
62 {
63 for (int i = 0; i < m_tmpSolverContactConstraintPool.size(); i++)
64 {
70 if (numFrictionPerContact == 2)
71 {
74 }
75 }
76 }
77 else
78 {
79 for (int i = 0; i < m_tmpSolverContactConstraintPool.size(); i++)
80 {
83 }
84 for (int i = 0; i < m_tmpSolverContactFrictionConstraintPool.size(); i++)
85 {
88 }
89 }
90
92 {
93 m_A.resize(0, 0);
94 m_b.resize(0);
95 m_x.resize(0);
96 m_lo.resize(0);
97 m_hi.resize(0);
98 return 0.f;
99 }
100 }
101
103 {
104 BT_PROFILE("createMLCP");
106 }
107 else
108 {
109 BT_PROFILE("createMLCPFast");
111 }
112
113 return 0.f;
114}
115
117{
118 bool result = true;
119
120 if (m_A.rows() == 0)
121 return true;
122
123 //if using split impulse, we solve 2 separate (M)LCPs
124 if (infoGlobal.m_splitImpulse)
125 {
128 // printf("solve first LCP\n");
130 if (result)
132 }
133 else
134 {
136 }
137 return result;
138}
139
141{
142 int jointIndex; // pointer to enclosing dxJoint object
143 int otherBodyIndex; // *other* body this joint is connected to
144 int nextJointNodeIndex; //-1 for null
146};
147
149{
151
152 int numConstraintRows = m_allConstraintPtrArray.size();
153 int n = numConstraintRows;
154 {
155 BT_PROFILE("init b (rhs)");
156 m_b.resize(numConstraintRows);
157 m_bSplit.resize(numConstraintRows);
158 m_b.setZero();
159 m_bSplit.setZero();
160 for (int i = 0; i < numConstraintRows; i++)
161 {
162 btScalar jacDiag = m_allConstraintPtrArray[i]->m_jacDiagABInv;
163 if (!btFuzzyZero(jacDiag))
164 {
166 btScalar rhsPenetration = m_allConstraintPtrArray[i]->m_rhsPenetration;
167 m_b[i] = rhs / jacDiag;
169 }
170 }
171 }
172
173 // btScalar* w = 0;
174 // int nub = 0;
175
176 m_lo.resize(numConstraintRows);
177 m_hi.resize(numConstraintRows);
178
179 {
180 BT_PROFILE("init lo/ho");
181
182 for (int i = 0; i < numConstraintRows; i++)
183 {
184 if (0) //m_limitDependencies[i]>=0)
185 {
186 m_lo[i] = -BT_INFINITY;
187 m_hi[i] = BT_INFINITY;
188 }
189 else
190 {
191 m_lo[i] = m_allConstraintPtrArray[i]->m_lowerLimit;
192 m_hi[i] = m_allConstraintPtrArray[i]->m_upperLimit;
193 }
194 }
195 }
196
197 //
199
200 int numBodies = m_tmpSolverBodyPool.size();
202 {
203 BT_PROFILE("bodyJointNodeArray.resize");
204 bodyJointNodeArray.resize(numBodies, -1);
205 }
207 {
208 BT_PROFILE("jointNodeArray.reserve");
210 }
211
213 {
214 BT_PROFILE("J3.resize");
215 J3.resize(2 * m, 8);
216 }
218 {
219 BT_PROFILE("JinvM3.resize/setZero");
220
221 JinvM3.resize(2 * m, 8);
222 JinvM3.setZero();
223 J3.setZero();
224 }
225 int cur = 0;
226 int rowOffset = 0;
228 {
229 BT_PROFILE("ofs resize");
230 ofs.resize(0);
231 ofs.resizeNoInitialize(m_allConstraintPtrArray.size());
232 }
233 {
234 BT_PROFILE("Compute J and JinvM");
235 int c = 0;
236
237 int numRows = 0;
238
239 for (int i = 0; i < m_allConstraintPtrArray.size(); i += numRows, c++)
240 {
241 ofs[c] = rowOffset;
242 int sbA = m_allConstraintPtrArray[i]->m_solverBodyIdA;
243 int sbB = m_allConstraintPtrArray[i]->m_solverBodyIdB;
244 btRigidBody* orgBodyA = m_tmpSolverBodyPool[sbA].m_originalBody;
245 btRigidBody* orgBodyB = m_tmpSolverBodyPool[sbB].m_originalBody;
246
248 if (orgBodyA)
249 {
250 {
251 int slotA = -1;
252 //find free jointNode slot for sbA
254 jointNodeArray.expand(); //NonInitializing();
257 jointNodeArray[slotA].nextJointNodeIndex = prevSlot;
258 jointNodeArray[slotA].jointIndex = c;
259 jointNodeArray[slotA].constraintRowIndex = i;
260 jointNodeArray[slotA].otherBodyIndex = orgBodyB ? sbB : -1;
261 }
262 for (int row = 0; row < numRows; row++, cur++)
263 {
264 btVector3 normalInvMass = m_allConstraintPtrArray[i + row]->m_contactNormal1 * orgBodyA->getInvMass();
265 btVector3 relPosCrossNormalInvInertia = m_allConstraintPtrArray[i + row]->m_relpos1CrossNormal * orgBodyA->getInvInertiaTensorWorld();
266
267 for (int r = 0; r < 3; r++)
268 {
269 J3.setElem(cur, r, m_allConstraintPtrArray[i + row]->m_contactNormal1[r]);
270 J3.setElem(cur, r + 4, m_allConstraintPtrArray[i + row]->m_relpos1CrossNormal[r]);
271 JinvM3.setElem(cur, r, normalInvMass[r]);
272 JinvM3.setElem(cur, r + 4, relPosCrossNormalInvInertia[r]);
273 }
274 J3.setElem(cur, 3, 0);
275 JinvM3.setElem(cur, 3, 0);
276 J3.setElem(cur, 7, 0);
277 JinvM3.setElem(cur, 7, 0);
278 }
279 }
280 else
281 {
282 cur += numRows;
283 }
284 if (orgBodyB)
285 {
286 {
287 int slotB = -1;
288 //find free jointNode slot for sbA
290 jointNodeArray.expand(); //NonInitializing();
293 jointNodeArray[slotB].nextJointNodeIndex = prevSlot;
294 jointNodeArray[slotB].jointIndex = c;
295 jointNodeArray[slotB].otherBodyIndex = orgBodyA ? sbA : -1;
296 jointNodeArray[slotB].constraintRowIndex = i;
297 }
298
299 for (int row = 0; row < numRows; row++, cur++)
300 {
301 btVector3 normalInvMassB = m_allConstraintPtrArray[i + row]->m_contactNormal2 * orgBodyB->getInvMass();
302 btVector3 relPosInvInertiaB = m_allConstraintPtrArray[i + row]->m_relpos2CrossNormal * orgBodyB->getInvInertiaTensorWorld();
303
304 for (int r = 0; r < 3; r++)
305 {
306 J3.setElem(cur, r, m_allConstraintPtrArray[i + row]->m_contactNormal2[r]);
307 J3.setElem(cur, r + 4, m_allConstraintPtrArray[i + row]->m_relpos2CrossNormal[r]);
308 JinvM3.setElem(cur, r, normalInvMassB[r]);
309 JinvM3.setElem(cur, r + 4, relPosInvInertiaB[r]);
310 }
311 J3.setElem(cur, 3, 0);
312 JinvM3.setElem(cur, 3, 0);
313 J3.setElem(cur, 7, 0);
314 JinvM3.setElem(cur, 7, 0);
315 }
316 }
317 else
318 {
319 cur += numRows;
320 }
322 }
323 }
324
325 //compute JinvM = J*invM.
326 const btScalar* JinvM = JinvM3.getBufferPointer();
327
328 const btScalar* Jptr = J3.getBufferPointer();
329 {
330 BT_PROFILE("m_A.resize");
331 m_A.resize(n, n);
332 }
333
334 {
335 BT_PROFILE("m_A.setZero");
336 m_A.setZero();
337 }
338 int c = 0;
339 {
340 int numRows = 0;
341 BT_PROFILE("Compute A");
342 for (int i = 0; i < m_allConstraintPtrArray.size(); i += numRows, c++)
343 {
344 int row__ = ofs[c];
345 int sbA = m_allConstraintPtrArray[i]->m_solverBodyIdA;
346 int sbB = m_allConstraintPtrArray[i]->m_solverBodyIdB;
347 // btRigidBody* orgBodyA = m_tmpSolverBodyPool[sbA].m_originalBody;
348 // btRigidBody* orgBodyB = m_tmpSolverBodyPool[sbB].m_originalBody;
349
351
352 const btScalar* JinvMrow = JinvM + 2 * 8 * (size_t)row__;
353
354 {
356 while (startJointNodeA >= 0)
357 {
358 int j0 = jointNodeArray[startJointNodeA].jointIndex;
359 int cr0 = jointNodeArray[startJointNodeA].constraintRowIndex;
360 if (j0 < c)
361 {
363 size_t ofsother = (m_allConstraintPtrArray[cr0]->m_solverBodyIdB == sbA) ? 8 * numRowsOther : 0;
364 //printf("%d joint i %d and j0: %d: ",count++,i,j0);
365 m_A.multiplyAdd2_p8r(JinvMrow,
366 Jptr + 2 * 8 * (size_t)ofs[j0] + ofsother, numRows, numRowsOther, row__, ofs[j0]);
367 }
368 startJointNodeA = jointNodeArray[startJointNodeA].nextJointNodeIndex;
369 }
370 }
371
372 {
374 while (startJointNodeB >= 0)
375 {
376 int j1 = jointNodeArray[startJointNodeB].jointIndex;
377 int cj1 = jointNodeArray[startJointNodeB].constraintRowIndex;
378
379 if (j1 < c)
380 {
382 size_t ofsother = (m_allConstraintPtrArray[cj1]->m_solverBodyIdB == sbB) ? 8 * numRowsOther : 0;
383 m_A.multiplyAdd2_p8r(JinvMrow + 8 * (size_t)numRows,
384 Jptr + 2 * 8 * (size_t)ofs[j1] + ofsother, numRows, numRowsOther, row__, ofs[j1]);
385 }
386 startJointNodeB = jointNodeArray[startJointNodeB].nextJointNodeIndex;
387 }
388 }
389 }
390
391 {
392 BT_PROFILE("compute diagonal");
393 // compute diagonal blocks of m_A
394
395 int row__ = 0;
397
398 int jj = 0;
399 for (; row__ < numJointRows;)
400 {
401 //int sbA = m_allConstraintPtrArray[row__]->m_solverBodyIdA;
402 int sbB = m_allConstraintPtrArray[row__]->m_solverBodyIdB;
403 // btRigidBody* orgBodyA = m_tmpSolverBodyPool[sbA].m_originalBody;
404 btRigidBody* orgBodyB = m_tmpSolverBodyPool[sbB].m_originalBody;
405
406 const unsigned int infom = row__ < m_tmpSolverNonContactConstraintPool.size() ? m_tmpConstraintSizesPool[jj].m_numConstraintRows : numContactRows;
407
408 const btScalar* JinvMrow = JinvM + 2 * 8 * (size_t)row__;
409 const btScalar* Jrow = Jptr + 2 * 8 * (size_t)row__;
410 m_A.multiply2_p8r(JinvMrow, Jrow, infom, infom, row__, row__);
411 if (orgBodyB)
412 {
413 m_A.multiplyAdd2_p8r(JinvMrow + 8 * (size_t)infom, Jrow + 8 * (size_t)infom, infom, infom, row__, row__);
414 }
415 row__ += infom;
416 jj++;
417 }
418 }
419 }
420
421 if (1)
422 {
423 // add cfm to the diagonal of m_A
424 for (int i = 0; i < m_A.rows(); ++i)
425 {
426 m_A.setElem(i, i, m_A(i, i) + infoGlobal.m_globalCfm / infoGlobal.m_timeStep);
427 }
428 }
429
431 {
432 BT_PROFILE("fill the upper triangle ");
433 m_A.copyLowerToUpperTriangle();
434 }
435
436 {
437 BT_PROFILE("resize/init x");
438 m_x.resize(numConstraintRows);
439 m_xSplit.resize(numConstraintRows);
440
441 if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING)
442 {
443 for (int i = 0; i < m_allConstraintPtrArray.size(); i++)
444 {
446 m_x[i] = c.m_appliedImpulse;
448 }
449 }
450 else
451 {
452 m_x.setZero();
453 m_xSplit.setZero();
454 }
455 }
456}
457
459{
460 int numBodies = this->m_tmpSolverBodyPool.size();
461 int numConstraintRows = m_allConstraintPtrArray.size();
462
463 m_b.resize(numConstraintRows);
464 if (infoGlobal.m_splitImpulse)
465 m_bSplit.resize(numConstraintRows);
466
467 m_bSplit.setZero();
468 m_b.setZero();
469
470 for (int i = 0; i < numConstraintRows; i++)
471 {
472 if (m_allConstraintPtrArray[i]->m_jacDiagABInv)
473 {
474 m_b[i] = m_allConstraintPtrArray[i]->m_rhs / m_allConstraintPtrArray[i]->m_jacDiagABInv;
475 if (infoGlobal.m_splitImpulse)
476 m_bSplit[i] = m_allConstraintPtrArray[i]->m_rhsPenetration / m_allConstraintPtrArray[i]->m_jacDiagABInv;
477 }
478 }
479
481 Minv.resize(6 * numBodies, 6 * numBodies);
482 Minv.setZero();
483 for (int i = 0; i < numBodies; i++)
484 {
486 const btVector3& invMass = rb.m_invMass;
487 setElem(Minv, i * 6 + 0, i * 6 + 0, invMass[0]);
488 setElem(Minv, i * 6 + 1, i * 6 + 1, invMass[1]);
489 setElem(Minv, i * 6 + 2, i * 6 + 2, invMass[2]);
490 btRigidBody* orgBody = m_tmpSolverBodyPool[i].m_originalBody;
491
492 for (int r = 0; r < 3; r++)
493 for (int c = 0; c < 3; c++)
494 setElem(Minv, i * 6 + 3 + r, i * 6 + 3 + c, orgBody ? orgBody->getInvInertiaTensorWorld()[r][c] : 0);
495 }
496
498 J.resize(numConstraintRows, 6 * numBodies);
499 J.setZero();
500
501 m_lo.resize(numConstraintRows);
502 m_hi.resize(numConstraintRows);
503
504 for (int i = 0; i < numConstraintRows; i++)
505 {
506 m_lo[i] = m_allConstraintPtrArray[i]->m_lowerLimit;
507 m_hi[i] = m_allConstraintPtrArray[i]->m_upperLimit;
508
509 int bodyIndex0 = m_allConstraintPtrArray[i]->m_solverBodyIdA;
510 int bodyIndex1 = m_allConstraintPtrArray[i]->m_solverBodyIdB;
511 if (m_tmpSolverBodyPool[bodyIndex0].m_originalBody)
512 {
513 setElem(J, i, 6 * bodyIndex0 + 0, m_allConstraintPtrArray[i]->m_contactNormal1[0]);
514 setElem(J, i, 6 * bodyIndex0 + 1, m_allConstraintPtrArray[i]->m_contactNormal1[1]);
515 setElem(J, i, 6 * bodyIndex0 + 2, m_allConstraintPtrArray[i]->m_contactNormal1[2]);
516 setElem(J, i, 6 * bodyIndex0 + 3, m_allConstraintPtrArray[i]->m_relpos1CrossNormal[0]);
517 setElem(J, i, 6 * bodyIndex0 + 4, m_allConstraintPtrArray[i]->m_relpos1CrossNormal[1]);
518 setElem(J, i, 6 * bodyIndex0 + 5, m_allConstraintPtrArray[i]->m_relpos1CrossNormal[2]);
519 }
520 if (m_tmpSolverBodyPool[bodyIndex1].m_originalBody)
521 {
522 setElem(J, i, 6 * bodyIndex1 + 0, m_allConstraintPtrArray[i]->m_contactNormal2[0]);
523 setElem(J, i, 6 * bodyIndex1 + 1, m_allConstraintPtrArray[i]->m_contactNormal2[1]);
524 setElem(J, i, 6 * bodyIndex1 + 2, m_allConstraintPtrArray[i]->m_contactNormal2[2]);
525 setElem(J, i, 6 * bodyIndex1 + 3, m_allConstraintPtrArray[i]->m_relpos2CrossNormal[0]);
526 setElem(J, i, 6 * bodyIndex1 + 4, m_allConstraintPtrArray[i]->m_relpos2CrossNormal[1]);
527 setElem(J, i, 6 * bodyIndex1 + 5, m_allConstraintPtrArray[i]->m_relpos2CrossNormal[2]);
528 }
529 }
530
532 J_transpose = J.transpose();
533
535 //Minv.printMatrix("Minv=");
536 {
537 {
538 BT_PROFILE("J*Minv");
539 tmp = J * Minv;
540 }
541 {
542 BT_PROFILE("J*tmp");
543 m_A = tmp * J_transpose;
544 }
545 }
546 //J.printMatrix("J");
547 if (1)
548 {
549 // add cfm to the diagonal of m_A
550 for (int i = 0; i < m_A.rows(); ++i)
551 {
552 m_A.setElem(i, i, m_A(i, i) + infoGlobal.m_globalCfm / infoGlobal.m_timeStep);
553 }
554 }
555
556 m_x.resize(numConstraintRows);
557 if (infoGlobal.m_splitImpulse)
558 m_xSplit.resize(numConstraintRows);
559 // m_x.setZero();
560
561 for (int i = 0; i < m_allConstraintPtrArray.size(); i++)
562 {
564 m_x[i] = c.m_appliedImpulse;
565 if (infoGlobal.m_splitImpulse)
567 }
568}
569
571{
572 bool result = true;
573 {
574 BT_PROFILE("solveMLCP");
575 // printf("m_A(%d,%d)\n", m_A.rows(),m_A.cols());
577 }
578
579 //check if solution is valid, and otherwise fallback to btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyIterations
580 if (result)
581 {
582 BT_PROFILE("process MLCP results");
583 for (int i = 0; i < m_allConstraintPtrArray.size(); i++)
584 {
585 {
587 int sbA = c.m_solverBodyIdA;
588 int sbB = c.m_solverBodyIdB;
589 //btRigidBody* orgBodyA = m_tmpSolverBodyPool[sbA].m_originalBody;
590 // btRigidBody* orgBodyB = m_tmpSolverBodyPool[sbB].m_originalBody;
591
594
595 {
597 c.m_appliedImpulse = m_x[i];
598 solverBodyA.internalApplyImpulse(c.m_contactNormal1 * solverBodyA.internalGetInvMass(), c.m_angularComponentA, deltaImpulse);
599 solverBodyB.internalApplyImpulse(c.m_contactNormal2 * solverBodyB.internalGetInvMass(), c.m_angularComponentB, deltaImpulse);
600 }
601
602 if (infoGlobal.m_splitImpulse)
603 {
605 solverBodyA.internalApplyPushImpulse(c.m_contactNormal1 * solverBodyA.internalGetInvMass(), c.m_angularComponentA, deltaImpulse);
606 solverBodyB.internalApplyPushImpulse(c.m_contactNormal2 * solverBodyB.internalGetInvMass(), c.m_angularComponentB, deltaImpulse);
608 }
609 }
610 }
611 }
612 else
613 {
614 // printf("m_fallback = %d\n",m_fallback);
615 m_fallback++;
617 }
618
619 return 0.f;
620}
@ SOLVER_USE_WARMSTARTING
bool interleaveContactAndFriction
bool gUseMatrixMultiply
#define btMatrixXu
Definition btMatrixX.h:529
void setElem(btMatrixXd &mat, int row, int col, double val)
Definition btMatrixX.h:514
const T & btMax(const T &a, const T &b)
Definition btMinMax.h:27
#define BT_PROFILE(name)
float btScalar
The btScalar type abstracts floating point numbers, to easily switch between double and single floati...
Definition btScalar.h:314
#define BT_INFINITY
Definition btScalar.h:405
bool btFuzzyZero(btScalar x)
Definition btScalar.h:572
#define btAssert(x)
Definition btScalar.h:153
The btAlignedObjectArray template class uses a subset of the stl::vector interface for its methods It...
int size() const
return the number of elements in the array
void resize(int newsize, const T &fillData=T())
void push_back(const T &_Val)
btCollisionObject can be used to manage collision detection objects.
The btIDebugDraw interface class allows hooking up a debug renderer to visually debug simulations.
original version written by Erwin Coumans, October 2013
virtual bool solveMLCP(const btMatrixXu &A, const btVectorXu &b, btVectorXu &x, const btVectorXu &lo, const btVectorXu &hi, const btAlignedObjectArray< int > &limitDependency, int numIterations, bool useSparsity=true)=0
btMatrixXu m_scratchTmp
btVectorXu m_b
btMatrixXu m_scratchJTranspose
virtual ~btMLCPSolver()
btMatrixXu m_scratchMInv
btAlignedObjectArray< int > m_limitDependencies
btMatrixXu m_scratchJ
btVectorXu m_x
virtual void createMLCP(const btContactSolverInfo &infoGlobal)
btMatrixXu m_scratchJ3
The following scratch variables are not stateful – contents are cleared prior to each use.
btVectorXu m_xSplit
virtual bool solveMLCP(const btContactSolverInfo &infoGlobal)
btVectorXu m_bSplit
when using 'split impulse' we solve two separate (M)LCPs
virtual btScalar solveGroupCacheFriendlySetup(btCollisionObject **bodies, int numBodies, btPersistentManifold **manifoldPtr, int numManifolds, btTypedConstraint **constraints, int numConstraints, const btContactSolverInfo &infoGlobal, btIDebugDraw *debugDrawer)
btVectorXu m_hi
btMatrixXu m_A
btAlignedObjectArray< int > m_scratchOfs
btMatrixXu m_scratchJInvM3
virtual void createMLCPFast(const btContactSolverInfo &infoGlobal)
virtual btScalar solveGroupCacheFriendlyIterations(btCollisionObject **bodies, int numBodies, btPersistentManifold **manifoldPtr, int numManifolds, btTypedConstraint **constraints, int numConstraints, const btContactSolverInfo &infoGlobal, btIDebugDraw *debugDrawer)
btMLCPSolver(btMLCPSolverInterface *solver)
original version written by Erwin Coumans, October 2013
btAlignedObjectArray< btSolverConstraint * > m_allConstraintPtrArray
btMLCPSolverInterface * m_solver
btVectorXu m_lo
btPersistentManifold is a contact point cache, it stays persistent as long as objects are overlapping...
The btRigidBody is the main class for rigid body objects.
Definition btRigidBody.h:60
virtual btScalar solveGroupCacheFriendlyIterations(btCollisionObject **bodies, int numBodies, btPersistentManifold **manifoldPtr, int numManifolds, btTypedConstraint **constraints, int numConstraints, const btContactSolverInfo &infoGlobal, btIDebugDraw *debugDrawer)
btAlignedObjectArray< btSolverBody > m_tmpSolverBodyPool
btAlignedObjectArray< btTypedConstraint::btConstraintInfo1 > m_tmpConstraintSizesPool
virtual btScalar solveGroupCacheFriendlySetup(btCollisionObject **bodies, int numBodies, btPersistentManifold **manifoldPtr, int numManifolds, btTypedConstraint **constraints, int numConstraints, const btContactSolverInfo &infoGlobal, btIDebugDraw *debugDrawer)
TypedConstraint is the baseclass for Bullet constraints and vehicles.
btVector3 can be used to represent 3D points and vectors.
Definition btVector3.h:82
The btSolverBody is an internal datastructure for the constraint solver. Only necessary data is packe...
1D constraint along a normal axis between bodyA and bodyB. It can be combined to solve contact and fr...
btSimdScalar m_appliedImpulse
btSimdScalar m_appliedPushImpulse