2 Bullet Continuous Collision Detection and Physics Library
3 Copyright (c) 2018 Erwin Coumans http://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.
16 ///This file was written by Erwin Coumans
18 #include "btMultiBodySphericalJointLimit.h"
19 #include "btMultiBody.h"
20 #include "btMultiBodyLinkCollider.h"
21 #include "BulletCollision/CollisionDispatch/btCollisionObject.h"
22 #include "LinearMath/btTransformUtil.h"
23 #include "BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h"
24 #include "LinearMath/btIDebugDraw.h"
26 btMultiBodySphericalJointLimit::btMultiBodySphericalJointLimit(btMultiBody* body, int link,
30 btScalar maxAppliedImpulse)
31 : btMultiBodyConstraint(body, body, link, body->getLink(link).m_parent, 3, true, MULTIBODY_CONSTRAINT_SPHERICAL_LIMIT),
32 m_desiredVelocity(0, 0, 0),
33 m_desiredPosition(0,0,0,1),
34 m_use_multi_dof_params(false),
38 m_rhsClamp(SIMD_INFINITY),
39 m_maxAppliedImpulseMultiDof(maxAppliedImpulse, maxAppliedImpulse, maxAppliedImpulse),
40 m_pivotA(m_bodyA->getLink(link).m_eVector),
41 m_pivotB(m_bodyB->getLink(link).m_eVector),
42 m_swingxRange(swingxRange),
43 m_swingyRange(swingyRange),
44 m_twistRange(twistRange)
48 m_maxAppliedImpulse = maxAppliedImpulse;
52 void btMultiBodySphericalJointLimit::finalizeMultiDof()
54 allocateJacobiansMultiDof();
55 // note: we rely on the fact that data.m_jacobians are
56 // always initialized to zero by the Constraint ctor
58 unsigned int offset = 6 + (m_bodyA->getLink(m_linkA).m_dofOffset + linkDoF);
60 // row 0: the lower bound
61 // row 0: the lower bound
62 jacobianA(0)[offset] = 1;
64 jacobianB(1)[offset] = -1;
66 m_numDofsFinalized = m_jacSizeBoth;
70 btMultiBodySphericalJointLimit::~btMultiBodySphericalJointLimit()
74 int btMultiBodySphericalJointLimit::getIslandIdA() const
76 if (this->m_linkA < 0)
78 btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider();
80 return col->getIslandTag();
84 if (m_bodyA->getLink(m_linkA).m_collider)
86 return m_bodyA->getLink(m_linkA).m_collider->getIslandTag();
92 int btMultiBodySphericalJointLimit::getIslandIdB() const
96 btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider();
98 return col->getIslandTag();
102 if (m_bodyB->getLink(m_linkB).m_collider)
104 return m_bodyB->getLink(m_linkB).m_collider->getIslandTag();
110 void btMultiBodySphericalJointLimit::createConstraintRows(btMultiBodyConstraintArray& constraintRows,
111 btMultiBodyJacobianData& data,
112 const btContactSolverInfo& infoGlobal)
114 // only positions need to be updated -- data.m_jacobians and force
115 // directions were set in the ctor and never change.
117 if (m_numDofsFinalized != m_jacSizeBoth)
123 if (m_numDofsFinalized != m_jacSizeBoth)
127 if (m_maxAppliedImpulse == 0.f)
130 const btScalar posError = 0;
131 const btVector3 zero(0, 0, 0);
134 btVector3 axis[3] = { btVector3(1, 0, 0), btVector3(0, 1, 0), btVector3(0, 0, 1) };
136 btQuaternion currentQuat(m_bodyA->getJointPosMultiDof(m_linkA)[0],
137 m_bodyA->getJointPosMultiDof(m_linkA)[1],
138 m_bodyA->getJointPosMultiDof(m_linkA)[2],
139 m_bodyA->getJointPosMultiDof(m_linkA)[3]);
141 btQuaternion refQuat = m_desiredPosition;
142 btVector3 vTwist(0,0,1);
144 btVector3 vConeNoTwist = quatRotate(currentQuat, vTwist);
145 vConeNoTwist.normalize();
146 btQuaternion qABCone = shortestArcQuat(vTwist, vConeNoTwist);
148 btQuaternion qABTwist = qABCone.inverse() * currentQuat;
149 qABTwist.normalize();
150 btQuaternion desiredQuat = qABTwist;
153 btQuaternion relRot = currentQuat.inverse() * desiredQuat;
155 btGeneric6DofSpring2Constraint::matrixToEulerXYZ(btMatrix3x3(relRot), angleDiff);
157 btScalar limitRanges[3] = {m_swingxRange, m_swingyRange, m_twistRange};
160 btQuaternion qMinTwist = qABTwist;
161 btScalar twistAngle = qABTwist.getAngle();
163 if (twistAngle > SIMD_PI) // long way around. flip quat and recalculate.
165 qMinTwist = -(qABTwist);
166 twistAngle = qMinTwist.getAngle();
168 btVector3 vTwistAxis = btVector3(qMinTwist.x(), qMinTwist.y(), qMinTwist.z());
169 if (twistAngle > SIMD_EPSILON)
170 vTwistAxis.normalize();
172 if (vTwistAxis.dot(vTwist)<0)
175 angleDiff[2] = twistAngle;
178 for (int row = 0; row < getNumRows(); row++)
180 btScalar allowed = limitRanges[row];
182 if((angleDiff[row]>-allowed)&&(angleDiff[row]<allowed))
189 if (angleDiff[row]>allowed)
191 angleDiff[row]-=allowed;
194 if (angleDiff[row]<-allowed)
196 angleDiff[row]+=allowed;
204 btScalar currentVelocity = m_bodyA->getJointVelMultiDof(m_linkA)[dof];
205 btScalar desiredVelocity = this->m_desiredVelocity[row];
207 double kd = m_use_multi_dof_params ? m_kd[row % 3] : m_kd[0];
208 btScalar velocityError = (desiredVelocity - currentVelocity) * kd;
210 btMatrix3x3 frameAworld;
211 frameAworld.setIdentity();
212 frameAworld = m_bodyA->localFrameToWorld(m_linkA, frameAworld);
213 btScalar posError = 0;
215 btAssert(m_bodyA->getLink(m_linkA).m_jointType == btMultibodyLink::eSpherical);
216 switch (m_bodyA->getLink(m_linkA).m_jointType)
218 case btMultibodyLink::eSpherical:
220 btVector3 constraintNormalAng = frameAworld.getColumn(row % 3);
221 double kp = m_use_multi_dof_params ? m_kp[row % 3] : m_kp[0];
222 posError = kp*angleDiff[row % 3];
223 double max_applied_impulse = m_use_multi_dof_params ? m_maxAppliedImpulseMultiDof[row % 3] : m_maxAppliedImpulse;
224 //should multiply by time step
225 //max_applied_impulse *= infoGlobal.m_timeStep
227 double min_applied_impulse = -max_applied_impulse;
231 max_applied_impulse=0;
233 min_applied_impulse=0;
235 if (btFabs(posError)>SIMD_EPSILON)
237 btMultiBodySolverConstraint& constraintRow = constraintRows.expandNonInitializing();
238 fillMultiBodyConstraint(constraintRow, data, 0, 0, constraintNormalAng,
239 zero, zero, zero,//pure angular, so zero out linear parts
242 min_applied_impulse, max_applied_impulse, true,
245 constraintRow.m_orgConstraint = this;
246 constraintRow.m_orgDofIndex = row;
260 void btMultiBodySphericalJointLimit::debugDraw(class btIDebugDraw* drawer)
266 btVector3 pivotBworld = m_bodyB->localPosToWorld(m_linkB, m_pivotB);
267 tr.setOrigin(pivotBworld);
268 drawer->drawTransform(tr, 0.1);