2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2003-2006 Erwin Coumans https://bulletphysics.org
5 This software is provided 'as-is', without any express or implied warranty.
6 In no event will the authors be held liable for any damages arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it freely,
9 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.
17 2014 May: btGeneric6DofSpring2Constraint is created from the original (2.82.2712) btGeneric6DofConstraint by Gabor Puhr and Tamas Umenhoffer
19 - Much more accurate and stable in a lot of situation. (Especially when a sleeping chain of RBs connected with 6dof2 is pulled)
20 - Stable and accurate spring with minimal energy loss that works with all of the solvers. (latter is not true for the original 6dof spring)
21 - Servo motor functionality
22 - Much more accurate bouncing. 0 really means zero bouncing (not true for the original 6odf) and there is only a minimal energy loss when the value is 1 (because of the solvers' precision)
23 - Rotation order for the Euler system can be set. (One axis' freedom is still limited to pi/2)
26 - It is slower than the original 6dof. There is no exact ratio, but half speed is a good estimation. (with PGS)
27 - At bouncing the correct velocity is calculated, but not the correct position. (it is because of the solver can correct position or velocity, but not both.)
30 /// 2009 March: btGeneric6DofConstraint refactored by Roman Ponomarev
31 /// Added support for generic constraint solver through getInfo1/getInfo2 methods
35 btGeneric6DofConstraint Refactored by Francisco Le?n
36 email: projectileman@yahoo.com
40 #include "btGeneric6DofSpring2Constraint.h"
41 #include "BulletDynamics/Dynamics/btRigidBody.h"
42 #include "LinearMath/btTransformUtil.h"
46 btGeneric6DofSpring2Constraint::btGeneric6DofSpring2Constraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB, RotateOrder rotOrder)
47 : btTypedConstraint(D6_SPRING_2_CONSTRAINT_TYPE, rbA, rbB), m_frameInA(frameInA), m_frameInB(frameInB), m_rotateOrder(rotOrder), m_flags(0)
49 calculateTransforms();
52 btGeneric6DofSpring2Constraint::btGeneric6DofSpring2Constraint(btRigidBody& rbB, const btTransform& frameInB, RotateOrder rotOrder)
53 : btTypedConstraint(D6_SPRING_2_CONSTRAINT_TYPE, getFixedBody(), rbB), m_frameInB(frameInB), m_rotateOrder(rotOrder), m_flags(0)
55 ///not providing rigidbody A means implicitly using worldspace for body A
56 m_frameInA = rbB.getCenterOfMassTransform() * m_frameInB;
57 calculateTransforms();
60 btScalar btGeneric6DofSpring2Constraint::btGetMatrixElem(const btMatrix3x3& mat, int index)
67 // MatrixToEulerXYZ from http://www.geometrictools.com/LibFoundation/Mathematics/Wm4Matrix3.inl.html
69 bool btGeneric6DofSpring2Constraint::matrixToEulerXYZ(const btMatrix3x3& mat, btVector3& xyz)
71 // rot = cy*cz -cy*sz sy
72 // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx
73 // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy
75 btScalar fi = btGetMatrixElem(mat, 2);
76 if (fi < btScalar(1.0f))
78 if (fi > btScalar(-1.0f))
80 xyz[0] = btAtan2(-btGetMatrixElem(mat, 5), btGetMatrixElem(mat, 8));
81 xyz[1] = btAsin(btGetMatrixElem(mat, 2));
82 xyz[2] = btAtan2(-btGetMatrixElem(mat, 1), btGetMatrixElem(mat, 0));
87 // WARNING. Not unique. XA - ZA = -atan2(r10,r11)
88 xyz[0] = -btAtan2(btGetMatrixElem(mat, 3), btGetMatrixElem(mat, 4));
89 xyz[1] = -SIMD_HALF_PI;
90 xyz[2] = btScalar(0.0);
96 // WARNING. Not unique. XAngle + ZAngle = atan2(r10,r11)
97 xyz[0] = btAtan2(btGetMatrixElem(mat, 3), btGetMatrixElem(mat, 4));
98 xyz[1] = SIMD_HALF_PI;
104 bool btGeneric6DofSpring2Constraint::matrixToEulerXZY(const btMatrix3x3& mat, btVector3& xyz)
106 // rot = cy*cz -sz sy*cz
107 // cy*cx*sz+sx*sy cx*cz sy*cx*sz-cy*sx
108 // cy*sx*sz-cx*sy sx*cz sy*sx*sz+cx*cy
110 btScalar fi = btGetMatrixElem(mat, 1);
111 if (fi < btScalar(1.0f))
113 if (fi > btScalar(-1.0f))
115 xyz[0] = btAtan2(btGetMatrixElem(mat, 7), btGetMatrixElem(mat, 4));
116 xyz[1] = btAtan2(btGetMatrixElem(mat, 2), btGetMatrixElem(mat, 0));
117 xyz[2] = btAsin(-btGetMatrixElem(mat, 1));
122 xyz[0] = -btAtan2(-btGetMatrixElem(mat, 6), btGetMatrixElem(mat, 8));
123 xyz[1] = btScalar(0.0);
124 xyz[2] = SIMD_HALF_PI;
130 xyz[0] = btAtan2(-btGetMatrixElem(mat, 6), btGetMatrixElem(mat, 8));
132 xyz[2] = -SIMD_HALF_PI;
137 bool btGeneric6DofSpring2Constraint::matrixToEulerYXZ(const btMatrix3x3& mat, btVector3& xyz)
139 // rot = cy*cz+sy*sx*sz cz*sy*sx-cy*sz cx*sy
141 // cy*sx*sz-cz*sy sy*sz+cy*cz*sx cy*cx
143 btScalar fi = btGetMatrixElem(mat, 5);
144 if (fi < btScalar(1.0f))
146 if (fi > btScalar(-1.0f))
148 xyz[0] = btAsin(-btGetMatrixElem(mat, 5));
149 xyz[1] = btAtan2(btGetMatrixElem(mat, 2), btGetMatrixElem(mat, 8));
150 xyz[2] = btAtan2(btGetMatrixElem(mat, 3), btGetMatrixElem(mat, 4));
155 xyz[0] = SIMD_HALF_PI;
156 xyz[1] = -btAtan2(-btGetMatrixElem(mat, 1), btGetMatrixElem(mat, 0));
157 xyz[2] = btScalar(0.0);
163 xyz[0] = -SIMD_HALF_PI;
164 xyz[1] = btAtan2(-btGetMatrixElem(mat, 1), btGetMatrixElem(mat, 0));
170 bool btGeneric6DofSpring2Constraint::matrixToEulerYZX(const btMatrix3x3& mat, btVector3& xyz)
172 // rot = cy*cz sy*sx-cy*cx*sz cx*sy+cy*sz*sx
174 // -cz*sy cy*sx+cx*sy*sz cy*cx-sy*sz*sx
176 btScalar fi = btGetMatrixElem(mat, 3);
177 if (fi < btScalar(1.0f))
179 if (fi > btScalar(-1.0f))
181 xyz[0] = btAtan2(-btGetMatrixElem(mat, 5), btGetMatrixElem(mat, 4));
182 xyz[1] = btAtan2(-btGetMatrixElem(mat, 6), btGetMatrixElem(mat, 0));
183 xyz[2] = btAsin(btGetMatrixElem(mat, 3));
188 xyz[0] = btScalar(0.0);
189 xyz[1] = -btAtan2(btGetMatrixElem(mat, 7), btGetMatrixElem(mat, 8));
190 xyz[2] = -SIMD_HALF_PI;
196 xyz[0] = btScalar(0.0);
197 xyz[1] = btAtan2(btGetMatrixElem(mat, 7), btGetMatrixElem(mat, 8));
198 xyz[2] = SIMD_HALF_PI;
203 bool btGeneric6DofSpring2Constraint::matrixToEulerZXY(const btMatrix3x3& mat, btVector3& xyz)
205 // rot = cz*cy-sz*sx*sy -cx*sz cz*sy+cy*sz*sx
206 // cy*sz+cz*sx*sy cz*cx sz*sy-cz*xy*sx
209 btScalar fi = btGetMatrixElem(mat, 7);
210 if (fi < btScalar(1.0f))
212 if (fi > btScalar(-1.0f))
214 xyz[0] = btAsin(btGetMatrixElem(mat, 7));
215 xyz[1] = btAtan2(-btGetMatrixElem(mat, 6), btGetMatrixElem(mat, 8));
216 xyz[2] = btAtan2(-btGetMatrixElem(mat, 1), btGetMatrixElem(mat, 4));
221 xyz[0] = -SIMD_HALF_PI;
222 xyz[1] = btScalar(0.0);
223 xyz[2] = -btAtan2(btGetMatrixElem(mat, 2), btGetMatrixElem(mat, 0));
229 xyz[0] = SIMD_HALF_PI;
230 xyz[1] = btScalar(0.0);
231 xyz[2] = btAtan2(btGetMatrixElem(mat, 2), btGetMatrixElem(mat, 0));
236 bool btGeneric6DofSpring2Constraint::matrixToEulerZYX(const btMatrix3x3& mat, btVector3& xyz)
238 // rot = cz*cy cz*sy*sx-cx*sz sz*sx+cz*cx*sy
239 // cy*sz cz*cx+sz*sy*sx cx*sz*sy-cz*sx
242 btScalar fi = btGetMatrixElem(mat, 6);
243 if (fi < btScalar(1.0f))
245 if (fi > btScalar(-1.0f))
247 xyz[0] = btAtan2(btGetMatrixElem(mat, 7), btGetMatrixElem(mat, 8));
248 xyz[1] = btAsin(-btGetMatrixElem(mat, 6));
249 xyz[2] = btAtan2(btGetMatrixElem(mat, 3), btGetMatrixElem(mat, 0));
254 xyz[0] = btScalar(0.0);
255 xyz[1] = SIMD_HALF_PI;
256 xyz[2] = -btAtan2(btGetMatrixElem(mat, 1), btGetMatrixElem(mat, 2));
262 xyz[0] = btScalar(0.0);
263 xyz[1] = -SIMD_HALF_PI;
264 xyz[2] = btAtan2(-btGetMatrixElem(mat, 1), -btGetMatrixElem(mat, 2));
269 void btGeneric6DofSpring2Constraint::calculateAngleInfo()
271 btMatrix3x3 relative_frame = m_calculatedTransformA.getBasis().inverse() * m_calculatedTransformB.getBasis();
272 switch (m_rotateOrder)
275 matrixToEulerXYZ(relative_frame, m_calculatedAxisAngleDiff);
278 matrixToEulerXZY(relative_frame, m_calculatedAxisAngleDiff);
281 matrixToEulerYXZ(relative_frame, m_calculatedAxisAngleDiff);
284 matrixToEulerYZX(relative_frame, m_calculatedAxisAngleDiff);
287 matrixToEulerZXY(relative_frame, m_calculatedAxisAngleDiff);
290 matrixToEulerZYX(relative_frame, m_calculatedAxisAngleDiff);
295 // in euler angle mode we do not actually constrain the angular velocity
296 // along the axes axis[0] and axis[2] (although we do use axis[1]) :
298 // to get constrain w2-w1 along ...not
299 // ------ --------------------- ------
300 // d(angle[0])/dt = 0 ax[1] x ax[2] ax[0]
301 // d(angle[1])/dt = 0 ax[1]
302 // d(angle[2])/dt = 0 ax[0] x ax[1] ax[2]
304 // constraining w2-w1 along an axis 'a' means that a'*(w2-w1)=0.
305 // to prove the result for angle[0], write the expression for angle[0] from
306 // GetInfo1 then take the derivative. to prove this for angle[2] it is
307 // easier to take the euler rate expression for d(angle[2])/dt with respect
308 // to the components of w and set that to 0.
309 switch (m_rotateOrder)
313 //Is this the "line of nodes" calculation choosing planes YZ (B coordinate system) and xy (A coordinate system)? (http://en.wikipedia.org/wiki/Euler_angles)
314 //The two planes are non-homologous, so this is a Tait Bryan angle formalism and not a proper Euler
315 //Extrinsic rotations are equal to the reversed order intrinsic rotations so the above xyz extrinsic rotations (axes are fixed) are the same as the zy'x" intrinsic rotations (axes are refreshed after each rotation)
316 //that is why xy and YZ planes are chosen (this will describe a zy'x" intrinsic rotation) (see the figure on the left at http://en.wikipedia.org/wiki/Euler_angles under Tait Bryan angles)
317 // x' = Nperp = N.cross(axis2)
318 // y' = N = axis2.cross(axis0)
325 //first rotate around z
326 //second rotate around y'= z.cross(X)
327 //third rotate around x" = X
328 //Original XYZ extrinsic rotation order.
329 //Planes: xy and YZ normals: z, X. Plane intersection (N) is z.cross(X)
330 btVector3 axis0 = m_calculatedTransformB.getBasis().getColumn(0);
331 btVector3 axis2 = m_calculatedTransformA.getBasis().getColumn(2);
332 m_calculatedAxis[1] = axis2.cross(axis0);
333 m_calculatedAxis[0] = m_calculatedAxis[1].cross(axis2);
334 m_calculatedAxis[2] = axis0.cross(m_calculatedAxis[1]);
339 //planes: xz,ZY normals: y, X
340 //first rotate around y
341 //second rotate around z'= y.cross(X)
342 //third rotate around x" = X
343 btVector3 axis0 = m_calculatedTransformB.getBasis().getColumn(0);
344 btVector3 axis1 = m_calculatedTransformA.getBasis().getColumn(1);
345 m_calculatedAxis[2] = axis0.cross(axis1);
346 m_calculatedAxis[0] = axis1.cross(m_calculatedAxis[2]);
347 m_calculatedAxis[1] = m_calculatedAxis[2].cross(axis0);
352 //planes: yx,XZ normals: z, Y
353 //first rotate around z
354 //second rotate around x'= z.cross(Y)
355 //third rotate around y" = Y
356 btVector3 axis1 = m_calculatedTransformB.getBasis().getColumn(1);
357 btVector3 axis2 = m_calculatedTransformA.getBasis().getColumn(2);
358 m_calculatedAxis[0] = axis1.cross(axis2);
359 m_calculatedAxis[1] = axis2.cross(m_calculatedAxis[0]);
360 m_calculatedAxis[2] = m_calculatedAxis[0].cross(axis1);
365 //planes: yz,ZX normals: x, Y
366 //first rotate around x
367 //second rotate around z'= x.cross(Y)
368 //third rotate around y" = Y
369 btVector3 axis0 = m_calculatedTransformA.getBasis().getColumn(0);
370 btVector3 axis1 = m_calculatedTransformB.getBasis().getColumn(1);
371 m_calculatedAxis[2] = axis0.cross(axis1);
372 m_calculatedAxis[0] = axis1.cross(m_calculatedAxis[2]);
373 m_calculatedAxis[1] = m_calculatedAxis[2].cross(axis0);
378 //planes: zx,XY normals: y, Z
379 //first rotate around y
380 //second rotate around x'= y.cross(Z)
381 //third rotate around z" = Z
382 btVector3 axis1 = m_calculatedTransformA.getBasis().getColumn(1);
383 btVector3 axis2 = m_calculatedTransformB.getBasis().getColumn(2);
384 m_calculatedAxis[0] = axis1.cross(axis2);
385 m_calculatedAxis[1] = axis2.cross(m_calculatedAxis[0]);
386 m_calculatedAxis[2] = m_calculatedAxis[0].cross(axis1);
391 //planes: zy,YX normals: x, Z
392 //first rotate around x
393 //second rotate around y' = x.cross(Z)
394 //third rotate around z" = Z
395 btVector3 axis0 = m_calculatedTransformA.getBasis().getColumn(0);
396 btVector3 axis2 = m_calculatedTransformB.getBasis().getColumn(2);
397 m_calculatedAxis[1] = axis2.cross(axis0);
398 m_calculatedAxis[0] = m_calculatedAxis[1].cross(axis2);
399 m_calculatedAxis[2] = axis0.cross(m_calculatedAxis[1]);
406 m_calculatedAxis[0].normalize();
407 m_calculatedAxis[1].normalize();
408 m_calculatedAxis[2].normalize();
411 void btGeneric6DofSpring2Constraint::calculateTransforms()
413 calculateTransforms(m_rbA.getCenterOfMassTransform(), m_rbB.getCenterOfMassTransform());
416 void btGeneric6DofSpring2Constraint::calculateTransforms(const btTransform& transA, const btTransform& transB)
418 m_calculatedTransformA = transA * m_frameInA;
419 m_calculatedTransformB = transB * m_frameInB;
420 calculateLinearInfo();
421 calculateAngleInfo();
423 btScalar miA = getRigidBodyA().getInvMass();
424 btScalar miB = getRigidBodyB().getInvMass();
425 m_hasStaticBody = (miA < SIMD_EPSILON) || (miB < SIMD_EPSILON);
426 btScalar miS = miA + miB;
427 if (miS > btScalar(0.f))
433 m_factA = btScalar(0.5f);
435 m_factB = btScalar(1.0f) - m_factA;
438 void btGeneric6DofSpring2Constraint::testAngularLimitMotor(int axis_index)
440 btScalar angle = m_calculatedAxisAngleDiff[axis_index];
441 angle = btAdjustAngleToLimits(angle, m_angularLimits[axis_index].m_loLimit, m_angularLimits[axis_index].m_hiLimit);
442 m_angularLimits[axis_index].m_currentPosition = angle;
443 m_angularLimits[axis_index].testLimitValue(angle);
446 void btGeneric6DofSpring2Constraint::getInfo1(btConstraintInfo1* info)
449 calculateTransforms(m_rbA.getCenterOfMassTransform(), m_rbB.getCenterOfMassTransform());
450 info->m_numConstraintRows = 0;
454 for (i = 0; i < 3; i++)
456 if (m_linearLimits.m_currentLimit[i] == 4)
457 info->m_numConstraintRows += 2;
458 else if (m_linearLimits.m_currentLimit[i] != 0)
459 info->m_numConstraintRows += 1;
460 if (m_linearLimits.m_enableMotor[i]) info->m_numConstraintRows += 1;
461 if (m_linearLimits.m_enableSpring[i]) info->m_numConstraintRows += 1;
463 //test angular limits
464 for (i = 0; i < 3; i++)
466 testAngularLimitMotor(i);
467 if (m_angularLimits[i].m_currentLimit == 4)
468 info->m_numConstraintRows += 2;
469 else if (m_angularLimits[i].m_currentLimit != 0)
470 info->m_numConstraintRows += 1;
471 if (m_angularLimits[i].m_enableMotor) info->m_numConstraintRows += 1;
472 if (m_angularLimits[i].m_enableSpring) info->m_numConstraintRows += 1;
476 void btGeneric6DofSpring2Constraint::getInfo2(btConstraintInfo2* info)
478 const btTransform& transA = m_rbA.getCenterOfMassTransform();
479 const btTransform& transB = m_rbB.getCenterOfMassTransform();
480 const btVector3& linVelA = m_rbA.getLinearVelocity();
481 const btVector3& linVelB = m_rbB.getLinearVelocity();
482 const btVector3& angVelA = m_rbA.getAngularVelocity();
483 const btVector3& angVelB = m_rbB.getAngularVelocity();
485 // for stability better to solve angular limits first
486 int row = setAngularLimits(info, 0, transA, transB, linVelA, linVelB, angVelA, angVelB);
487 setLinearLimits(info, row, transA, transB, linVelA, linVelB, angVelA, angVelB);
490 int btGeneric6DofSpring2Constraint::setLinearLimits(btConstraintInfo2* info, int row, const btTransform& transA, const btTransform& transB, const btVector3& linVelA, const btVector3& linVelB, const btVector3& angVelA, const btVector3& angVelB)
492 //solve linear limits
493 btRotationalLimitMotor2 limot;
494 for (int i = 0; i < 3; i++)
496 if (m_linearLimits.m_currentLimit[i] || m_linearLimits.m_enableMotor[i] || m_linearLimits.m_enableSpring[i])
497 { // re-use rotational motor code
498 limot.m_bounce = m_linearLimits.m_bounce[i];
499 limot.m_currentLimit = m_linearLimits.m_currentLimit[i];
500 limot.m_currentPosition = m_linearLimits.m_currentLinearDiff[i];
501 limot.m_currentLimitError = m_linearLimits.m_currentLimitError[i];
502 limot.m_currentLimitErrorHi = m_linearLimits.m_currentLimitErrorHi[i];
503 limot.m_enableMotor = m_linearLimits.m_enableMotor[i];
504 limot.m_servoMotor = m_linearLimits.m_servoMotor[i];
505 limot.m_servoTarget = m_linearLimits.m_servoTarget[i];
506 limot.m_enableSpring = m_linearLimits.m_enableSpring[i];
507 limot.m_springStiffness = m_linearLimits.m_springStiffness[i];
508 limot.m_springStiffnessLimited = m_linearLimits.m_springStiffnessLimited[i];
509 limot.m_springDamping = m_linearLimits.m_springDamping[i];
510 limot.m_springDampingLimited = m_linearLimits.m_springDampingLimited[i];
511 limot.m_equilibriumPoint = m_linearLimits.m_equilibriumPoint[i];
512 limot.m_hiLimit = m_linearLimits.m_upperLimit[i];
513 limot.m_loLimit = m_linearLimits.m_lowerLimit[i];
514 limot.m_maxMotorForce = m_linearLimits.m_maxMotorForce[i];
515 limot.m_targetVelocity = m_linearLimits.m_targetVelocity[i];
516 btVector3 axis = m_calculatedTransformA.getBasis().getColumn(i);
517 int flags = m_flags >> (i * BT_6DOF_FLAGS_AXIS_SHIFT2);
518 limot.m_stopCFM = (flags & BT_6DOF_FLAGS_CFM_STOP2) ? m_linearLimits.m_stopCFM[i] : info->cfm[0];
519 limot.m_stopERP = (flags & BT_6DOF_FLAGS_ERP_STOP2) ? m_linearLimits.m_stopERP[i] : info->erp;
520 limot.m_motorCFM = (flags & BT_6DOF_FLAGS_CFM_MOTO2) ? m_linearLimits.m_motorCFM[i] : info->cfm[0];
521 limot.m_motorERP = (flags & BT_6DOF_FLAGS_ERP_MOTO2) ? m_linearLimits.m_motorERP[i] : info->erp;
523 //rotAllowed is a bit of a magic from the original 6dof. The calculation of it here is something that imitates the original behavior as much as possible.
524 int indx1 = (i + 1) % 3;
525 int indx2 = (i + 2) % 3;
526 int rotAllowed = 1; // rotations around orthos to current axis (it is used only when one of the body is static)
527 #define D6_LIMIT_ERROR_THRESHOLD_FOR_ROTATION 1.0e-3
528 bool indx1Violated = m_angularLimits[indx1].m_currentLimit == 1 ||
529 m_angularLimits[indx1].m_currentLimit == 2 ||
530 (m_angularLimits[indx1].m_currentLimit == 3 && (m_angularLimits[indx1].m_currentLimitError < -D6_LIMIT_ERROR_THRESHOLD_FOR_ROTATION || m_angularLimits[indx1].m_currentLimitError > D6_LIMIT_ERROR_THRESHOLD_FOR_ROTATION)) ||
531 (m_angularLimits[indx1].m_currentLimit == 4 && (m_angularLimits[indx1].m_currentLimitError < -D6_LIMIT_ERROR_THRESHOLD_FOR_ROTATION || m_angularLimits[indx1].m_currentLimitErrorHi > D6_LIMIT_ERROR_THRESHOLD_FOR_ROTATION));
532 bool indx2Violated = m_angularLimits[indx2].m_currentLimit == 1 ||
533 m_angularLimits[indx2].m_currentLimit == 2 ||
534 (m_angularLimits[indx2].m_currentLimit == 3 && (m_angularLimits[indx2].m_currentLimitError < -D6_LIMIT_ERROR_THRESHOLD_FOR_ROTATION || m_angularLimits[indx2].m_currentLimitError > D6_LIMIT_ERROR_THRESHOLD_FOR_ROTATION)) ||
535 (m_angularLimits[indx2].m_currentLimit == 4 && (m_angularLimits[indx2].m_currentLimitError < -D6_LIMIT_ERROR_THRESHOLD_FOR_ROTATION || m_angularLimits[indx2].m_currentLimitErrorHi > D6_LIMIT_ERROR_THRESHOLD_FOR_ROTATION));
536 if (indx1Violated && indx2Violated)
540 row += get_limit_motor_info2(&limot, transA, transB, linVelA, linVelB, angVelA, angVelB, info, row, axis, 0, rotAllowed);
546 int btGeneric6DofSpring2Constraint::setAngularLimits(btConstraintInfo2* info, int row_offset, const btTransform& transA, const btTransform& transB, const btVector3& linVelA, const btVector3& linVelB, const btVector3& angVelA, const btVector3& angVelB)
548 int row = row_offset;
550 //order of rotational constraint rows
551 int cIdx[] = {0, 1, 2};
552 switch (m_rotateOrder)
588 for (int ii = 0; ii < 3; ii++)
591 if (m_angularLimits[i].m_currentLimit || m_angularLimits[i].m_enableMotor || m_angularLimits[i].m_enableSpring)
593 btVector3 axis = getAxis(i);
594 int flags = m_flags >> ((i + 3) * BT_6DOF_FLAGS_AXIS_SHIFT2);
595 if (!(flags & BT_6DOF_FLAGS_CFM_STOP2))
597 m_angularLimits[i].m_stopCFM = info->cfm[0];
599 if (!(flags & BT_6DOF_FLAGS_ERP_STOP2))
601 m_angularLimits[i].m_stopERP = info->erp;
603 if (!(flags & BT_6DOF_FLAGS_CFM_MOTO2))
605 m_angularLimits[i].m_motorCFM = info->cfm[0];
607 if (!(flags & BT_6DOF_FLAGS_ERP_MOTO2))
609 m_angularLimits[i].m_motorERP = info->erp;
611 row += get_limit_motor_info2(&m_angularLimits[i], transA, transB, linVelA, linVelB, angVelA, angVelB, info, row, axis, 1);
618 void btGeneric6DofSpring2Constraint::setFrames(const btTransform& frameA, const btTransform& frameB)
623 calculateTransforms();
626 void btGeneric6DofSpring2Constraint::calculateLinearInfo()
628 m_calculatedLinearDiff = m_calculatedTransformB.getOrigin() - m_calculatedTransformA.getOrigin();
629 m_calculatedLinearDiff = m_calculatedTransformA.getBasis().inverse() * m_calculatedLinearDiff;
630 for (int i = 0; i < 3; i++)
632 m_linearLimits.m_currentLinearDiff[i] = m_calculatedLinearDiff[i];
633 m_linearLimits.testLimitValue(i, m_calculatedLinearDiff[i]);
637 void btGeneric6DofSpring2Constraint::calculateJacobi(btRotationalLimitMotor2* limot, const btTransform& transA, const btTransform& transB, btConstraintInfo2* info, int srow, btVector3& ax1, int rotational, int rotAllowed)
639 btScalar* J1 = rotational ? info->m_J1angularAxis : info->m_J1linearAxis;
640 btScalar* J2 = rotational ? info->m_J2angularAxis : info->m_J2linearAxis;
642 J1[srow + 0] = ax1[0];
643 J1[srow + 1] = ax1[1];
644 J1[srow + 2] = ax1[2];
646 J2[srow + 0] = -ax1[0];
647 J2[srow + 1] = -ax1[1];
648 J2[srow + 2] = -ax1[2];
652 btVector3 tmpA, tmpB, relA, relB;
653 // get vector from bodyB to frameB in WCS
654 relB = m_calculatedTransformB.getOrigin() - transB.getOrigin();
656 relA = m_calculatedTransformA.getOrigin() - transA.getOrigin();
657 tmpA = relA.cross(ax1);
658 tmpB = relB.cross(ax1);
659 if (m_hasStaticBody && (!rotAllowed))
665 for (i = 0; i < 3; i++) info->m_J1angularAxis[srow + i] = tmpA[i];
666 for (i = 0; i < 3; i++) info->m_J2angularAxis[srow + i] = -tmpB[i];
670 int btGeneric6DofSpring2Constraint::get_limit_motor_info2(
671 btRotationalLimitMotor2* limot,
672 const btTransform& transA, const btTransform& transB, const btVector3& linVelA, const btVector3& linVelB, const btVector3& angVelA, const btVector3& angVelB,
673 btConstraintInfo2* info, int row, btVector3& ax1, int rotational, int rotAllowed)
676 int srow = row * info->rowskip;
678 if (limot->m_currentLimit == 4)
680 btScalar vel = rotational ? angVelA.dot(ax1) - angVelB.dot(ax1) : linVelA.dot(ax1) - linVelB.dot(ax1);
682 calculateJacobi(limot, transA, transB, info, srow, ax1, rotational, rotAllowed);
683 info->m_constraintError[srow] = info->fps * limot->m_stopERP * limot->m_currentLimitError * (rotational ? -1 : 1);
686 if (info->m_constraintError[srow] - vel * limot->m_stopERP > 0)
688 btScalar bounceerror = -limot->m_bounce * vel;
689 if (bounceerror > info->m_constraintError[srow]) info->m_constraintError[srow] = bounceerror;
694 if (info->m_constraintError[srow] - vel * limot->m_stopERP < 0)
696 btScalar bounceerror = -limot->m_bounce * vel;
697 if (bounceerror < info->m_constraintError[srow]) info->m_constraintError[srow] = bounceerror;
700 info->m_lowerLimit[srow] = rotational ? 0 : -SIMD_INFINITY;
701 info->m_upperLimit[srow] = rotational ? SIMD_INFINITY : 0;
702 info->cfm[srow] = limot->m_stopCFM;
703 srow += info->rowskip;
706 calculateJacobi(limot, transA, transB, info, srow, ax1, rotational, rotAllowed);
707 info->m_constraintError[srow] = info->fps * limot->m_stopERP * limot->m_currentLimitErrorHi * (rotational ? -1 : 1);
710 if (info->m_constraintError[srow] - vel * limot->m_stopERP < 0)
712 btScalar bounceerror = -limot->m_bounce * vel;
713 if (bounceerror < info->m_constraintError[srow]) info->m_constraintError[srow] = bounceerror;
718 if (info->m_constraintError[srow] - vel * limot->m_stopERP > 0)
720 btScalar bounceerror = -limot->m_bounce * vel;
721 if (bounceerror > info->m_constraintError[srow]) info->m_constraintError[srow] = bounceerror;
724 info->m_lowerLimit[srow] = rotational ? -SIMD_INFINITY : 0;
725 info->m_upperLimit[srow] = rotational ? 0 : SIMD_INFINITY;
726 info->cfm[srow] = limot->m_stopCFM;
727 srow += info->rowskip;
730 else if (limot->m_currentLimit == 3)
732 calculateJacobi(limot, transA, transB, info, srow, ax1, rotational, rotAllowed);
733 info->m_constraintError[srow] = info->fps * limot->m_stopERP * limot->m_currentLimitError * (rotational ? -1 : 1);
734 info->m_lowerLimit[srow] = -SIMD_INFINITY;
735 info->m_upperLimit[srow] = SIMD_INFINITY;
736 info->cfm[srow] = limot->m_stopCFM;
737 srow += info->rowskip;
741 if (limot->m_enableMotor && !limot->m_servoMotor)
743 calculateJacobi(limot, transA, transB, info, srow, ax1, rotational, rotAllowed);
744 btScalar tag_vel = rotational ? limot->m_targetVelocity : -limot->m_targetVelocity;
745 btScalar mot_fact = getMotorFactor(limot->m_currentPosition,
749 info->fps * limot->m_motorERP);
750 info->m_constraintError[srow] = mot_fact * limot->m_targetVelocity;
751 info->m_lowerLimit[srow] = -limot->m_maxMotorForce / info->fps;
752 info->m_upperLimit[srow] = limot->m_maxMotorForce / info->fps;
753 info->cfm[srow] = limot->m_motorCFM;
754 srow += info->rowskip;
758 if (limot->m_enableMotor && limot->m_servoMotor)
760 btScalar error = limot->m_currentPosition - limot->m_servoTarget;
761 btScalar curServoTarget = limot->m_servoTarget;
767 curServoTarget += SIMD_2_PI;
769 if (error < -SIMD_PI)
772 curServoTarget -= SIMD_2_PI;
776 calculateJacobi(limot, transA, transB, info, srow, ax1, rotational, rotAllowed);
777 btScalar targetvelocity = error < 0 ? -limot->m_targetVelocity : limot->m_targetVelocity;
778 btScalar tag_vel = -targetvelocity;
784 if (limot->m_loLimit > limot->m_hiLimit)
786 lowLimit = error > 0 ? curServoTarget : -SIMD_INFINITY;
787 hiLimit = error < 0 ? curServoTarget : SIMD_INFINITY;
791 lowLimit = error > 0 && curServoTarget > limot->m_loLimit ? curServoTarget : limot->m_loLimit;
792 hiLimit = error < 0 && curServoTarget < limot->m_hiLimit ? curServoTarget : limot->m_hiLimit;
794 mot_fact = getMotorFactor(limot->m_currentPosition, lowLimit, hiLimit, tag_vel, info->fps * limot->m_motorERP);
800 info->m_constraintError[srow] = mot_fact * targetvelocity * (rotational ? -1 : 1);
801 info->m_lowerLimit[srow] = -limot->m_maxMotorForce / info->fps;
802 info->m_upperLimit[srow] = limot->m_maxMotorForce / info->fps;
803 info->cfm[srow] = limot->m_motorCFM;
804 srow += info->rowskip;
808 if (limot->m_enableSpring)
810 btScalar error = limot->m_currentPosition - limot->m_equilibriumPoint;
811 calculateJacobi(limot, transA, transB, info, srow, ax1, rotational, rotAllowed);
813 //btScalar cfm = 1.0 / ((1.0/info->fps)*limot->m_springStiffness+ limot->m_springDamping);
816 //btScalar erp = (1.0/info->fps)*limot->m_springStiffness / ((1.0/info->fps)*limot->m_springStiffness + limot->m_springDamping);
817 //info->m_constraintError[srow] = info->fps * erp * error * (rotational ? -1.0 : 1.0);
818 //info->m_lowerLimit[srow] = -SIMD_INFINITY;
819 //info->m_upperLimit[srow] = SIMD_INFINITY;
821 btScalar dt = BT_ONE / info->fps;
822 btScalar kd = limot->m_springDamping;
823 btScalar ks = limot->m_springStiffness;
827 vel = angVelA.dot(ax1) - angVelB.dot(ax1);
831 btVector3 tanVelA = angVelA.cross(m_calculatedTransformA.getOrigin() - transA.getOrigin());
832 btVector3 tanVelB = angVelB.cross(m_calculatedTransformB.getOrigin() - transB.getOrigin());
833 vel = (linVelA + tanVelA).dot(ax1) - (linVelB + tanVelB).dot(ax1);
835 btScalar cfm = BT_ZERO;
836 btScalar mA = BT_ONE / m_rbA.getInvMass();
837 btScalar mB = BT_ONE / m_rbB.getInvMass();
840 btScalar rrA = (m_calculatedTransformA.getOrigin() - transA.getOrigin()).length2();
841 btScalar rrB = (m_calculatedTransformB.getOrigin() - transB.getOrigin()).length2();
842 if (m_rbA.getInvMass()) mA = mA * rrA + 1 / (m_rbA.getInvInertiaTensorWorld() * ax1).length();
843 if (m_rbB.getInvMass()) mB = mB * rrB + 1 / (m_rbB.getInvInertiaTensorWorld() * ax1).length();
846 if (m_rbA.getInvMass() == 0) m = mB; else
847 if (m_rbB.getInvMass() == 0) m = mA; else
848 m = mA*mB / (mA + mB);
849 btScalar angularfreq = btSqrt(ks / m);
851 //limit stiffness (the spring should not be sampled faster that the quarter of its angular frequency)
852 if (limot->m_springStiffnessLimited && 0.25 < angularfreq * dt)
854 ks = BT_ONE / dt / dt / btScalar(16.0) * m;
856 //avoid damping that would blow up the spring
857 if (limot->m_springDampingLimited && kd * dt > m)
861 btScalar fs = ks * error * dt;
862 btScalar fd = -kd * (vel) * (rotational ? -1 : 1) * dt;
863 btScalar f = (fs + fd);
865 // after the spring force affecting the body(es) the new velocity will be
866 // vel + f / m * (rotational ? -1 : 1)
867 // so in theory this should be set here for m_constraintError
868 // (with m_constraintError we set a desired velocity for the affected body(es))
869 // however in practice any value is fine as long as it is greater than the "proper" velocity,
870 // because the m_lowerLimit and the m_upperLimit will determinate the strength of the final pulling force
871 // so it is much simpler (and more robust) just to simply use inf (with the proper sign)
872 // (Even with our best intent the "new" velocity is only an estimation. If we underestimate
873 // the "proper" velocity that will weaken the spring, however if we overestimate it, it doesn't
874 // matter, because the solver will limit it according the force limit)
875 // you may also wonder what if the current velocity (vel) so high that the pulling force will not change its direction (in this iteration)
876 // will we not request a velocity with the wrong direction ?
877 // and the answer is not, because in practice during the solving the current velocity is subtracted from the m_constraintError
878 // so the sign of the force that is really matters
879 if (m_flags & BT_6DOF_FLAGS_USE_INFINITE_ERROR)
880 info->m_constraintError[srow] = (rotational ? -1 : 1) * (f < 0 ? -SIMD_INFINITY : SIMD_INFINITY);
882 info->m_constraintError[srow] = vel + f / m * (rotational ? -1 : 1);
884 btScalar minf = f < fd ? f : fd;
885 btScalar maxf = f < fd ? fd : f;
888 info->m_lowerLimit[srow] = minf > 0 ? 0 : minf;
889 info->m_upperLimit[srow] = maxf < 0 ? 0 : maxf;
893 info->m_lowerLimit[srow] = -maxf > 0 ? 0 : -maxf;
894 info->m_upperLimit[srow] = -minf < 0 ? 0 : -minf;
897 info->cfm[srow] = cfm;
898 srow += info->rowskip;
905 //override the default global value of a parameter (such as ERP or CFM), optionally provide the axis (0..5).
906 //If no axis is provided, it uses the default axis for this constraint.
907 void btGeneric6DofSpring2Constraint::setParam(int num, btScalar value, int axis)
909 if ((axis >= 0) && (axis < 3))
913 case BT_CONSTRAINT_STOP_ERP:
914 m_linearLimits.m_stopERP[axis] = value;
915 m_flags |= BT_6DOF_FLAGS_ERP_STOP2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2);
917 case BT_CONSTRAINT_STOP_CFM:
918 m_linearLimits.m_stopCFM[axis] = value;
919 m_flags |= BT_6DOF_FLAGS_CFM_STOP2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2);
921 case BT_CONSTRAINT_ERP:
922 m_linearLimits.m_motorERP[axis] = value;
923 m_flags |= BT_6DOF_FLAGS_ERP_MOTO2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2);
925 case BT_CONSTRAINT_CFM:
926 m_linearLimits.m_motorCFM[axis] = value;
927 m_flags |= BT_6DOF_FLAGS_CFM_MOTO2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2);
930 btAssertConstrParams(0);
933 else if ((axis >= 3) && (axis < 6))
937 case BT_CONSTRAINT_STOP_ERP:
938 m_angularLimits[axis - 3].m_stopERP = value;
939 m_flags |= BT_6DOF_FLAGS_ERP_STOP2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2);
941 case BT_CONSTRAINT_STOP_CFM:
942 m_angularLimits[axis - 3].m_stopCFM = value;
943 m_flags |= BT_6DOF_FLAGS_CFM_STOP2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2);
945 case BT_CONSTRAINT_ERP:
946 m_angularLimits[axis - 3].m_motorERP = value;
947 m_flags |= BT_6DOF_FLAGS_ERP_MOTO2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2);
949 case BT_CONSTRAINT_CFM:
950 m_angularLimits[axis - 3].m_motorCFM = value;
951 m_flags |= BT_6DOF_FLAGS_CFM_MOTO2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2);
954 btAssertConstrParams(0);
959 btAssertConstrParams(0);
963 //return the local value of parameter
964 btScalar btGeneric6DofSpring2Constraint::getParam(int num, int axis) const
967 if ((axis >= 0) && (axis < 3))
971 case BT_CONSTRAINT_STOP_ERP:
972 btAssertConstrParams(m_flags & (BT_6DOF_FLAGS_ERP_STOP2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2)));
973 retVal = m_linearLimits.m_stopERP[axis];
975 case BT_CONSTRAINT_STOP_CFM:
976 btAssertConstrParams(m_flags & (BT_6DOF_FLAGS_CFM_STOP2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2)));
977 retVal = m_linearLimits.m_stopCFM[axis];
979 case BT_CONSTRAINT_ERP:
980 btAssertConstrParams(m_flags & (BT_6DOF_FLAGS_ERP_MOTO2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2)));
981 retVal = m_linearLimits.m_motorERP[axis];
983 case BT_CONSTRAINT_CFM:
984 btAssertConstrParams(m_flags & (BT_6DOF_FLAGS_CFM_MOTO2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2)));
985 retVal = m_linearLimits.m_motorCFM[axis];
988 btAssertConstrParams(0);
991 else if ((axis >= 3) && (axis < 6))
995 case BT_CONSTRAINT_STOP_ERP:
996 btAssertConstrParams(m_flags & (BT_6DOF_FLAGS_ERP_STOP2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2)));
997 retVal = m_angularLimits[axis - 3].m_stopERP;
999 case BT_CONSTRAINT_STOP_CFM:
1000 btAssertConstrParams(m_flags & (BT_6DOF_FLAGS_CFM_STOP2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2)));
1001 retVal = m_angularLimits[axis - 3].m_stopCFM;
1003 case BT_CONSTRAINT_ERP:
1004 btAssertConstrParams(m_flags & (BT_6DOF_FLAGS_ERP_MOTO2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2)));
1005 retVal = m_angularLimits[axis - 3].m_motorERP;
1007 case BT_CONSTRAINT_CFM:
1008 btAssertConstrParams(m_flags & (BT_6DOF_FLAGS_CFM_MOTO2 << (axis * BT_6DOF_FLAGS_AXIS_SHIFT2)));
1009 retVal = m_angularLimits[axis - 3].m_motorCFM;
1012 btAssertConstrParams(0);
1017 btAssertConstrParams(0);
1022 void btGeneric6DofSpring2Constraint::setAxis(const btVector3& axis1, const btVector3& axis2)
1024 btVector3 zAxis = axis1.normalized();
1025 btVector3 yAxis = axis2.normalized();
1026 btVector3 xAxis = yAxis.cross(zAxis); // we want right coordinate system
1028 btTransform frameInW;
1029 frameInW.setIdentity();
1030 frameInW.getBasis().setValue(xAxis[0], yAxis[0], zAxis[0],
1031 xAxis[1], yAxis[1], zAxis[1],
1032 xAxis[2], yAxis[2], zAxis[2]);
1034 // now get constraint frame in local coordinate systems
1035 m_frameInA = m_rbA.getCenterOfMassTransform().inverse() * frameInW;
1036 m_frameInB = m_rbB.getCenterOfMassTransform().inverse() * frameInW;
1038 calculateTransforms();
1041 void btGeneric6DofSpring2Constraint::setBounce(int index, btScalar bounce)
1043 btAssert((index >= 0) && (index < 6));
1045 m_linearLimits.m_bounce[index] = bounce;
1047 m_angularLimits[index - 3].m_bounce = bounce;
1050 void btGeneric6DofSpring2Constraint::enableMotor(int index, bool onOff)
1052 btAssert((index >= 0) && (index < 6));
1054 m_linearLimits.m_enableMotor[index] = onOff;
1056 m_angularLimits[index - 3].m_enableMotor = onOff;
1059 void btGeneric6DofSpring2Constraint::setServo(int index, bool onOff)
1061 btAssert((index >= 0) && (index < 6));
1063 m_linearLimits.m_servoMotor[index] = onOff;
1065 m_angularLimits[index - 3].m_servoMotor = onOff;
1068 void btGeneric6DofSpring2Constraint::setTargetVelocity(int index, btScalar velocity)
1070 btAssert((index >= 0) && (index < 6));
1072 m_linearLimits.m_targetVelocity[index] = velocity;
1074 m_angularLimits[index - 3].m_targetVelocity = velocity;
1077 void btGeneric6DofSpring2Constraint::setServoTarget(int index, btScalar targetOrg)
1079 btAssert((index >= 0) && (index < 6));
1082 m_linearLimits.m_servoTarget[index] = targetOrg;
1086 //wrap between -PI and PI, see also
1087 //https://stackoverflow.com/questions/4633177/c-how-to-wrap-a-float-to-the-interval-pi-pi
1089 btScalar target = targetOrg + SIMD_PI;
1092 btScalar m = target - SIMD_2_PI * std::floor(target / SIMD_2_PI);
1093 // handle boundary cases resulted from floating-point cut off:
1103 if (SIMD_2_PI + m == SIMD_2_PI)
1106 target = SIMD_2_PI + m;
1117 m_angularLimits[index - 3].m_servoTarget = target;
1121 void btGeneric6DofSpring2Constraint::setMaxMotorForce(int index, btScalar force)
1123 btAssert((index >= 0) && (index < 6));
1125 m_linearLimits.m_maxMotorForce[index] = force;
1127 m_angularLimits[index - 3].m_maxMotorForce = force;
1130 void btGeneric6DofSpring2Constraint::enableSpring(int index, bool onOff)
1132 btAssert((index >= 0) && (index < 6));
1134 m_linearLimits.m_enableSpring[index] = onOff;
1136 m_angularLimits[index - 3].m_enableSpring = onOff;
1139 void btGeneric6DofSpring2Constraint::setStiffness(int index, btScalar stiffness, bool limitIfNeeded)
1141 btAssert((index >= 0) && (index < 6));
1144 m_linearLimits.m_springStiffness[index] = stiffness;
1145 m_linearLimits.m_springStiffnessLimited[index] = limitIfNeeded;
1149 m_angularLimits[index - 3].m_springStiffness = stiffness;
1150 m_angularLimits[index - 3].m_springStiffnessLimited = limitIfNeeded;
1154 void btGeneric6DofSpring2Constraint::setDamping(int index, btScalar damping, bool limitIfNeeded)
1156 btAssert((index >= 0) && (index < 6));
1159 m_linearLimits.m_springDamping[index] = damping;
1160 m_linearLimits.m_springDampingLimited[index] = limitIfNeeded;
1164 m_angularLimits[index - 3].m_springDamping = damping;
1165 m_angularLimits[index - 3].m_springDampingLimited = limitIfNeeded;
1169 void btGeneric6DofSpring2Constraint::setEquilibriumPoint()
1171 calculateTransforms();
1173 for (i = 0; i < 3; i++)
1174 m_linearLimits.m_equilibriumPoint[i] = m_calculatedLinearDiff[i];
1175 for (i = 0; i < 3; i++)
1176 m_angularLimits[i].m_equilibriumPoint = m_calculatedAxisAngleDiff[i];
1179 void btGeneric6DofSpring2Constraint::setEquilibriumPoint(int index)
1181 btAssert((index >= 0) && (index < 6));
1182 calculateTransforms();
1184 m_linearLimits.m_equilibriumPoint[index] = m_calculatedLinearDiff[index];
1186 m_angularLimits[index - 3].m_equilibriumPoint = m_calculatedAxisAngleDiff[index - 3];
1189 void btGeneric6DofSpring2Constraint::setEquilibriumPoint(int index, btScalar val)
1191 btAssert((index >= 0) && (index < 6));
1193 m_linearLimits.m_equilibriumPoint[index] = val;
1195 m_angularLimits[index - 3].m_equilibriumPoint = val;
1198 //////////////////////////// btRotationalLimitMotor2 ////////////////////////////////////
1200 void btRotationalLimitMotor2::testLimitValue(btScalar test_value)
1202 //we can't normalize the angles here because we would lost the sign that we use later, but it doesn't seem to be a problem
1203 if (m_loLimit > m_hiLimit)
1206 m_currentLimitError = btScalar(0.f);
1208 else if (m_loLimit == m_hiLimit)
1210 m_currentLimitError = test_value - m_loLimit;
1215 m_currentLimitError = test_value - m_loLimit;
1216 m_currentLimitErrorHi = test_value - m_hiLimit;
1221 //////////////////////////// btTranslationalLimitMotor2 ////////////////////////////////////
1223 void btTranslationalLimitMotor2::testLimitValue(int limitIndex, btScalar test_value)
1225 btScalar loLimit = m_lowerLimit[limitIndex];
1226 btScalar hiLimit = m_upperLimit[limitIndex];
1227 if (loLimit > hiLimit)
1229 m_currentLimitError[limitIndex] = 0;
1230 m_currentLimit[limitIndex] = 0;
1232 else if (loLimit == hiLimit)
1234 m_currentLimitError[limitIndex] = test_value - loLimit;
1235 m_currentLimit[limitIndex] = 3;
1239 m_currentLimitError[limitIndex] = test_value - loLimit;
1240 m_currentLimitErrorHi[limitIndex] = test_value - hiLimit;
1241 m_currentLimit[limitIndex] = 4;