1 /********************************************************************************
2 * ReactPhysics3D physics library, http://www.reactphysics3d.com                 *
3 * Copyright (c) 2010-2020 Daniel Chappuis                                       *
4 *********************************************************************************
5 *                                                                               *
6 * This software is provided 'as-is', without any express or implied warranty.   *
7 * In no event will the authors be held liable for any damages arising from the  *
8 * use of this software.                                                         *
9 *                                                                               *
10 * Permission is granted to anyone to use this software for any purpose,         *
11 * including commercial applications, and to alter it and redistribute it        *
12 * freely, subject to the following restrictions:                                *
13 *                                                                               *
14 * 1. The origin of this software must not be misrepresented; you must not claim *
15 *    that you wrote the original software. If you use this software in a        *
16 *    product, an acknowledgment in the product documentation would be           *
17 *    appreciated but is not required.                                           *
18 *                                                                               *
19 * 2. Altered source versions must be plainly marked as such, and must not be    *
20 *    misrepresented as being the original software.                             *
21 *                                                                               *
22 * 3. This notice may not be removed or altered from any source distribution.    *
23 *                                                                               *
24 ********************************************************************************/
25 
26 // Libraries
27 #include <reactphysics3d/systems/SolveBallAndSocketJointSystem.h>
28 #include <reactphysics3d/engine/PhysicsWorld.h>
29 #include <reactphysics3d/body/RigidBody.h>
30 
31 using namespace reactphysics3d;
32 
33 // Static variables definition
34 const decimal SolveBallAndSocketJointSystem::BETA = decimal(0.2);
35 
36 // Constructor
SolveBallAndSocketJointSystem(PhysicsWorld & world,RigidBodyComponents & rigidBodyComponents,TransformComponents & transformComponents,JointComponents & jointComponents,BallAndSocketJointComponents & ballAndSocketJointComponents)37 SolveBallAndSocketJointSystem::SolveBallAndSocketJointSystem(PhysicsWorld& world, RigidBodyComponents& rigidBodyComponents,
38                                                              TransformComponents& transformComponents,
39                                                              JointComponents& jointComponents,
40                                                              BallAndSocketJointComponents& ballAndSocketJointComponents)
41               :mWorld(world), mRigidBodyComponents(rigidBodyComponents), mTransformComponents(transformComponents),
42                mJointComponents(jointComponents), mBallAndSocketJointComponents(ballAndSocketJointComponents),
43                mTimeStep(0), mIsWarmStartingActive(true) {
44 
45 }
46 
47 // Initialize before solving the constraint
initBeforeSolve()48 void SolveBallAndSocketJointSystem::initBeforeSolve() {
49 
50     // For each joint
51     for (uint32 i=0; i < mBallAndSocketJointComponents.getNbEnabledComponents(); i++) {
52 
53         const Entity jointEntity = mBallAndSocketJointComponents.mJointEntities[i];
54 
55         // Get the bodies entities
56         const Entity body1Entity = mJointComponents.getBody1Entity(jointEntity);
57         const Entity body2Entity = mJointComponents.getBody2Entity(jointEntity);
58 
59         assert(!mRigidBodyComponents.getIsEntityDisabled(body1Entity));
60         assert(!mRigidBodyComponents.getIsEntityDisabled(body2Entity));
61 
62         // Get the inertia tensor of bodies
63         mBallAndSocketJointComponents.mI1[i] = RigidBody::getWorldInertiaTensorInverse(mWorld, body1Entity);
64         mBallAndSocketJointComponents.mI2[i] = RigidBody::getWorldInertiaTensorInverse(mWorld, body2Entity);
65     }
66 
67     // For each joint
68     for (uint32 i=0; i < mBallAndSocketJointComponents.getNbEnabledComponents(); i++) {
69 
70         const Entity jointEntity = mBallAndSocketJointComponents.mJointEntities[i];
71 
72         // Get the bodies entities
73         const Entity body1Entity = mJointComponents.getBody1Entity(jointEntity);
74         const Entity body2Entity = mJointComponents.getBody2Entity(jointEntity);
75 
76         const Quaternion& orientationBody1 = mTransformComponents.getTransform(body1Entity).getOrientation();
77         const Quaternion& orientationBody2 = mTransformComponents.getTransform(body2Entity).getOrientation();
78 
79         // Compute the vector from body center to the anchor point in world-space
80         mBallAndSocketJointComponents.mR1World[i] = orientationBody1 * mBallAndSocketJointComponents.mLocalAnchorPointBody1[i];
81         mBallAndSocketJointComponents.mR2World[i] = orientationBody2 * mBallAndSocketJointComponents.mLocalAnchorPointBody2[i];
82     }
83 
84     // For each joint
85     for (uint32 i=0; i < mBallAndSocketJointComponents.getNbEnabledComponents(); i++) {
86 
87         const Entity jointEntity = mBallAndSocketJointComponents.mJointEntities[i];
88 
89         // Compute the corresponding skew-symmetric matrices
90         const Vector3& r1World = mBallAndSocketJointComponents.mR1World[i];
91         const Vector3& r2World = mBallAndSocketJointComponents.mR2World[i];
92         Matrix3x3 skewSymmetricMatrixU1 = Matrix3x3::computeSkewSymmetricMatrixForCrossProduct(r1World);
93         Matrix3x3 skewSymmetricMatrixU2 = Matrix3x3::computeSkewSymmetricMatrixForCrossProduct(r2World);
94 
95         // Get the bodies entities
96         const Entity body1Entity = mJointComponents.getBody1Entity(jointEntity);
97         const Entity body2Entity = mJointComponents.getBody2Entity(jointEntity);
98 
99         const uint32 componentIndexBody1 = mRigidBodyComponents.getEntityIndex(body1Entity);
100         const uint32 componentIndexBody2 = mRigidBodyComponents.getEntityIndex(body2Entity);
101 
102         // Compute the matrix K=JM^-1J^t (3x3 matrix)
103         const decimal body1MassInverse = mRigidBodyComponents.mInverseMasses[componentIndexBody1];
104         const decimal body2MassInverse = mRigidBodyComponents.mInverseMasses[componentIndexBody2];
105         const decimal inverseMassBodies =  body1MassInverse + body2MassInverse;
106         const Matrix3x3& i1 = mBallAndSocketJointComponents.mI1[i];
107         const Matrix3x3& i2 = mBallAndSocketJointComponents.mI2[i];
108         Matrix3x3 massMatrix = Matrix3x3(inverseMassBodies, 0, 0,
109                                         0, inverseMassBodies, 0,
110                                         0, 0, inverseMassBodies) +
111                                skewSymmetricMatrixU1 * i1 * skewSymmetricMatrixU1.getTranspose() +
112                                skewSymmetricMatrixU2 * i2 * skewSymmetricMatrixU2.getTranspose();
113 
114         // Compute the inverse mass matrix K^-1
115         mBallAndSocketJointComponents.mInverseMassMatrix[i].setToZero();
116         if (mRigidBodyComponents.mBodyTypes[componentIndexBody1] == BodyType::DYNAMIC ||
117             mRigidBodyComponents.mBodyTypes[componentIndexBody2] == BodyType::DYNAMIC) {
118             mBallAndSocketJointComponents.mInverseMassMatrix[i] = massMatrix.getInverse();
119         }
120     }
121 
122     const decimal biasFactor = (BETA / mTimeStep);
123 
124     // For each joint
125     for (uint32 i=0; i < mBallAndSocketJointComponents.getNbEnabledComponents(); i++) {
126 
127         const Entity jointEntity = mBallAndSocketJointComponents.mJointEntities[i];
128 
129         // Get the bodies entities
130         const Entity body1Entity = mJointComponents.getBody1Entity(jointEntity);
131         const Entity body2Entity = mJointComponents.getBody2Entity(jointEntity);
132 
133         const Vector3& r1World = mBallAndSocketJointComponents.mR1World[i];
134         const Vector3& r2World = mBallAndSocketJointComponents.mR2World[i];
135 
136         const Vector3& x1 = mRigidBodyComponents.getCenterOfMassWorld(body1Entity);
137         const Vector3& x2 = mRigidBodyComponents.getCenterOfMassWorld(body2Entity);
138 
139         // Compute the bias "b" of the constraint
140         mBallAndSocketJointComponents.mBiasVector[i].setToZero();
141         if (mJointComponents.getPositionCorrectionTechnique(jointEntity) == JointsPositionCorrectionTechnique::BAUMGARTE_JOINTS) {
142             mBallAndSocketJointComponents.mBiasVector[i] = biasFactor * (x2 + r2World - x1 - r1World);
143         }
144     }
145 
146     // If warm-starting is not enabled
147     if (!mIsWarmStartingActive) {
148 
149         // For each joint
150         for (uint32 i=0; i < mBallAndSocketJointComponents.getNbEnabledComponents(); i++) {
151 
152             // Reset the accumulated impulse
153             mBallAndSocketJointComponents.mImpulse[i].setToZero();
154         }
155     }
156 }
157 
158 // Warm start the constraint (apply the previous impulse at the beginning of the step)
warmstart()159 void SolveBallAndSocketJointSystem::warmstart() {
160 
161     // For each joint component
162     for (uint32 i=0; i < mBallAndSocketJointComponents.getNbEnabledComponents(); i++) {
163 
164         const Entity jointEntity = mBallAndSocketJointComponents.mJointEntities[i];
165 
166         const Entity body1Entity = mJointComponents.getBody1Entity(jointEntity);
167         const Entity body2Entity = mJointComponents.getBody2Entity(jointEntity);
168 
169         const uint32 componentIndexBody1 = mRigidBodyComponents.getEntityIndex(body1Entity);
170         const uint32 componentIndexBody2 = mRigidBodyComponents.getEntityIndex(body2Entity);
171 
172         // Get the velocities
173         Vector3& v1 = mRigidBodyComponents.mConstrainedLinearVelocities[componentIndexBody1];
174         Vector3& v2 = mRigidBodyComponents.mConstrainedLinearVelocities[componentIndexBody2];
175         Vector3& w1 = mRigidBodyComponents.mConstrainedAngularVelocities[componentIndexBody1];
176         Vector3& w2 = mRigidBodyComponents.mConstrainedAngularVelocities[componentIndexBody2];
177 
178         const Vector3& r1World = mBallAndSocketJointComponents.mR1World[i];
179         const Vector3& r2World = mBallAndSocketJointComponents.mR2World[i];
180 
181         const Matrix3x3& i1 = mBallAndSocketJointComponents.mI1[i];
182         const Matrix3x3& i2 = mBallAndSocketJointComponents.mI2[i];
183 
184         // Compute the impulse P=J^T * lambda for the body 1
185         const Vector3 linearImpulseBody1 = -mBallAndSocketJointComponents.mImpulse[i];
186         const Vector3 angularImpulseBody1 = mBallAndSocketJointComponents.mImpulse[i].cross(r1World);
187 
188         // Apply the impulse to the body 1
189         v1 += mRigidBodyComponents.mInverseMasses[componentIndexBody1] * linearImpulseBody1;
190         w1 += i1 * angularImpulseBody1;
191 
192         // Compute the impulse P=J^T * lambda for the body 2
193         const Vector3 angularImpulseBody2 = -mBallAndSocketJointComponents.mImpulse[i].cross(r2World);
194 
195         // Apply the impulse to the body to the body 2
196         v2 += mRigidBodyComponents.mInverseMasses[componentIndexBody2] * mBallAndSocketJointComponents.mImpulse[i];
197         w2 += i2 * angularImpulseBody2;
198     }
199 }
200 
201 // Solve the velocity constraint
solveVelocityConstraint()202 void SolveBallAndSocketJointSystem::solveVelocityConstraint() {
203 
204     // For each joint component
205     for (uint32 i=0; i < mBallAndSocketJointComponents.getNbEnabledComponents(); i++) {
206 
207         const Entity jointEntity = mBallAndSocketJointComponents.mJointEntities[i];
208 
209         const Entity body1Entity = mJointComponents.getBody1Entity(jointEntity);
210         const Entity body2Entity = mJointComponents.getBody2Entity(jointEntity);
211 
212         const uint32 componentIndexBody1 = mRigidBodyComponents.getEntityIndex(body1Entity);
213         const uint32 componentIndexBody2 = mRigidBodyComponents.getEntityIndex(body2Entity);
214 
215         // Get the velocities
216         Vector3& v1 = mRigidBodyComponents.mConstrainedLinearVelocities[componentIndexBody1];
217         Vector3& v2 = mRigidBodyComponents.mConstrainedLinearVelocities[componentIndexBody2];
218         Vector3& w1 = mRigidBodyComponents.mConstrainedAngularVelocities[componentIndexBody1];
219         Vector3& w2 = mRigidBodyComponents.mConstrainedAngularVelocities[componentIndexBody2];
220 
221         const Matrix3x3& i1 = mBallAndSocketJointComponents.mI1[i];
222         const Matrix3x3& i2 = mBallAndSocketJointComponents.mI2[i];
223 
224         // Compute J*v
225         const Vector3 Jv = v2 + w2.cross(mBallAndSocketJointComponents.mR2World[i]) - v1 - w1.cross(mBallAndSocketJointComponents.mR1World[i]);
226 
227         // Compute the Lagrange multiplier lambda
228         const Vector3 deltaLambda = mBallAndSocketJointComponents.mInverseMassMatrix[i] * (-Jv - mBallAndSocketJointComponents.mBiasVector[i]);
229         mBallAndSocketJointComponents.mImpulse[i] += deltaLambda;
230 
231         // Compute the impulse P=J^T * lambda for the body 1
232         const Vector3 linearImpulseBody1 = -deltaLambda;
233         const Vector3 angularImpulseBody1 = deltaLambda.cross(mBallAndSocketJointComponents.mR1World[i]);
234 
235         // Apply the impulse to the body 1
236         v1 += mRigidBodyComponents.mInverseMasses[componentIndexBody1] * linearImpulseBody1;
237         w1 += i1 * angularImpulseBody1;
238 
239         // Compute the impulse P=J^T * lambda for the body 2
240         const Vector3 angularImpulseBody2 = -deltaLambda.cross(mBallAndSocketJointComponents.mR2World[i]);
241 
242         // Apply the impulse to the body 2
243         v2 += mRigidBodyComponents.mInverseMasses[componentIndexBody2] * deltaLambda;
244         w2 += i2 * angularImpulseBody2;
245     }
246 }
247 
248 // Solve the position constraint (for position error correction)
solvePositionConstraint()249 void SolveBallAndSocketJointSystem::solvePositionConstraint() {
250 
251     // For each joint component
252     for (uint32 i=0; i < mBallAndSocketJointComponents.getNbEnabledComponents(); i++) {
253 
254         const Entity jointEntity = mBallAndSocketJointComponents.mJointEntities[i];
255 
256         // If the error position correction technique is not the non-linear-gauss-seidel, we do
257         // do not execute this method
258         if (mJointComponents.getPositionCorrectionTechnique(jointEntity) != JointsPositionCorrectionTechnique::NON_LINEAR_GAUSS_SEIDEL) continue;
259 
260         const Entity body1Entity = mJointComponents.getBody1Entity(jointEntity);
261         const Entity body2Entity = mJointComponents.getBody2Entity(jointEntity);
262 
263         // Recompute the inverse inertia tensors
264         mBallAndSocketJointComponents.mI1[i] = RigidBody::getWorldInertiaTensorInverse(mWorld, body1Entity);
265         mBallAndSocketJointComponents.mI2[i] = RigidBody::getWorldInertiaTensorInverse(mWorld, body2Entity);
266     }
267 
268     // For each joint component
269     for (uint32 i=0; i < mBallAndSocketJointComponents.getNbEnabledComponents(); i++) {
270 
271         const Entity jointEntity = mBallAndSocketJointComponents.mJointEntities[i];
272 
273         // If the error position correction technique is not the non-linear-gauss-seidel, we do
274         // do not execute this method
275         if (mJointComponents.getPositionCorrectionTechnique(jointEntity) != JointsPositionCorrectionTechnique::NON_LINEAR_GAUSS_SEIDEL) continue;
276 
277         const Entity body1Entity = mJointComponents.getBody1Entity(jointEntity);
278         const Entity body2Entity = mJointComponents.getBody2Entity(jointEntity);
279 
280         // Compute the vector from body center to the anchor point in world-space
281         mBallAndSocketJointComponents.mR1World[i] = mRigidBodyComponents.getConstrainedOrientation(body1Entity) *
282                                                     mBallAndSocketJointComponents.mLocalAnchorPointBody1[i];
283         mBallAndSocketJointComponents.mR2World[i] = mRigidBodyComponents.getConstrainedOrientation(body2Entity) *
284                                                     mBallAndSocketJointComponents.mLocalAnchorPointBody2[i];
285     }
286 
287     // For each joint component
288     for (uint32 i=0; i < mBallAndSocketJointComponents.getNbEnabledComponents(); i++) {
289 
290         const Entity jointEntity = mBallAndSocketJointComponents.mJointEntities[i];
291 
292         // If the error position correction technique is not the non-linear-gauss-seidel, we do
293         // do not execute this method
294         if (mJointComponents.getPositionCorrectionTechnique(jointEntity) != JointsPositionCorrectionTechnique::NON_LINEAR_GAUSS_SEIDEL) continue;
295 
296         const Entity body1Entity = mJointComponents.getBody1Entity(jointEntity);
297         const Entity body2Entity = mJointComponents.getBody2Entity(jointEntity);
298 
299         const uint32 componentIndexBody1 = mRigidBodyComponents.getEntityIndex(body1Entity);
300         const uint32 componentIndexBody2 = mRigidBodyComponents.getEntityIndex(body2Entity);
301 
302         const Vector3& r1World = mBallAndSocketJointComponents.mR1World[i];
303         const Vector3& r2World = mBallAndSocketJointComponents.mR2World[i];
304 
305         // Compute the corresponding skew-symmetric matrices
306         Matrix3x3 skewSymmetricMatrixU1 = Matrix3x3::computeSkewSymmetricMatrixForCrossProduct(r1World);
307         Matrix3x3 skewSymmetricMatrixU2 = Matrix3x3::computeSkewSymmetricMatrixForCrossProduct(r2World);
308 
309         // Get the inverse mass and inverse inertia tensors of the bodies
310         const decimal inverseMassBody1 = mRigidBodyComponents.mInverseMasses[componentIndexBody1];
311         const decimal inverseMassBody2 = mRigidBodyComponents.mInverseMasses[componentIndexBody2];
312 
313         // Recompute the inverse mass matrix K=J^TM^-1J of of the 3 translation constraints
314         decimal inverseMassBodies = inverseMassBody1 + inverseMassBody2;
315         Matrix3x3 massMatrix = Matrix3x3(inverseMassBodies, 0, 0,
316                                         0, inverseMassBodies, 0,
317                                         0, 0, inverseMassBodies) +
318                                skewSymmetricMatrixU1 * mBallAndSocketJointComponents.mI1[i] * skewSymmetricMatrixU1.getTranspose() +
319                                skewSymmetricMatrixU2 * mBallAndSocketJointComponents.mI2[i] * skewSymmetricMatrixU2.getTranspose();
320         mBallAndSocketJointComponents.mInverseMassMatrix[i].setToZero();
321         if (mRigidBodyComponents.mBodyTypes[componentIndexBody1] == BodyType::DYNAMIC ||
322             mRigidBodyComponents.mBodyTypes[componentIndexBody2] == BodyType::DYNAMIC) {
323             mBallAndSocketJointComponents.mInverseMassMatrix[i] = massMatrix.getInverse();
324         }
325     }
326 
327     // For each joint component
328     for (uint32 i=0; i < mBallAndSocketJointComponents.getNbEnabledComponents(); i++) {
329 
330         const Entity jointEntity = mBallAndSocketJointComponents.mJointEntities[i];
331 
332         // If the error position correction technique is not the non-linear-gauss-seidel, we do
333         // do not execute this method
334         if (mJointComponents.getPositionCorrectionTechnique(jointEntity) != JointsPositionCorrectionTechnique::NON_LINEAR_GAUSS_SEIDEL) continue;
335 
336         const Entity body1Entity = mJointComponents.getBody1Entity(jointEntity);
337         const Entity body2Entity = mJointComponents.getBody2Entity(jointEntity);
338 
339         const uint32 componentIndexBody1 = mRigidBodyComponents.getEntityIndex(body1Entity);
340         const uint32 componentIndexBody2 = mRigidBodyComponents.getEntityIndex(body2Entity);
341 
342         Vector3& x1 = mRigidBodyComponents.mConstrainedPositions[componentIndexBody1];
343         Vector3& x2 = mRigidBodyComponents.mConstrainedPositions[componentIndexBody2];
344 
345         const Vector3& r1World = mBallAndSocketJointComponents.mR1World[i];
346         const Vector3& r2World = mBallAndSocketJointComponents.mR2World[i];
347 
348         // Compute the constraint error (value of the C(x) function)
349         const Vector3 constraintError = (x2 + r2World - x1 - r1World);
350 
351         // Compute the Lagrange multiplier lambda
352         // TODO : Do not solve the system by computing the inverse each time and multiplying with the
353         //        right-hand side vector but instead use a method to directly solve the linear system.
354         const Vector3 lambda = mBallAndSocketJointComponents.mInverseMassMatrix[i] * (-constraintError);
355 
356         // Compute the impulse of body 1
357         const Vector3 linearImpulseBody1 = -lambda;
358         const Vector3 angularImpulseBody1 = lambda.cross(r1World);
359 
360         // Get the inverse mass and inverse inertia tensors of the bodies
361         const decimal inverseMassBody1 = mRigidBodyComponents.mInverseMasses[componentIndexBody1];
362         const decimal inverseMassBody2 = mRigidBodyComponents.mInverseMasses[componentIndexBody2];
363 
364         // Compute the pseudo velocity of body 1
365         const Vector3 v1 = inverseMassBody1 * linearImpulseBody1;
366         const Vector3 w1 = mBallAndSocketJointComponents.mI1[i] * angularImpulseBody1;
367 
368         Quaternion& q1 = mRigidBodyComponents.mConstrainedOrientations[componentIndexBody1];
369         Quaternion& q2 = mRigidBodyComponents.mConstrainedOrientations[componentIndexBody2];
370 
371         // Update the body center of mass and orientation of body 1
372         x1 += v1;
373         q1 += Quaternion(0, w1) * q1 * decimal(0.5);
374         q1.normalize();
375 
376         // Compute the impulse of body 2
377         const Vector3 angularImpulseBody2 = -lambda.cross(r2World);
378 
379         // Compute the pseudo velocity of body 2
380         const Vector3 v2 = inverseMassBody2 * lambda;
381         const Vector3 w2 = mBallAndSocketJointComponents.mI2[i] * angularImpulseBody2;
382 
383         // Update the body position/orientation of body 2
384         x2 += v2;
385         q2 += Quaternion(0, w2) * q2 * decimal(0.5);
386         q2.normalize();
387     }
388 }
389