Bullet Collision Detection & Physics Library
btConeTwistConstraint.cpp
Go to the documentation of this file.
1 /*
2 Bullet Continuous Collision Detection and Physics Library
3 btConeTwistConstraint is Copyright (c) 2007 Starbreeze Studios
4 
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:
10 
11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
14 
15 Written by: Marcus Hennix
16 */
17 
18 
19 #include "btConeTwistConstraint.h"
22 #include "LinearMath/btMinMax.h"
23 #include <new>
24 
25 
26 
27 //#define CONETWIST_USE_OBSOLETE_SOLVER true
28 #define CONETWIST_USE_OBSOLETE_SOLVER false
29 #define CONETWIST_DEF_FIX_THRESH btScalar(.05f)
30 
31 
33 {
34  btVector3 vec = axis * invInertiaWorld;
35  return axis.dot(vec);
36 }
37 
38 
39 
40 
42  const btTransform& rbAFrame,const btTransform& rbBFrame)
43  :btTypedConstraint(CONETWIST_CONSTRAINT_TYPE, rbA,rbB),m_rbAFrame(rbAFrame),m_rbBFrame(rbBFrame),
44  m_angularOnly(false),
45  m_useSolveConstraintObsolete(CONETWIST_USE_OBSOLETE_SOLVER)
46 {
47  init();
48 }
49 
51  :btTypedConstraint(CONETWIST_CONSTRAINT_TYPE,rbA),m_rbAFrame(rbAFrame),
52  m_angularOnly(false),
53  m_useSolveConstraintObsolete(CONETWIST_USE_OBSOLETE_SOLVER)
54 {
56  init();
57 }
58 
59 
61 {
62  m_angularOnly = false;
63  m_solveTwistLimit = false;
64  m_solveSwingLimit = false;
65  m_bMotorEnabled = false;
67 
69  m_damping = btScalar(0.01);
71  m_flags = 0;
72  m_linCFM = btScalar(0.f);
73  m_linERP = btScalar(0.7f);
74  m_angCFM = btScalar(0.f);
75 }
76 
77 
79 {
81  {
82  info->m_numConstraintRows = 0;
83  info->nub = 0;
84  }
85  else
86  {
87  info->m_numConstraintRows = 3;
88  info->nub = 3;
91  {
92  info->m_numConstraintRows++;
93  info->nub--;
95  {
96  info->m_numConstraintRows++;
97  info->nub--;
98  }
99  }
101  {
102  info->m_numConstraintRows++;
103  info->nub--;
104  }
105  }
106 }
107 
109 {
110  //always reserve 6 rows: object transform is not available on SPU
111  info->m_numConstraintRows = 6;
112  info->nub = 0;
113 
114 }
115 
116 
118 {
120 }
121 
122 void btConeTwistConstraint::getInfo2NonVirtual (btConstraintInfo2* info,const btTransform& transA,const btTransform& transB,const btMatrix3x3& invInertiaWorldA,const btMatrix3x3& invInertiaWorldB)
123 {
124  calcAngleInfo2(transA,transB,invInertiaWorldA,invInertiaWorldB);
125 
127  // set jacobian
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();
132  {
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);
138  }
139  btVector3 a2 = transB.getBasis() * m_rbBFrame.getOrigin();
140  {
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);
145  }
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;
149  int j;
150  for (j=0; j<3; j++)
151  {
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;
156  {
157  info->cfm[j*info->rowskip] = m_linCFM;
158  }
159  }
160  int row = 3;
161  int srow = row * info->rowskip;
162  btVector3 ax1;
163  // angular limits
165  {
166  btScalar *J1 = info->m_J1angularAxis;
167  btScalar *J2 = info->m_J2angularAxis;
169  {
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;
174  J1[srow+0] = p[0];
175  J1[srow+1] = p[1];
176  J1[srow+2] = p[2];
177  J1[srow1+0] = q[0];
178  J1[srow1+1] = q[1];
179  J1[srow1+2] = q[2];
180  J2[srow+0] = -p[0];
181  J2[srow+1] = -p[1];
182  J2[srow+2] = -p[2];
183  J2[srow1+0] = -q[0];
184  J2[srow1+1] = -q[1];
185  J2[srow1+2] = -q[2];
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;
194  }
195  else
196  {
198  J1[srow+0] = ax1[0];
199  J1[srow+1] = ax1[1];
200  J1[srow+2] = ax1[2];
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;
205 
206  info->m_constraintError[srow] = k * m_swingCorrection;
208  {
209  info->cfm[srow] = m_angCFM;
210  }
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;
215  }
216  }
218  {
220  btScalar *J1 = info->m_J1angularAxis;
221  btScalar *J2 = info->m_J2angularAxis;
222  J1[srow+0] = ax1[0];
223  J1[srow+1] = ax1[1];
224  J1[srow+2] = ax1[2];
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;
231  {
232  info->cfm[srow] = m_angCFM;
233  }
234  if(m_twistSpan > 0.0f)
235  {
236 
237  if(m_twistCorrection > 0.0f)
238  {
239  info->m_lowerLimit[srow] = 0;
240  info->m_upperLimit[srow] = SIMD_INFINITY;
241  }
242  else
243  {
244  info->m_lowerLimit[srow] = -SIMD_INFINITY;
245  info->m_upperLimit[srow] = 0;
246  }
247  }
248  else
249  {
250  info->m_lowerLimit[srow] = -SIMD_INFINITY;
251  info->m_upperLimit[srow] = SIMD_INFINITY;
252  }
253  srow += info->rowskip;
254  }
255 }
256 
257 
258 
260 {
262  {
266  m_accMotorImpulse = btVector3(0.,0.,0.);
267 
268  if (!m_angularOnly)
269  {
272  btVector3 relPos = pivotBInW - pivotAInW;
273 
274  btVector3 normal[3];
275  if (relPos.length2() > SIMD_EPSILON)
276  {
277  normal[0] = relPos.normalized();
278  }
279  else
280  {
281  normal[0].setValue(btScalar(1.0),0,0);
282  }
283 
284  btPlaneSpace1(normal[0], normal[1], normal[2]);
285 
286  for (int i=0;i<3;i++)
287  {
288  new (&m_jac[i]) btJacobianEntry(
291  pivotAInW - m_rbA.getCenterOfMassPosition(),
292  pivotBInW - m_rbB.getCenterOfMassPosition(),
293  normal[i],
295  m_rbA.getInvMass(),
297  m_rbB.getInvMass());
298  }
299  }
300 
302  }
303 }
304 
305 
306 
307 void btConeTwistConstraint::solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep)
308 {
309  #ifndef __SPU__
311  {
314 
315  btScalar tau = btScalar(0.3);
316 
317  //linear part
318  if (!m_angularOnly)
319  {
320  btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition();
321  btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition();
322 
323  btVector3 vel1;
324  bodyA.internalGetVelocityInLocalPointObsolete(rel_pos1,vel1);
325  btVector3 vel2;
326  bodyB.internalGetVelocityInLocalPointObsolete(rel_pos2,vel2);
327  btVector3 vel = vel1 - vel2;
328 
329  for (int i=0;i<3;i++)
330  {
331  const btVector3& normal = m_jac[i].m_linearJointAxis;
332  btScalar jacDiagABInv = btScalar(1.) / m_jac[i].getDiagonal();
333 
334  btScalar rel_vel;
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;
340 
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);
345 
346  }
347  }
348 
349  // apply motor
350  if (m_bMotorEnabled)
351  {
352  // compute current and predicted transforms
355  btVector3 omegaA; bodyA.internalGetAngularVelocity(omegaA);
356  btVector3 omegaB; bodyB.internalGetAngularVelocity(omegaB);
357  btTransform trAPred; trAPred.setIdentity();
358  btVector3 zerovec(0,0,0);
360  trACur, zerovec, omegaA, timeStep, trAPred);
361  btTransform trBPred; trBPred.setIdentity();
363  trBCur, zerovec, omegaB, timeStep, trBPred);
364 
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();
370 
371  // compute desired omegas in world
372  btVector3 omegaADes, omegaBDes;
373 
374  btTransformUtil::calculateVelocity(trACur, trADes, timeStep, zerovec, omegaADes);
375  btTransformUtil::calculateVelocity(trBCur, trBDes, timeStep, zerovec, omegaBDes);
376 
377  // compute delta omegas
378  btVector3 dOmegaA = omegaADes - omegaA;
379  btVector3 dOmegaB = omegaBDes - omegaB;
380 
381  // compute weighted avg axis of dOmega (weighting based on inertias)
382  btVector3 axisA, axisB;
383  btScalar kAxisAInv = 0, kAxisBInv = 0;
384 
385  if (dOmegaA.length2() > SIMD_EPSILON)
386  {
387  axisA = dOmegaA.normalized();
388  kAxisAInv = getRigidBodyA().computeAngularImpulseDenominator(axisA);
389  }
390 
391  if (dOmegaB.length2() > SIMD_EPSILON)
392  {
393  axisB = dOmegaB.normalized();
394  kAxisBInv = getRigidBodyB().computeAngularImpulseDenominator(axisB);
395  }
396 
397  btVector3 avgAxis = kAxisAInv * axisA + kAxisBInv * axisB;
398 
399  static bool bDoTorque = true;
400  if (bDoTorque && avgAxis.length2() > SIMD_EPSILON)
401  {
402  avgAxis.normalize();
403  kAxisAInv = getRigidBodyA().computeAngularImpulseDenominator(avgAxis);
404  kAxisBInv = getRigidBodyB().computeAngularImpulseDenominator(avgAxis);
405  btScalar kInvCombined = kAxisAInv + kAxisBInv;
406 
407  btVector3 impulse = (kAxisAInv * dOmegaA - kAxisBInv * dOmegaB) /
408  (kInvCombined * kInvCombined);
409 
410  if (m_maxMotorImpulse >= 0)
411  {
412  btScalar fMaxImpulse = m_maxMotorImpulse;
414  fMaxImpulse = fMaxImpulse/kAxisAInv;
415 
416  btVector3 newUnclampedAccImpulse = m_accMotorImpulse + impulse;
417  btScalar newUnclampedMag = newUnclampedAccImpulse.length();
418  if (newUnclampedMag > fMaxImpulse)
419  {
420  newUnclampedAccImpulse.normalize();
421  newUnclampedAccImpulse *= fMaxImpulse;
422  impulse = newUnclampedAccImpulse - m_accMotorImpulse;
423  }
424  m_accMotorImpulse += impulse;
425  }
426 
427  btScalar impulseMag = impulse.length();
428  btVector3 impulseAxis = impulse / impulseMag;
429 
430  bodyA.internalApplyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*impulseAxis, impulseMag);
431  bodyB.internalApplyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*impulseAxis, -impulseMag);
432 
433  }
434  }
435  else if (m_damping > SIMD_EPSILON) // no motor: do a little damping
436  {
437  btVector3 angVelA; bodyA.internalGetAngularVelocity(angVelA);
438  btVector3 angVelB; bodyB.internalGetAngularVelocity(angVelB);
439  btVector3 relVel = angVelB - angVelA;
440  if (relVel.length2() > SIMD_EPSILON)
441  {
442  btVector3 relVelAxis = relVel.normalized();
443  btScalar m_kDamping = btScalar(1.) /
446  btVector3 impulse = m_damping * m_kDamping * relVel;
447 
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);
452  }
453  }
454 
455  // joint limits
456  {
458  btVector3 angVelA;
459  bodyA.internalGetAngularVelocity(angVelA);
460  btVector3 angVelB;
461  bodyB.internalGetAngularVelocity(angVelB);
462 
463  // solve swing limit
464  if (m_solveSwingLimit)
465  {
467  btScalar relSwingVel = (angVelB - angVelA).dot(m_swingAxis);
468  if (relSwingVel > 0)
469  amplitude += m_swingLimitRatio * relSwingVel * m_relaxationFactor;
470  btScalar impulseMag = amplitude * m_kSwing;
471 
472  // Clamp the accumulated impulse
475  impulseMag = m_accSwingLimitImpulse - temp;
476 
477  btVector3 impulse = m_swingAxis * impulseMag;
478 
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)
481  {
482  btVector3 impulseTwistCouple = impulse.dot(m_twistAxisA) * m_twistAxisA;
483  btVector3 impulseNoTwistCouple = impulse - impulseTwistCouple;
484  impulse = impulseNoTwistCouple;
485  }
486 
487  impulseMag = impulse.length();
488  btVector3 noTwistSwingAxis = impulse / impulseMag;
489 
490  bodyA.internalApplyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*noTwistSwingAxis, impulseMag);
491  bodyB.internalApplyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*noTwistSwingAxis, -impulseMag);
492  }
493 
494 
495  // solve twist limit
496  if (m_solveTwistLimit)
497  {
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;
503 
504  // Clamp the accumulated impulse
507  impulseMag = m_accTwistLimitImpulse - temp;
508 
509  // btVector3 impulse = m_twistAxis * impulseMag;
510 
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);
513  }
514  }
515  }
516 #else
517 btAssert(0);
518 #endif //__SPU__
519 }
520 
521 
522 
523 
525 {
526  (void)timeStep;
527 
528 }
529 
530 
531 #ifndef __SPU__
533 {
536  m_solveTwistLimit = false;
537  m_solveSwingLimit = false;
538 
539  btVector3 b1Axis1,b1Axis2,b1Axis3;
540  btVector3 b2Axis1,b2Axis2;
541 
544 
545  btScalar swing1=btScalar(0.),swing2 = btScalar(0.);
546 
547  btScalar swx=btScalar(0.),swy = btScalar(0.);
548  btScalar thresh = btScalar(10.);
549  btScalar fact;
550 
551  // Get Frame into world space
552  if (m_swingSpan1 >= btScalar(0.05f))
553  {
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));
560  swing1 *= fact;
561  }
562 
563  if (m_swingSpan2 >= btScalar(0.05f))
564  {
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));
571  swing2 *= fact;
572  }
573 
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;
577 
578  if (EllipseAngle > 1.0f)
579  {
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));
585  btScalar swingAxisSign = (b2Axis1.dot(b1Axis1) >= 0.0f) ? 1.0f : -1.0f;
586  m_swingAxis *= swingAxisSign;
587  }
588 
589  // Twist limits
590  if (m_twistSpan >= btScalar(0.))
591  {
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;
597 
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)
601  {
602  m_twistCorrection = -(twist + m_twistSpan);
603  m_solveTwistLimit = true;
604  m_twistAxis = (b2Axis1 + b1Axis1) * 0.5f;
606  m_twistAxis *= -1.0f;
607  }
608  else if (twist > m_twistSpan*lockedFreeFactor)
609  {
610  m_twistCorrection = (twist - m_twistSpan);
611  m_solveTwistLimit = true;
612  m_twistAxis = (b2Axis1 + b1Axis1) * 0.5f;
614  }
615  }
616 }
617 #endif //__SPU__
618 
619 static btVector3 vTwist(1,0,0); // twist axis in constraint's space
620 
621 
622 
623 void btConeTwistConstraint::calcAngleInfo2(const btTransform& transA, const btTransform& transB, const btMatrix3x3& invInertiaWorldA,const btMatrix3x3& invInertiaWorldB)
624 {
627  m_solveTwistLimit = false;
628  m_solveSwingLimit = false;
629  // compute rotation of A wrt B (in constraint space)
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))
643  {
644  return;
645  }
646  m_swingAxis = swingAxis;
648  m_swingCorrection = qDeltaAB.getAngle();
650  {
651  m_solveSwingLimit = true;
652  }
653  return;
654  }
655 
656 
657  {
658  // compute rotation of A wrt B (in constraint space)
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();
667 
669  {
670  btScalar swingAngle, swingLimit = 0; btVector3 swingAxis;
671  computeConeLimitInfo(qABCone, swingAngle, swingAxis, swingLimit);
672 
673  if (swingAngle > swingLimit * m_limitSoftness)
674  {
675  m_solveSwingLimit = true;
676 
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)
682  {
683  m_swingLimitRatio = (swingAngle - swingLimit * m_limitSoftness)/
684  (swingLimit - swingLimit * m_limitSoftness);
685  }
686 
687  // swing correction tries to get back to soft limit
688  m_swingCorrection = swingAngle - (swingLimit * m_limitSoftness);
689 
690  // adjustment of swing axis (based on ellipse normal)
692 
693  // Calculate necessary axis & factors
694  m_swingAxis = quatRotate(qB, -swingAxis);
695 
696  m_twistAxisA.setValue(0,0,0);
697 
698  m_kSwing = btScalar(1.) /
699  (computeAngularImpulseDenominator(m_swingAxis,invInertiaWorldA) +
701  }
702  }
703  else
704  {
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);
712  btVector3 target;
713  btScalar x = ivB.dot(ivA);
714  btScalar y = ivB.dot(jvA);
715  btScalar z = ivB.dot(kvA);
717  { // fixed. We'll need to add one more row to constraint
718  if((!btFuzzyZero(y)) || (!(btFuzzyZero(z))))
719  {
720  m_solveSwingLimit = true;
721  m_swingAxis = -ivB.cross(ivA);
722  }
723  }
724  else
725  {
727  { // hinge around Y axis
728  if(!(btFuzzyZero(y)))
729  {
730  m_solveSwingLimit = true;
732  {
733  y = btScalar(0.f);
734  btScalar span2 = btAtan2(z, x);
735  if(span2 > m_swingSpan2)
736  {
737  x = btCos(m_swingSpan2);
738  z = btSin(m_swingSpan2);
739  }
740  else if(span2 < -m_swingSpan2)
741  {
742  x = btCos(m_swingSpan2);
743  z = -btSin(m_swingSpan2);
744  }
745  }
746  }
747  }
748  else
749  { // hinge around Z axis
750  if(!btFuzzyZero(z))
751  {
752  m_solveSwingLimit = true;
754  {
755  z = btScalar(0.f);
756  btScalar span1 = btAtan2(y, x);
757  if(span1 > m_swingSpan1)
758  {
759  x = btCos(m_swingSpan1);
760  y = btSin(m_swingSpan1);
761  }
762  else if(span1 < -m_swingSpan1)
763  {
764  x = btCos(m_swingSpan1);
765  y = -btSin(m_swingSpan1);
766  }
767  }
768  }
769  }
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];
773  target.normalize();
774  m_swingAxis = -ivB.cross(target);
777  }
778  }
779 
780  if (m_twistSpan >= btScalar(0.f))
781  {
782  btVector3 twistAxis;
783  computeTwistLimitInfo(qABTwist, m_twistAngle, twistAxis);
784 
786  {
787  m_solveTwistLimit = true;
788 
789  m_twistLimitRatio = 1.f;
790  if (m_twistAngle < m_twistSpan && m_limitSoftness < 1.f - SIMD_EPSILON)
791  {
793  (m_twistSpan - m_twistSpan * m_limitSoftness);
794  }
795 
796  // twist correction tries to get back to soft limit
798 
799  m_twistAxis = quatRotate(qB, -twistAxis);
800 
801  m_kTwist = btScalar(1.) /
802  (computeAngularImpulseDenominator(m_twistAxis,invInertiaWorldA) +
803  computeAngularImpulseDenominator(m_twistAxis,invInertiaWorldB));
804  }
805 
806  if (m_solveSwingLimit)
807  m_twistAxisA = quatRotate(qA, -twistAxis);
808  }
809  else
810  {
811  m_twistAngle = btScalar(0.f);
812  }
813  }
814 }
815 
816 
817 
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".
822  btScalar& swingAngle, // out
823  btVector3& vSwingAxis, // out
824  btScalar& swingLimit) // out
825 {
826  swingAngle = qCone.getAngle();
827  if (swingAngle > SIMD_EPSILON)
828  {
829  vSwingAxis = btVector3(qCone.x(), qCone.y(), qCone.z());
830  vSwingAxis.normalize();
831 #if 0
832  // non-zero twist?! this should never happen.
833  btAssert(fabs(vSwingAxis.x()) <= SIMD_EPSILON));
834 #endif
835 
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.)
839 
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();
845 
846  // Now, we use the slope of the vector (using x/yEllipse) and find the length
847  // of the line that intersects the ellipse:
848  // x^2 y^2
849  // --- + --- = 1, where a and b are semi-major axes 2 and 1 respectively (ie. the limits)
850  // a^2 b^2
851  // Do the math and it should be clear.
852 
853  swingLimit = m_swingSpan1; // if xEllipse == 0, we have a pure vSwingAxis.z rotation: just use swingspan1
854  if (fabs(xEllipse) > SIMD_EPSILON)
855  {
856  btScalar surfaceSlope2 = (yEllipse*yEllipse)/(xEllipse*xEllipse);
858  norm += surfaceSlope2 / (m_swingSpan1 * m_swingSpan1);
859  btScalar swingLimit2 = (1 + surfaceSlope2) / norm;
860  swingLimit = sqrt(swingLimit2);
861  }
862 
863  // test!
864  /*swingLimit = m_swingSpan2;
865  if (fabs(vSwingAxis.z()) > SIMD_EPSILON)
866  {
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;
874  }*/
875  }
876  else if (swingAngle < 0)
877  {
878  // this should never happen!
879 #if 0
880  btAssert(0);
881 #endif
882  }
883 }
884 
886 {
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);
890 
891  // Use the slope of the vector (using x/yEllipse) and find the length
892  // of the line that intersects the ellipse:
893  // x^2 y^2
894  // --- + --- = 1, where a and b are semi-major axes 2 and 1 respectively (ie. the limits)
895  // a^2 b^2
896  // Do the math and it should be clear.
897 
898  float swingLimit = m_swingSpan1; // if xEllipse == 0, just use axis b (1)
899  if (fabs(xEllipse) > SIMD_EPSILON)
900  {
901  btScalar surfaceSlope2 = (yEllipse*yEllipse)/(xEllipse*xEllipse);
903  norm += surfaceSlope2 / (m_swingSpan1 * m_swingSpan1);
904  btScalar swingLimit2 = (1 + surfaceSlope2) / norm;
905  swingLimit = sqrt(swingLimit2);
906  }
907 
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);
914 }
915 
916 // given a twist rotation in constraint space, (pre: cone must already be removed)
917 // this method computes its corresponding angle and axis.
919  btScalar& twistAngle, // out
920  btVector3& vTwistAxis) // out
921 {
922  btQuaternion qMinTwist = qTwist;
923  twistAngle = qTwist.getAngle();
924 
925  if (twistAngle > SIMD_PI) // long way around. flip quat and recalculate.
926  {
927  qMinTwist = -(qTwist);
928  twistAngle = qMinTwist.getAngle();
929  }
930  if (twistAngle < 0)
931  {
932  // this should never happen
933 #if 0
934  btAssert(0);
935 #endif
936  }
937 
938  vTwistAxis = btVector3(qMinTwist.x(), qMinTwist.y(), qMinTwist.z());
939  if (twistAngle > SIMD_EPSILON)
940  vTwistAxis.normalize();
941 }
942 
943 
945 {
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.
950 
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();
955 
956  // do the math...
957  if (fabs(z) > SIMD_EPSILON) // avoid division by 0. and we don't need an update if z == 0.
958  {
959  // compute gradient/normal of ellipse surface at current "point"
960  btScalar grad = y/z;
961  grad *= m_swingSpan2 / m_swingSpan1;
962 
963  // adjust y/z to represent normal at point (instead of vector to point)
964  if (y > 0)
965  y = fabs(grad * z);
966  else
967  y = -fabs(grad * z);
968 
969  // convert ellipse direction back to swing axis
970  vSwingAxis.setZ(-y);
971  vSwingAxis.setY( z);
972  vSwingAxis.normalize();
973  }
974 }
975 
976 
977 
979 {
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();
986 
988  setMotorTargetInConstraintSpace(qConstraint);
989 }
990 
991 
993 {
994  m_qTarget = q;
995 
996  // clamp motor target to within limits
997  {
998  btScalar softness = 1.f;//m_limitSoftness;
999 
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();
1004 
1005  // clamp cone
1006  if (m_swingSpan1 >= btScalar(0.05f) && m_swingSpan2 >= btScalar(0.05f))
1007  {
1008  btScalar swingAngle, swingLimit; btVector3 swingAxis;
1009  computeConeLimitInfo(qTargetCone, swingAngle, swingAxis, swingLimit);
1010 
1011  if (fabs(swingAngle) > SIMD_EPSILON)
1012  {
1013  if (swingAngle > swingLimit*softness)
1014  swingAngle = swingLimit*softness;
1015  else if (swingAngle < -swingLimit*softness)
1016  swingAngle = -swingLimit*softness;
1017  qTargetCone = btQuaternion(swingAxis, swingAngle);
1018  }
1019  }
1020 
1021  // clamp twist
1022  if (m_twistSpan >= btScalar(0.05f))
1023  {
1024  btScalar twistAngle; btVector3 twistAxis;
1025  computeTwistLimitInfo(qTargetTwist, twistAngle, twistAxis);
1026 
1027  if (fabs(twistAngle) > SIMD_EPSILON)
1028  {
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);
1035  }
1036  }
1037 
1038  m_qTarget = qTargetCone * qTargetTwist;
1039  }
1040 }
1041 
1044 void btConeTwistConstraint::setParam(int num, btScalar value, int axis)
1045 {
1046  switch(num)
1047  {
1048  case BT_CONSTRAINT_ERP :
1049  case BT_CONSTRAINT_STOP_ERP :
1050  if((axis >= 0) && (axis < 3))
1051  {
1052  m_linERP = value;
1054  }
1055  else
1056  {
1057  m_biasFactor = value;
1058  }
1059  break;
1060  case BT_CONSTRAINT_CFM :
1061  case BT_CONSTRAINT_STOP_CFM :
1062  if((axis >= 0) && (axis < 3))
1063  {
1064  m_linCFM = value;
1066  }
1067  else
1068  {
1069  m_angCFM = value;
1071  }
1072  break;
1073  default:
1075  break;
1076  }
1077 }
1078 
1081 {
1082  btScalar retVal = 0;
1083  switch(num)
1084  {
1085  case BT_CONSTRAINT_ERP :
1086  case BT_CONSTRAINT_STOP_ERP :
1087  if((axis >= 0) && (axis < 3))
1088  {
1090  retVal = m_linERP;
1091  }
1092  else if((axis >= 3) && (axis < 6))
1093  {
1094  retVal = m_biasFactor;
1095  }
1096  else
1097  {
1099  }
1100  break;
1101  case BT_CONSTRAINT_CFM :
1102  case BT_CONSTRAINT_STOP_CFM :
1103  if((axis >= 0) && (axis < 3))
1104  {
1106  retVal = m_linCFM;
1107  }
1108  else if((axis >= 3) && (axis < 6))
1109  {
1111  retVal = m_angCFM;
1112  }
1113  else
1114  {
1116  }
1117  break;
1118  default :
1120  }
1121  return retVal;
1122 }
1123 
1124 
1125 void btConeTwistConstraint::setFrames(const btTransform & frameA, const btTransform & frameB)
1126 {
1127  m_rbAFrame = frameA;
1128  m_rbBFrame = frameB;
1129  buildJacobian();
1130  //calculateTransforms();
1131 }
1132 
1133 
1134 
1135