rigid.cpp 11 KB

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