rigid.cpp

Engine/source/T3D/rigid.cpp

More...

Detailed Description

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "T3D/rigid.h"
 25#include "console/console.h"
 26
 27
 28//----------------------------------------------------------------------------
 29
 30Rigid::Rigid()
 31{
 32   force.set(0.0f,0.0f,0.0f);
 33   torque.set(0.0f,0.0f,0.0f);
 34   linVelocity.set(0.0f,0.0f,0.0f);
 35   linPosition.set(0.0f,0.0f,0.0f);
 36   linMomentum.set(0.0f,0.0f,0.0f);
 37   angVelocity.set(0.0f,0.0f,0.0f);
 38   angMomentum.set(0.0f,0.0f,0.0f);
 39   
 40   angPosition.identity();
 41   invWorldInertia.identity();
 42
 43   centerOfMass.set(0.0f,0.0f,0.0f);
 44   worldCenterOfMass = linPosition;
 45   mass = oneOverMass = 1.0f;
 46   invObjectInertia.identity();
 47   restitution = 0.3f;
 48   friction = 0.5f;
 49   atRest = false;
 50}
 51
 52void Rigid::clearForces()
 53{
 54   force.set(0.0f,0.0f,0.0f);
 55   torque.set(0.0f,0.0f,0.0f);
 56}
 57
 58//-----------------------------------------------------------------------------
 59void Rigid::integrate(F32 delta)
 60{
 61   // Update Angular position
 62   F32 angle = angVelocity.len();
 63   if (angle != 0.0f) {
 64      QuatF dq;
 65      F32 sinHalfAngle;
 66      mSinCos(angle * delta * -0.5f, sinHalfAngle, dq.w);
 67      sinHalfAngle *= 1.0f / angle;
 68      dq.x = angVelocity.x * sinHalfAngle;
 69      dq.y = angVelocity.y * sinHalfAngle;
 70      dq.z = angVelocity.z * sinHalfAngle;
 71      QuatF tmp = angPosition;
 72      angPosition.mul(tmp, dq);
 73      angPosition.normalize();
 74
 75      // Rotate the position around the center of mass
 76      Point3F lp = linPosition - worldCenterOfMass;
 77      dq.mulP(lp,&linPosition);
 78      linPosition += worldCenterOfMass;
 79   }
 80
 81   // Update angular momentum
 82   angMomentum = angMomentum + torque * delta;
 83
 84   // Update linear position, momentum
 85   linPosition = linPosition + linVelocity * delta;
 86   linMomentum = linMomentum + force * delta;
 87   linVelocity = linMomentum * oneOverMass;
 88
 89   // Update dependent state variables
 90   updateInertialTensor();
 91   updateVelocity();
 92   updateCenterOfMass();
 93}
 94
 95void Rigid::updateVelocity()
 96{
 97   linVelocity.x = linMomentum.x * oneOverMass;
 98   linVelocity.y = linMomentum.y * oneOverMass;
 99   linVelocity.z = linMomentum.z * oneOverMass;
100   invWorldInertia.mulV(angMomentum,&angVelocity);
101}
102
103void Rigid::updateInertialTensor()
104{
105   MatrixF iv,qmat;
106   angPosition.setMatrix(&qmat);
107   iv.mul(qmat,invObjectInertia);
108   qmat.transpose();
109   invWorldInertia.mul(iv,qmat);
110}
111
112void Rigid::updateCenterOfMass()
113{
114   // Move the center of mass into world space
115   angPosition.mulP(centerOfMass,&worldCenterOfMass);
116   worldCenterOfMass += linPosition;
117}
118
119void Rigid::applyImpulse(const Point3F &r, const Point3F &impulse)
120{
121   atRest = false;
122
123   // Linear momentum and velocity
124   linMomentum  += impulse;
125   linVelocity.x = linMomentum.x * oneOverMass;
126   linVelocity.y = linMomentum.y * oneOverMass;
127   linVelocity.z = linMomentum.z * oneOverMass;
128
129   // Rotational momentum and velocity
130   Point3F tv;
131   mCross(r,impulse,&tv);
132   angMomentum += tv;
133   invWorldInertia.mulV(angMomentum, &angVelocity);
134}
135
136//-----------------------------------------------------------------------------
137/** Resolve collision with another rigid body
138   Computes & applies the collision impulses needed to keep the bodies
139   from interpenetrating.
140
141   tg: This function was commented out... I uncommented it, but haven't
142   double checked the math.
143*/
144bool Rigid::resolveCollision(const Point3F& p, const Point3F &normal, Rigid* rigid)
145{
146   atRest = false;
147   Point3F v1,v2,r1,r2;
148   getOriginVector(p,&r1);
149   getVelocity(r1,&v1);
150   rigid->getOriginVector(p,&r2);
151   rigid->getVelocity(r2,&v2);
152
153   // Make sure they are converging
154   F32 nv = mDot(v1,normal);
155   nv -= mDot(v2,normal);
156   if (nv > 0.0f)
157      return false;
158
159   // Compute impulse
160   F32 d, n = -nv * (2.0f + restitution * rigid->restitution);
161   Point3F a1,b1,c1;
162   mCross(r1,normal,&a1);
163   invWorldInertia.mulV(a1,&b1);
164   mCross(b1,r1,&c1);
165
166   Point3F a2,b2,c2;
167   mCross(r2,normal,&a2);
168   rigid->invWorldInertia.mulV(a2,&b2);
169   mCross(b2,r2,&c2);
170
171   Point3F c3 = c1 + c2;
172   d = oneOverMass + rigid->oneOverMass + mDot(c3,normal);
173   Point3F impulse = normal * (n / d);
174
175   applyImpulse(r1,impulse);
176   impulse.neg();
177   rigid->applyImpulse(r2, impulse);
178   return true;
179}
180
181//-----------------------------------------------------------------------------
182/** Resolve collision with an immovable object
183   Computes & applies the collision impulse needed to keep the body
184   from penetrating the given surface.
185*/
186bool Rigid::resolveCollision(const Point3F& p, const Point3F &normal)
187{
188   atRest = false;
189   Point3F v,r;
190   getOriginVector(p,&r);
191   getVelocity(r,&v);
192   F32 n = -mDot(v,normal);
193   if (n >= 0.0f) {
194
195      // Collision impulse, straight forward force stuff.
196      F32 d = getZeroImpulse(r,normal);
197      F32 j = n * (1.0f + restitution) * d;
198      Point3F impulse = normal * j;
199
200      // Friction impulse, calculated as a function of the
201      // amount of force it would take to stop the motion
202      // perpendicular to the normal.
203      Point3F uv = v + (normal * n);
204      F32 ul = uv.len();
205      if (ul) {
206         uv /= -ul;
207         F32 u = ul * getZeroImpulse(r,uv);
208         j *= friction;
209         if (u > j)
210            u = j;
211         impulse += uv * u;
212      }
213
214      //
215      applyImpulse(r,impulse);
216   }
217   return true;
218}
219
220//-----------------------------------------------------------------------------
221/** Calculate the inertia along the given vector
222   This function can be used to calculate the amount of force needed to
223   affect a change in velocity along the specified normal applied at
224   the given point.
225*/
226F32 Rigid::getZeroImpulse(const Point3F& r,const Point3F& normal)
227{
228   Point3F a,b,c;
229   mCross(r,normal,&a);
230   invWorldInertia.mulV(a,&b);
231   mCross(b,r,&c);
232   return 1 / (oneOverMass + mDot(c,normal));
233}
234
235F32 Rigid::getKineticEnergy()
236{
237   Point3F w;
238   QuatF qmat = angPosition;
239   qmat.inverse();
240   qmat.mulP(angVelocity,&w);
241   const F32* f = invObjectInertia;
242   return 0.5f * ((mass * mDot(linVelocity,linVelocity)) +
243      w.x * w.x / f[0] +
244      w.y * w.y / f[5] +
245      w.z * w.z / f[10]);
246}
247
248void Rigid::getOriginVector(const Point3F &p,Point3F* r)
249{
250   *r = p - worldCenterOfMass;
251}
252
253void Rigid::setCenterOfMass(const Point3F &newCenter)
254{
255   // Sets the center of mass relative to the origin.
256   centerOfMass = newCenter;
257
258   // Update world center of mass
259   angPosition.mulP(centerOfMass,&worldCenterOfMass);
260   worldCenterOfMass += linPosition;
261}
262
263void Rigid::translateCenterOfMass(const Point3F &oldPos,const Point3F &newPos)
264{
265   // I + mass * (crossmatrix(centerOfMass)^2 - crossmatrix(newCenter)^2)
266   MatrixF oldx,newx;
267   oldx.setCrossProduct(oldPos);
268   newx.setCrossProduct(newPos);
269   for (S32 row = 0; row < 3; row++)
270      for (S32 col = 0; col < 3; col++) {
271         F32 n = newx(row,col), o = oldx(row,col);
272         objectInertia(row,col) += mass * ((o * o) - (n * n));
273      }
274
275   // Make sure the matrix is symetrical
276   objectInertia(1,0) = objectInertia(0,1);
277   objectInertia(2,0) = objectInertia(0,2);
278   objectInertia(2,1) = objectInertia(1,2);
279}
280
281void Rigid::getVelocity(const Point3F& r, Point3F* v)
282{
283   mCross(angVelocity, r, v);
284   *v += linVelocity;
285}
286
287void Rigid::getTransform(MatrixF* mat)
288{
289   angPosition.setMatrix(mat);
290   mat->setColumn(3,linPosition);
291}
292
293void Rigid::setTransform(const MatrixF& mat)
294{
295   angPosition.set(mat);
296   mat.getColumn(3,&linPosition);
297
298   // Update center of mass
299   angPosition.mulP(centerOfMass,&worldCenterOfMass);
300   worldCenterOfMass += linPosition;
301}
302
303
304//----------------------------------------------------------------------------
305/** Set the rigid body moment of inertia
306   The moment is calculated as a box with the given dimensions.
307*/
308void Rigid::setObjectInertia(const Point3F& r)
309{
310   // Rotational moment of inertia of a box
311   F32 ot = mass / 12.0f;
312   F32 a = r.x * r.x;
313   F32 b = r.y * r.y;
314   F32 c = r.z * r.z;
315
316   objectInertia.identity();
317   F32* f = objectInertia;
318   f[0]  = ot * (b + c);
319   f[5]  = ot * (c + a);
320   f[10] = ot * (a + b);
321
322   invertObjectInertia();
323   updateInertialTensor();
324}
325
326
327//----------------------------------------------------------------------------
328/** Set the rigid body moment of inertia
329   The moment is calculated as a unit sphere.
330*/
331void Rigid::setObjectInertia()
332{
333   objectInertia.identity();
334   F32 radius = 1.0f;
335   F32* f = objectInertia;
336   f[0] = f[5] = f[10] = (0.4f * mass * radius * radius);
337   invertObjectInertia();
338   updateInertialTensor();
339}
340
341void Rigid::invertObjectInertia()
342{
343  invObjectInertia = objectInertia;
344  invObjectInertia.fullInverse();
345}
346
347
348//----------------------------------------------------------------------------
349
350bool Rigid::checkRestCondition()
351{
352//   F32 k = getKineticEnergy(mWorldToObj);
353//   F32 G = -force.z * oneOverMass * 0.032;
354//   F32 Kg = 0.5 * mRigid.mass * G * G;
355//   if (k < Kg * restTol)
356//      mRigid.setAtRest();
357   return atRest;
358}
359
360void Rigid::setAtRest()
361{
362   atRest = true;
363   linVelocity.set(0.0f,0.0f,0.0f);
364   linMomentum.set(0.0f,0.0f,0.0f);
365   angVelocity.set(0.0f,0.0f,0.0f);
366   angMomentum.set(0.0f,0.0f,0.0f);
367}
368