2 Bullet Continuous Collision Detection and Physics Library
3 btConeTwistConstraint is Copyright (c) 2007 Starbreeze Studios
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.
15 Written by: Marcus Hennix
19 #include "btConeTwistConstraint.h"
20 #include "BulletDynamics/Dynamics/btRigidBody.h"
21 #include "LinearMath/btTransformUtil.h"
22 #include "LinearMath/btMinMax.h"
27 //#define CONETWIST_USE_OBSOLETE_SOLVER true
28 #define CONETWIST_USE_OBSOLETE_SOLVER false
29 #define CONETWIST_DEF_FIX_THRESH btScalar(.05f)
32 SIMD_FORCE_INLINE btScalar computeAngularImpulseDenominator(const btVector3& axis, const btMatrix3x3& invInertiaWorld)
34 btVector3 vec = axis * invInertiaWorld;
41 btConeTwistConstraint::btConeTwistConstraint(btRigidBody& rbA,btRigidBody& rbB,
42 const btTransform& rbAFrame,const btTransform& rbBFrame)
43 :btTypedConstraint(CONETWIST_CONSTRAINT_TYPE, rbA,rbB),m_rbAFrame(rbAFrame),m_rbBFrame(rbBFrame),
45 m_useSolveConstraintObsolete(CONETWIST_USE_OBSOLETE_SOLVER)
50 btConeTwistConstraint::btConeTwistConstraint(btRigidBody& rbA,const btTransform& rbAFrame)
51 :btTypedConstraint(CONETWIST_CONSTRAINT_TYPE,rbA),m_rbAFrame(rbAFrame),
53 m_useSolveConstraintObsolete(CONETWIST_USE_OBSOLETE_SOLVER)
55 m_rbBFrame = m_rbAFrame;
60 void btConeTwistConstraint::init()
62 m_angularOnly = false;
63 m_solveTwistLimit = false;
64 m_solveSwingLimit = false;
65 m_bMotorEnabled = false;
66 m_maxMotorImpulse = btScalar(-1);
68 setLimit(btScalar(BT_LARGE_FLOAT), btScalar(BT_LARGE_FLOAT), btScalar(BT_LARGE_FLOAT));
69 m_damping = btScalar(0.01);
70 m_fixThresh = CONETWIST_DEF_FIX_THRESH;
72 m_linCFM = btScalar(0.f);
73 m_linERP = btScalar(0.7f);
74 m_angCFM = btScalar(0.f);
78 void btConeTwistConstraint::getInfo1 (btConstraintInfo1* info)
80 if (m_useSolveConstraintObsolete)
82 info->m_numConstraintRows = 0;
87 info->m_numConstraintRows = 3;
89 calcAngleInfo2(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform(),m_rbA.getInvInertiaTensorWorld(),m_rbB.getInvInertiaTensorWorld());
92 info->m_numConstraintRows++;
94 if((m_swingSpan1 < m_fixThresh) && (m_swingSpan2 < m_fixThresh))
96 info->m_numConstraintRows++;
100 if(m_solveTwistLimit)
102 info->m_numConstraintRows++;
108 void btConeTwistConstraint::getInfo1NonVirtual (btConstraintInfo1* info)
110 //always reserve 6 rows: object transform is not available on SPU
111 info->m_numConstraintRows = 6;
117 void btConeTwistConstraint::getInfo2 (btConstraintInfo2* info)
119 getInfo2NonVirtual(info,m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform(),m_rbA.getInvInertiaTensorWorld(),m_rbB.getInvInertiaTensorWorld());
122 void btConeTwistConstraint::getInfo2NonVirtual (btConstraintInfo2* info,const btTransform& transA,const btTransform& transB,const btMatrix3x3& invInertiaWorldA,const btMatrix3x3& invInertiaWorldB)
124 calcAngleInfo2(transA,transB,invInertiaWorldA,invInertiaWorldB);
126 btAssert(!m_useSolveConstraintObsolete);
128 info->m_J1linearAxis[0] = 1;
129 info->m_J1linearAxis[info->rowskip+1] = 1;
130 info->m_J1linearAxis[2*info->rowskip+2] = 1;
131 btVector3 a1 = transA.getBasis() * m_rbAFrame.getOrigin();
133 btVector3* angular0 = (btVector3*)(info->m_J1angularAxis);
134 btVector3* angular1 = (btVector3*)(info->m_J1angularAxis+info->rowskip);
135 btVector3* angular2 = (btVector3*)(info->m_J1angularAxis+2*info->rowskip);
136 btVector3 a1neg = -a1;
137 a1neg.getSkewSymmetricMatrix(angular0,angular1,angular2);
139 btVector3 a2 = transB.getBasis() * m_rbBFrame.getOrigin();
141 btVector3* angular0 = (btVector3*)(info->m_J2angularAxis);
142 btVector3* angular1 = (btVector3*)(info->m_J2angularAxis+info->rowskip);
143 btVector3* angular2 = (btVector3*)(info->m_J2angularAxis+2*info->rowskip);
144 a2.getSkewSymmetricMatrix(angular0,angular1,angular2);
146 // set right hand side
147 btScalar linERP = (m_flags & BT_CONETWIST_FLAGS_LIN_ERP) ? m_linERP : info->erp;
148 btScalar k = info->fps * linERP;
152 info->m_constraintError[j*info->rowskip] = k * (a2[j] + transB.getOrigin()[j] - a1[j] - transA.getOrigin()[j]);
153 info->m_lowerLimit[j*info->rowskip] = -SIMD_INFINITY;
154 info->m_upperLimit[j*info->rowskip] = SIMD_INFINITY;
155 if(m_flags & BT_CONETWIST_FLAGS_LIN_CFM)
157 info->cfm[j*info->rowskip] = m_linCFM;
161 int srow = row * info->rowskip;
164 if(m_solveSwingLimit)
166 btScalar *J1 = info->m_J1angularAxis;
167 btScalar *J2 = info->m_J2angularAxis;
168 if((m_swingSpan1 < m_fixThresh) && (m_swingSpan2 < m_fixThresh))
170 btTransform trA = transA*m_rbAFrame;
171 btVector3 p = trA.getBasis().getColumn(1);
172 btVector3 q = trA.getBasis().getColumn(2);
173 int srow1 = srow + info->rowskip;
186 btScalar fact = info->fps * m_relaxationFactor;
187 info->m_constraintError[srow] = fact * m_swingAxis.dot(p);
188 info->m_constraintError[srow1] = fact * m_swingAxis.dot(q);
189 info->m_lowerLimit[srow] = -SIMD_INFINITY;
190 info->m_upperLimit[srow] = SIMD_INFINITY;
191 info->m_lowerLimit[srow1] = -SIMD_INFINITY;
192 info->m_upperLimit[srow1] = SIMD_INFINITY;
193 srow = srow1 + info->rowskip;
197 ax1 = m_swingAxis * m_relaxationFactor * m_relaxationFactor;
201 J2[srow+0] = -ax1[0];
202 J2[srow+1] = -ax1[1];
203 J2[srow+2] = -ax1[2];
204 btScalar k = info->fps * m_biasFactor;
206 info->m_constraintError[srow] = k * m_swingCorrection;
207 if(m_flags & BT_CONETWIST_FLAGS_ANG_CFM)
209 info->cfm[srow] = m_angCFM;
211 // m_swingCorrection is always positive or 0
212 info->m_lowerLimit[srow] = 0;
213 info->m_upperLimit[srow] = SIMD_INFINITY;
214 srow += info->rowskip;
217 if(m_solveTwistLimit)
219 ax1 = m_twistAxis * m_relaxationFactor * m_relaxationFactor;
220 btScalar *J1 = info->m_J1angularAxis;
221 btScalar *J2 = info->m_J2angularAxis;
225 J2[srow+0] = -ax1[0];
226 J2[srow+1] = -ax1[1];
227 J2[srow+2] = -ax1[2];
228 btScalar k = info->fps * m_biasFactor;
229 info->m_constraintError[srow] = k * m_twistCorrection;
230 if(m_flags & BT_CONETWIST_FLAGS_ANG_CFM)
232 info->cfm[srow] = m_angCFM;
234 if(m_twistSpan > 0.0f)
237 if(m_twistCorrection > 0.0f)
239 info->m_lowerLimit[srow] = 0;
240 info->m_upperLimit[srow] = SIMD_INFINITY;
244 info->m_lowerLimit[srow] = -SIMD_INFINITY;
245 info->m_upperLimit[srow] = 0;
250 info->m_lowerLimit[srow] = -SIMD_INFINITY;
251 info->m_upperLimit[srow] = SIMD_INFINITY;
253 srow += info->rowskip;
259 void btConeTwistConstraint::buildJacobian()
261 if (m_useSolveConstraintObsolete)
263 m_appliedImpulse = btScalar(0.);
264 m_accTwistLimitImpulse = btScalar(0.);
265 m_accSwingLimitImpulse = btScalar(0.);
266 m_accMotorImpulse = btVector3(0.,0.,0.);
270 btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_rbAFrame.getOrigin();
271 btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_rbBFrame.getOrigin();
272 btVector3 relPos = pivotBInW - pivotAInW;
275 if (relPos.length2() > SIMD_EPSILON)
277 normal[0] = relPos.normalized();
281 normal[0].setValue(btScalar(1.0),0,0);
284 btPlaneSpace1(normal[0], normal[1], normal[2]);
286 for (int i=0;i<3;i++)
288 new (&m_jac[i]) btJacobianEntry(
289 m_rbA.getCenterOfMassTransform().getBasis().transpose(),
290 m_rbB.getCenterOfMassTransform().getBasis().transpose(),
291 pivotAInW - m_rbA.getCenterOfMassPosition(),
292 pivotBInW - m_rbB.getCenterOfMassPosition(),
294 m_rbA.getInvInertiaDiagLocal(),
296 m_rbB.getInvInertiaDiagLocal(),
301 calcAngleInfo2(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform(),m_rbA.getInvInertiaTensorWorld(),m_rbB.getInvInertiaTensorWorld());
307 void btConeTwistConstraint::solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep)
310 if (m_useSolveConstraintObsolete)
312 btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_rbAFrame.getOrigin();
313 btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_rbBFrame.getOrigin();
315 btScalar tau = btScalar(0.3);
320 btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition();
321 btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition();
324 bodyA.internalGetVelocityInLocalPointObsolete(rel_pos1,vel1);
326 bodyB.internalGetVelocityInLocalPointObsolete(rel_pos2,vel2);
327 btVector3 vel = vel1 - vel2;
329 for (int i=0;i<3;i++)
331 const btVector3& normal = m_jac[i].m_linearJointAxis;
332 btScalar jacDiagABInv = btScalar(1.) / m_jac[i].getDiagonal();
335 rel_vel = normal.dot(vel);
336 //positional error (zeroth order error)
337 btScalar depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal
338 btScalar impulse = depth*tau/timeStep * jacDiagABInv - rel_vel * jacDiagABInv;
339 m_appliedImpulse += impulse;
341 btVector3 ftorqueAxis1 = rel_pos1.cross(normal);
342 btVector3 ftorqueAxis2 = rel_pos2.cross(normal);
343 bodyA.internalApplyImpulse(normal*m_rbA.getInvMass(), m_rbA.getInvInertiaTensorWorld()*ftorqueAxis1,impulse);
344 bodyB.internalApplyImpulse(normal*m_rbB.getInvMass(), m_rbB.getInvInertiaTensorWorld()*ftorqueAxis2,-impulse);
352 // compute current and predicted transforms
353 btTransform trACur = m_rbA.getCenterOfMassTransform();
354 btTransform trBCur = m_rbB.getCenterOfMassTransform();
355 btVector3 omegaA; bodyA.internalGetAngularVelocity(omegaA);
356 btVector3 omegaB; bodyB.internalGetAngularVelocity(omegaB);
357 btTransform trAPred; trAPred.setIdentity();
358 btVector3 zerovec(0,0,0);
359 btTransformUtil::integrateTransform(
360 trACur, zerovec, omegaA, timeStep, trAPred);
361 btTransform trBPred; trBPred.setIdentity();
362 btTransformUtil::integrateTransform(
363 trBCur, zerovec, omegaB, timeStep, trBPred);
365 // compute desired transforms in world
366 btTransform trPose(m_qTarget);
367 btTransform trABDes = m_rbBFrame * trPose * m_rbAFrame.inverse();
368 btTransform trADes = trBPred * trABDes;
369 btTransform trBDes = trAPred * trABDes.inverse();
371 // compute desired omegas in world
372 btVector3 omegaADes, omegaBDes;
374 btTransformUtil::calculateVelocity(trACur, trADes, timeStep, zerovec, omegaADes);
375 btTransformUtil::calculateVelocity(trBCur, trBDes, timeStep, zerovec, omegaBDes);
377 // compute delta omegas
378 btVector3 dOmegaA = omegaADes - omegaA;
379 btVector3 dOmegaB = omegaBDes - omegaB;
381 // compute weighted avg axis of dOmega (weighting based on inertias)
382 btVector3 axisA, axisB;
383 btScalar kAxisAInv = 0, kAxisBInv = 0;
385 if (dOmegaA.length2() > SIMD_EPSILON)
387 axisA = dOmegaA.normalized();
388 kAxisAInv = getRigidBodyA().computeAngularImpulseDenominator(axisA);
391 if (dOmegaB.length2() > SIMD_EPSILON)
393 axisB = dOmegaB.normalized();
394 kAxisBInv = getRigidBodyB().computeAngularImpulseDenominator(axisB);
397 btVector3 avgAxis = kAxisAInv * axisA + kAxisBInv * axisB;
399 static bool bDoTorque = true;
400 if (bDoTorque && avgAxis.length2() > SIMD_EPSILON)
403 kAxisAInv = getRigidBodyA().computeAngularImpulseDenominator(avgAxis);
404 kAxisBInv = getRigidBodyB().computeAngularImpulseDenominator(avgAxis);
405 btScalar kInvCombined = kAxisAInv + kAxisBInv;
407 btVector3 impulse = (kAxisAInv * dOmegaA - kAxisBInv * dOmegaB) /
408 (kInvCombined * kInvCombined);
410 if (m_maxMotorImpulse >= 0)
412 btScalar fMaxImpulse = m_maxMotorImpulse;
413 if (m_bNormalizedMotorStrength)
414 fMaxImpulse = fMaxImpulse/kAxisAInv;
416 btVector3 newUnclampedAccImpulse = m_accMotorImpulse + impulse;
417 btScalar newUnclampedMag = newUnclampedAccImpulse.length();
418 if (newUnclampedMag > fMaxImpulse)
420 newUnclampedAccImpulse.normalize();
421 newUnclampedAccImpulse *= fMaxImpulse;
422 impulse = newUnclampedAccImpulse - m_accMotorImpulse;
424 m_accMotorImpulse += impulse;
427 btScalar impulseMag = impulse.length();
428 btVector3 impulseAxis = impulse / impulseMag;
430 bodyA.internalApplyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*impulseAxis, impulseMag);
431 bodyB.internalApplyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*impulseAxis, -impulseMag);
435 else if (m_damping > SIMD_EPSILON) // no motor: do a little damping
437 btVector3 angVelA; bodyA.internalGetAngularVelocity(angVelA);
438 btVector3 angVelB; bodyB.internalGetAngularVelocity(angVelB);
439 btVector3 relVel = angVelB - angVelA;
440 if (relVel.length2() > SIMD_EPSILON)
442 btVector3 relVelAxis = relVel.normalized();
443 btScalar m_kDamping = btScalar(1.) /
444 (getRigidBodyA().computeAngularImpulseDenominator(relVelAxis) +
445 getRigidBodyB().computeAngularImpulseDenominator(relVelAxis));
446 btVector3 impulse = m_damping * m_kDamping * relVel;
448 btScalar impulseMag = impulse.length();
449 btVector3 impulseAxis = impulse / impulseMag;
450 bodyA.internalApplyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*impulseAxis, impulseMag);
451 bodyB.internalApplyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*impulseAxis, -impulseMag);
457 ///solve angular part
459 bodyA.internalGetAngularVelocity(angVelA);
461 bodyB.internalGetAngularVelocity(angVelB);
464 if (m_solveSwingLimit)
466 btScalar amplitude = m_swingLimitRatio * m_swingCorrection*m_biasFactor/timeStep;
467 btScalar relSwingVel = (angVelB - angVelA).dot(m_swingAxis);
469 amplitude += m_swingLimitRatio * relSwingVel * m_relaxationFactor;
470 btScalar impulseMag = amplitude * m_kSwing;
472 // Clamp the accumulated impulse
473 btScalar temp = m_accSwingLimitImpulse;
474 m_accSwingLimitImpulse = btMax(m_accSwingLimitImpulse + impulseMag, btScalar(0.0) );
475 impulseMag = m_accSwingLimitImpulse - temp;
477 btVector3 impulse = m_swingAxis * impulseMag;
479 // don't let cone response affect twist
480 // (this can happen since body A's twist doesn't match body B's AND we use an elliptical cone limit)
482 btVector3 impulseTwistCouple = impulse.dot(m_twistAxisA) * m_twistAxisA;
483 btVector3 impulseNoTwistCouple = impulse - impulseTwistCouple;
484 impulse = impulseNoTwistCouple;
487 impulseMag = impulse.length();
488 btVector3 noTwistSwingAxis = impulse / impulseMag;
490 bodyA.internalApplyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*noTwistSwingAxis, impulseMag);
491 bodyB.internalApplyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*noTwistSwingAxis, -impulseMag);
496 if (m_solveTwistLimit)
498 btScalar amplitude = m_twistLimitRatio * m_twistCorrection*m_biasFactor/timeStep;
499 btScalar relTwistVel = (angVelB - angVelA).dot( m_twistAxis );
500 if (relTwistVel > 0) // only damp when moving towards limit (m_twistAxis flipping is important)
501 amplitude += m_twistLimitRatio * relTwistVel * m_relaxationFactor;
502 btScalar impulseMag = amplitude * m_kTwist;
504 // Clamp the accumulated impulse
505 btScalar temp = m_accTwistLimitImpulse;
506 m_accTwistLimitImpulse = btMax(m_accTwistLimitImpulse + impulseMag, btScalar(0.0) );
507 impulseMag = m_accTwistLimitImpulse - temp;
509 // btVector3 impulse = m_twistAxis * impulseMag;
511 bodyA.internalApplyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*m_twistAxis,impulseMag);
512 bodyB.internalApplyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*m_twistAxis,-impulseMag);
524 void btConeTwistConstraint::updateRHS(btScalar timeStep)
532 void btConeTwistConstraint::calcAngleInfo()
534 m_swingCorrection = btScalar(0.);
535 m_twistLimitSign = btScalar(0.);
536 m_solveTwistLimit = false;
537 m_solveSwingLimit = false;
539 btVector3 b1Axis1,b1Axis2,b1Axis3;
540 btVector3 b2Axis1,b2Axis2;
542 b1Axis1 = getRigidBodyA().getCenterOfMassTransform().getBasis() * this->m_rbAFrame.getBasis().getColumn(0);
543 b2Axis1 = getRigidBodyB().getCenterOfMassTransform().getBasis() * this->m_rbBFrame.getBasis().getColumn(0);
545 btScalar swing1=btScalar(0.),swing2 = btScalar(0.);
547 btScalar swx=btScalar(0.),swy = btScalar(0.);
548 btScalar thresh = btScalar(10.);
551 // Get Frame into world space
552 if (m_swingSpan1 >= btScalar(0.05f))
554 b1Axis2 = getRigidBodyA().getCenterOfMassTransform().getBasis() * this->m_rbAFrame.getBasis().getColumn(1);
555 swx = b2Axis1.dot(b1Axis1);
556 swy = b2Axis1.dot(b1Axis2);
557 swing1 = btAtan2Fast(swy, swx);
558 fact = (swy*swy + swx*swx) * thresh * thresh;
559 fact = fact / (fact + btScalar(1.0));
563 if (m_swingSpan2 >= btScalar(0.05f))
565 b1Axis3 = getRigidBodyA().getCenterOfMassTransform().getBasis() * this->m_rbAFrame.getBasis().getColumn(2);
566 swx = b2Axis1.dot(b1Axis1);
567 swy = b2Axis1.dot(b1Axis3);
568 swing2 = btAtan2Fast(swy, swx);
569 fact = (swy*swy + swx*swx) * thresh * thresh;
570 fact = fact / (fact + btScalar(1.0));
574 btScalar RMaxAngle1Sq = 1.0f / (m_swingSpan1*m_swingSpan1);
575 btScalar RMaxAngle2Sq = 1.0f / (m_swingSpan2*m_swingSpan2);
576 btScalar EllipseAngle = btFabs(swing1*swing1)* RMaxAngle1Sq + btFabs(swing2*swing2) * RMaxAngle2Sq;
578 if (EllipseAngle > 1.0f)
580 m_swingCorrection = EllipseAngle-1.0f;
581 m_solveSwingLimit = true;
582 // Calculate necessary axis & factors
583 m_swingAxis = b2Axis1.cross(b1Axis2* b2Axis1.dot(b1Axis2) + b1Axis3* b2Axis1.dot(b1Axis3));
584 m_swingAxis.normalize();
585 btScalar swingAxisSign = (b2Axis1.dot(b1Axis1) >= 0.0f) ? 1.0f : -1.0f;
586 m_swingAxis *= swingAxisSign;
590 if (m_twistSpan >= btScalar(0.))
592 btVector3 b2Axis2 = getRigidBodyB().getCenterOfMassTransform().getBasis() * this->m_rbBFrame.getBasis().getColumn(1);
593 btQuaternion rotationArc = shortestArcQuat(b2Axis1,b1Axis1);
594 btVector3 TwistRef = quatRotate(rotationArc,b2Axis2);
595 btScalar twist = btAtan2Fast( TwistRef.dot(b1Axis3), TwistRef.dot(b1Axis2) );
596 m_twistAngle = twist;
598 // btScalar lockedFreeFactor = (m_twistSpan > btScalar(0.05f)) ? m_limitSoftness : btScalar(0.);
599 btScalar lockedFreeFactor = (m_twistSpan > btScalar(0.05f)) ? btScalar(1.0f) : btScalar(0.);
600 if (twist <= -m_twistSpan*lockedFreeFactor)
602 m_twistCorrection = -(twist + m_twistSpan);
603 m_solveTwistLimit = true;
604 m_twistAxis = (b2Axis1 + b1Axis1) * 0.5f;
605 m_twistAxis.normalize();
606 m_twistAxis *= -1.0f;
608 else if (twist > m_twistSpan*lockedFreeFactor)
610 m_twistCorrection = (twist - m_twistSpan);
611 m_solveTwistLimit = true;
612 m_twistAxis = (b2Axis1 + b1Axis1) * 0.5f;
613 m_twistAxis.normalize();
619 static btVector3 vTwist(1,0,0); // twist axis in constraint's space
623 void btConeTwistConstraint::calcAngleInfo2(const btTransform& transA, const btTransform& transB, const btMatrix3x3& invInertiaWorldA,const btMatrix3x3& invInertiaWorldB)
625 m_swingCorrection = btScalar(0.);
626 m_twistLimitSign = btScalar(0.);
627 m_solveTwistLimit = false;
628 m_solveSwingLimit = false;
629 // compute rotation of A wrt B (in constraint space)
630 if (m_bMotorEnabled && (!m_useSolveConstraintObsolete))
631 { // it is assumed that setMotorTarget() was alredy called
632 // and motor target m_qTarget is within constraint limits
633 // TODO : split rotation to pure swing and pure twist
634 // compute desired transforms in world
635 btTransform trPose(m_qTarget);
636 btTransform trA = transA * m_rbAFrame;
637 btTransform trB = transB * m_rbBFrame;
638 btTransform trDeltaAB = trB * trPose * trA.inverse();
639 btQuaternion qDeltaAB = trDeltaAB.getRotation();
640 btVector3 swingAxis = btVector3(qDeltaAB.x(), qDeltaAB.y(), qDeltaAB.z());
641 float swingAxisLen2 = swingAxis.length2();
642 if(btFuzzyZero(swingAxisLen2))
646 m_swingAxis = swingAxis;
647 m_swingAxis.normalize();
648 m_swingCorrection = qDeltaAB.getAngle();
649 if(!btFuzzyZero(m_swingCorrection))
651 m_solveSwingLimit = true;
658 // compute rotation of A wrt B (in constraint space)
659 btQuaternion qA = transA.getRotation() * m_rbAFrame.getRotation();
660 btQuaternion qB = transB.getRotation() * m_rbBFrame.getRotation();
661 btQuaternion qAB = qB.inverse() * qA;
662 // split rotation into cone and twist
663 // (all this is done from B's perspective. Maybe I should be averaging axes...)
664 btVector3 vConeNoTwist = quatRotate(qAB, vTwist); vConeNoTwist.normalize();
665 btQuaternion qABCone = shortestArcQuat(vTwist, vConeNoTwist); qABCone.normalize();
666 btQuaternion qABTwist = qABCone.inverse() * qAB; qABTwist.normalize();
668 if (m_swingSpan1 >= m_fixThresh && m_swingSpan2 >= m_fixThresh)
670 btScalar swingAngle, swingLimit = 0; btVector3 swingAxis;
671 computeConeLimitInfo(qABCone, swingAngle, swingAxis, swingLimit);
673 if (swingAngle > swingLimit * m_limitSoftness)
675 m_solveSwingLimit = true;
677 // compute limit ratio: 0->1, where
678 // 0 == beginning of soft limit
679 // 1 == hard/real limit
680 m_swingLimitRatio = 1.f;
681 if (swingAngle < swingLimit && m_limitSoftness < 1.f - SIMD_EPSILON)
683 m_swingLimitRatio = (swingAngle - swingLimit * m_limitSoftness)/
684 (swingLimit - swingLimit * m_limitSoftness);
687 // swing correction tries to get back to soft limit
688 m_swingCorrection = swingAngle - (swingLimit * m_limitSoftness);
690 // adjustment of swing axis (based on ellipse normal)
691 adjustSwingAxisToUseEllipseNormal(swingAxis);
693 // Calculate necessary axis & factors
694 m_swingAxis = quatRotate(qB, -swingAxis);
696 m_twistAxisA.setValue(0,0,0);
698 m_kSwing = btScalar(1.) /
699 (computeAngularImpulseDenominator(m_swingAxis,invInertiaWorldA) +
700 computeAngularImpulseDenominator(m_swingAxis,invInertiaWorldB));
705 // you haven't set any limits;
706 // or you're trying to set at least one of the swing limits too small. (if so, do you really want a conetwist constraint?)
707 // anyway, we have either hinge or fixed joint
708 btVector3 ivA = transA.getBasis() * m_rbAFrame.getBasis().getColumn(0);
709 btVector3 jvA = transA.getBasis() * m_rbAFrame.getBasis().getColumn(1);
710 btVector3 kvA = transA.getBasis() * m_rbAFrame.getBasis().getColumn(2);
711 btVector3 ivB = transB.getBasis() * m_rbBFrame.getBasis().getColumn(0);
713 btScalar x = ivB.dot(ivA);
714 btScalar y = ivB.dot(jvA);
715 btScalar z = ivB.dot(kvA);
716 if((m_swingSpan1 < m_fixThresh) && (m_swingSpan2 < m_fixThresh))
717 { // fixed. We'll need to add one more row to constraint
718 if((!btFuzzyZero(y)) || (!(btFuzzyZero(z))))
720 m_solveSwingLimit = true;
721 m_swingAxis = -ivB.cross(ivA);
726 if(m_swingSpan1 < m_fixThresh)
727 { // hinge around Y axis
728 if(!(btFuzzyZero(y)))
730 m_solveSwingLimit = true;
731 if(m_swingSpan2 >= m_fixThresh)
734 btScalar span2 = btAtan2(z, x);
735 if(span2 > m_swingSpan2)
737 x = btCos(m_swingSpan2);
738 z = btSin(m_swingSpan2);
740 else if(span2 < -m_swingSpan2)
742 x = btCos(m_swingSpan2);
743 z = -btSin(m_swingSpan2);
749 { // hinge around Z axis
752 m_solveSwingLimit = true;
753 if(m_swingSpan1 >= m_fixThresh)
756 btScalar span1 = btAtan2(y, x);
757 if(span1 > m_swingSpan1)
759 x = btCos(m_swingSpan1);
760 y = btSin(m_swingSpan1);
762 else if(span1 < -m_swingSpan1)
764 x = btCos(m_swingSpan1);
765 y = -btSin(m_swingSpan1);
770 target[0] = x * ivA[0] + y * jvA[0] + z * kvA[0];
771 target[1] = x * ivA[1] + y * jvA[1] + z * kvA[1];
772 target[2] = x * ivA[2] + y * jvA[2] + z * kvA[2];
774 m_swingAxis = -ivB.cross(target);
775 m_swingCorrection = m_swingAxis.length();
776 m_swingAxis.normalize();
780 if (m_twistSpan >= btScalar(0.f))
783 computeTwistLimitInfo(qABTwist, m_twistAngle, twistAxis);
785 if (m_twistAngle > m_twistSpan*m_limitSoftness)
787 m_solveTwistLimit = true;
789 m_twistLimitRatio = 1.f;
790 if (m_twistAngle < m_twistSpan && m_limitSoftness < 1.f - SIMD_EPSILON)
792 m_twistLimitRatio = (m_twistAngle - m_twistSpan * m_limitSoftness)/
793 (m_twistSpan - m_twistSpan * m_limitSoftness);
796 // twist correction tries to get back to soft limit
797 m_twistCorrection = m_twistAngle - (m_twistSpan * m_limitSoftness);
799 m_twistAxis = quatRotate(qB, -twistAxis);
801 m_kTwist = btScalar(1.) /
802 (computeAngularImpulseDenominator(m_twistAxis,invInertiaWorldA) +
803 computeAngularImpulseDenominator(m_twistAxis,invInertiaWorldB));
806 if (m_solveSwingLimit)
807 m_twistAxisA = quatRotate(qA, -twistAxis);
811 m_twistAngle = btScalar(0.f);
818 // given a cone rotation in constraint space, (pre: twist must already be removed)
819 // this method computes its corresponding swing angle and axis.
820 // more interestingly, it computes the cone/swing limit (angle) for this cone "pose".
821 void btConeTwistConstraint::computeConeLimitInfo(const btQuaternion& qCone,
822 btScalar& swingAngle, // out
823 btVector3& vSwingAxis, // out
824 btScalar& swingLimit) // out
826 swingAngle = qCone.getAngle();
827 if (swingAngle > SIMD_EPSILON)
829 vSwingAxis = btVector3(qCone.x(), qCone.y(), qCone.z());
830 vSwingAxis.normalize();
832 // non-zero twist?! this should never happen.
833 btAssert(fabs(vSwingAxis.x()) <= SIMD_EPSILON));
836 // Compute limit for given swing. tricky:
837 // Given a swing axis, we're looking for the intersection with the bounding cone ellipse.
838 // (Since we're dealing with angles, this ellipse is embedded on the surface of a sphere.)
840 // For starters, compute the direction from center to surface of ellipse.
841 // This is just the perpendicular (ie. rotate 2D vector by PI/2) of the swing axis.
842 // (vSwingAxis is the cone rotation (in z,y); change vars and rotate to (x,y) coords.)
843 btScalar xEllipse = vSwingAxis.y();
844 btScalar yEllipse = -vSwingAxis.z();
846 // Now, we use the slope of the vector (using x/yEllipse) and find the length
847 // of the line that intersects the ellipse:
849 // --- + --- = 1, where a and b are semi-major axes 2 and 1 respectively (ie. the limits)
851 // Do the math and it should be clear.
853 swingLimit = m_swingSpan1; // if xEllipse == 0, we have a pure vSwingAxis.z rotation: just use swingspan1
854 if (fabs(xEllipse) > SIMD_EPSILON)
856 btScalar surfaceSlope2 = (yEllipse*yEllipse)/(xEllipse*xEllipse);
857 btScalar norm = 1 / (m_swingSpan2 * m_swingSpan2);
858 norm += surfaceSlope2 / (m_swingSpan1 * m_swingSpan1);
859 btScalar swingLimit2 = (1 + surfaceSlope2) / norm;
860 swingLimit = sqrt(swingLimit2);
864 /*swingLimit = m_swingSpan2;
865 if (fabs(vSwingAxis.z()) > SIMD_EPSILON)
867 btScalar mag_2 = m_swingSpan1*m_swingSpan1 + m_swingSpan2*m_swingSpan2;
868 btScalar sinphi = m_swingSpan2 / sqrt(mag_2);
869 btScalar phi = asin(sinphi);
870 btScalar theta = atan2(fabs(vSwingAxis.y()),fabs(vSwingAxis.z()));
871 btScalar alpha = 3.14159f - theta - phi;
872 btScalar sinalpha = sin(alpha);
873 swingLimit = m_swingSpan1 * sinphi/sinalpha;
876 else if (swingAngle < 0)
878 // this should never happen!
885 btVector3 btConeTwistConstraint::GetPointForAngle(btScalar fAngleInRadians, btScalar fLength) const
887 // compute x/y in ellipse using cone angle (0 -> 2*PI along surface of cone)
888 btScalar xEllipse = btCos(fAngleInRadians);
889 btScalar yEllipse = btSin(fAngleInRadians);
891 // Use the slope of the vector (using x/yEllipse) and find the length
892 // of the line that intersects the ellipse:
894 // --- + --- = 1, where a and b are semi-major axes 2 and 1 respectively (ie. the limits)
896 // Do the math and it should be clear.
898 float swingLimit = m_swingSpan1; // if xEllipse == 0, just use axis b (1)
899 if (fabs(xEllipse) > SIMD_EPSILON)
901 btScalar surfaceSlope2 = (yEllipse*yEllipse)/(xEllipse*xEllipse);
902 btScalar norm = 1 / (m_swingSpan2 * m_swingSpan2);
903 norm += surfaceSlope2 / (m_swingSpan1 * m_swingSpan1);
904 btScalar swingLimit2 = (1 + surfaceSlope2) / norm;
905 swingLimit = sqrt(swingLimit2);
908 // convert into point in constraint space:
909 // note: twist is x-axis, swing 1 and 2 are along the z and y axes respectively
910 btVector3 vSwingAxis(0, xEllipse, -yEllipse);
911 btQuaternion qSwing(vSwingAxis, swingLimit);
912 btVector3 vPointInConstraintSpace(fLength,0,0);
913 return quatRotate(qSwing, vPointInConstraintSpace);
916 // given a twist rotation in constraint space, (pre: cone must already be removed)
917 // this method computes its corresponding angle and axis.
918 void btConeTwistConstraint::computeTwistLimitInfo(const btQuaternion& qTwist,
919 btScalar& twistAngle, // out
920 btVector3& vTwistAxis) // out
922 btQuaternion qMinTwist = qTwist;
923 twistAngle = qTwist.getAngle();
925 if (twistAngle > SIMD_PI) // long way around. flip quat and recalculate.
927 qMinTwist = -(qTwist);
928 twistAngle = qMinTwist.getAngle();
932 // this should never happen
938 vTwistAxis = btVector3(qMinTwist.x(), qMinTwist.y(), qMinTwist.z());
939 if (twistAngle > SIMD_EPSILON)
940 vTwistAxis.normalize();
944 void btConeTwistConstraint::adjustSwingAxisToUseEllipseNormal(btVector3& vSwingAxis) const
946 // the swing axis is computed as the "twist-free" cone rotation,
947 // but the cone limit is not circular, but elliptical (if swingspan1 != swingspan2).
948 // so, if we're outside the limits, the closest way back inside the cone isn't
949 // along the vector back to the center. better (and more stable) to use the ellipse normal.
951 // convert swing axis to direction from center to surface of ellipse
952 // (ie. rotate 2D vector by PI/2)
953 btScalar y = -vSwingAxis.z();
954 btScalar z = vSwingAxis.y();
957 if (fabs(z) > SIMD_EPSILON) // avoid division by 0. and we don't need an update if z == 0.
959 // compute gradient/normal of ellipse surface at current "point"
961 grad *= m_swingSpan2 / m_swingSpan1;
963 // adjust y/z to represent normal at point (instead of vector to point)
969 // convert ellipse direction back to swing axis
972 vSwingAxis.normalize();
978 void btConeTwistConstraint::setMotorTarget(const btQuaternion &q)
980 btTransform trACur = m_rbA.getCenterOfMassTransform();
981 btTransform trBCur = m_rbB.getCenterOfMassTransform();
982 // btTransform trABCur = trBCur.inverse() * trACur;
983 // btQuaternion qABCur = trABCur.getRotation();
984 // btTransform trConstraintCur = (trBCur * m_rbBFrame).inverse() * (trACur * m_rbAFrame);
985 //btQuaternion qConstraintCur = trConstraintCur.getRotation();
987 btQuaternion qConstraint = m_rbBFrame.getRotation().inverse() * q * m_rbAFrame.getRotation();
988 setMotorTargetInConstraintSpace(qConstraint);
992 void btConeTwistConstraint::setMotorTargetInConstraintSpace(const btQuaternion &q)
996 // clamp motor target to within limits
998 btScalar softness = 1.f;//m_limitSoftness;
1000 // split into twist and cone
1001 btVector3 vTwisted = quatRotate(m_qTarget, vTwist);
1002 btQuaternion qTargetCone = shortestArcQuat(vTwist, vTwisted); qTargetCone.normalize();
1003 btQuaternion qTargetTwist = qTargetCone.inverse() * m_qTarget; qTargetTwist.normalize();
1006 if (m_swingSpan1 >= btScalar(0.05f) && m_swingSpan2 >= btScalar(0.05f))
1008 btScalar swingAngle, swingLimit; btVector3 swingAxis;
1009 computeConeLimitInfo(qTargetCone, swingAngle, swingAxis, swingLimit);
1011 if (fabs(swingAngle) > SIMD_EPSILON)
1013 if (swingAngle > swingLimit*softness)
1014 swingAngle = swingLimit*softness;
1015 else if (swingAngle < -swingLimit*softness)
1016 swingAngle = -swingLimit*softness;
1017 qTargetCone = btQuaternion(swingAxis, swingAngle);
1022 if (m_twistSpan >= btScalar(0.05f))
1024 btScalar twistAngle; btVector3 twistAxis;
1025 computeTwistLimitInfo(qTargetTwist, twistAngle, twistAxis);
1027 if (fabs(twistAngle) > SIMD_EPSILON)
1029 // eddy todo: limitSoftness used here???
1030 if (twistAngle > m_twistSpan*softness)
1031 twistAngle = m_twistSpan*softness;
1032 else if (twistAngle < -m_twistSpan*softness)
1033 twistAngle = -m_twistSpan*softness;
1034 qTargetTwist = btQuaternion(twistAxis, twistAngle);
1038 m_qTarget = qTargetCone * qTargetTwist;
1042 ///override the default global value of a parameter (such as ERP or CFM), optionally provide the axis (0..5).
1043 ///If no axis is provided, it uses the default axis for this constraint.
1044 void btConeTwistConstraint::setParam(int num, btScalar value, int axis)
1048 case BT_CONSTRAINT_ERP :
1049 case BT_CONSTRAINT_STOP_ERP :
1050 if((axis >= 0) && (axis < 3))
1053 m_flags |= BT_CONETWIST_FLAGS_LIN_ERP;
1057 m_biasFactor = value;
1060 case BT_CONSTRAINT_CFM :
1061 case BT_CONSTRAINT_STOP_CFM :
1062 if((axis >= 0) && (axis < 3))
1065 m_flags |= BT_CONETWIST_FLAGS_LIN_CFM;
1070 m_flags |= BT_CONETWIST_FLAGS_ANG_CFM;
1074 btAssertConstrParams(0);
1079 ///return the local value of parameter
1080 btScalar btConeTwistConstraint::getParam(int num, int axis) const
1082 btScalar retVal = 0;
1085 case BT_CONSTRAINT_ERP :
1086 case BT_CONSTRAINT_STOP_ERP :
1087 if((axis >= 0) && (axis < 3))
1089 btAssertConstrParams(m_flags & BT_CONETWIST_FLAGS_LIN_ERP);
1092 else if((axis >= 3) && (axis < 6))
1094 retVal = m_biasFactor;
1098 btAssertConstrParams(0);
1101 case BT_CONSTRAINT_CFM :
1102 case BT_CONSTRAINT_STOP_CFM :
1103 if((axis >= 0) && (axis < 3))
1105 btAssertConstrParams(m_flags & BT_CONETWIST_FLAGS_LIN_CFM);
1108 else if((axis >= 3) && (axis < 6))
1110 btAssertConstrParams(m_flags & BT_CONETWIST_FLAGS_ANG_CFM);
1115 btAssertConstrParams(0);
1119 btAssertConstrParams(0);
1125 void btConeTwistConstraint::setFrames(const btTransform & frameA, const btTransform & frameB)
1127 m_rbAFrame = frameA;
1128 m_rbBFrame = frameB;
1130 //calculateTransforms();