RevoluteJoint.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. /*
  2. * Farseer Physics Engine based on Box2D.XNA port:
  3. * Copyright (c) 2010 Ian Qvist
  4. *
  5. * Box2D.XNA port of Box2D:
  6. * Copyright (c) 2009 Brandon Furtwangler, Nathan Furtwangler
  7. *
  8. * Original source Box2D:
  9. * Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
  10. *
  11. * This software is provided 'as-is', without any express or implied
  12. * warranty. In no event will the authors be held liable for any damages
  13. * arising from the use of this software.
  14. * Permission is granted to anyone to use this software for any purpose,
  15. * including commercial applications, and to alter it and redistribute it
  16. * freely, subject to the following restrictions:
  17. * 1. The origin of this software must not be misrepresented; you must not
  18. * claim that you wrote the original software. If you use this software
  19. * in a product, an acknowledgment in the product documentation would be
  20. * appreciated but is not required.
  21. * 2. Altered source versions must be plainly marked as such, and must not be
  22. * misrepresented as being the original software.
  23. * 3. This notice may not be removed or altered from any source distribution.
  24. */
  25. using System;
  26. using System.Diagnostics;
  27. using FarseerPhysics.Common;
  28. using Microsoft.Xna.Framework;
  29. namespace FarseerPhysics.Dynamics.Joints
  30. {
  31. /// <summary>
  32. /// A revolute joint rains to bodies to share a common point while they
  33. /// are free to rotate about the point. The relative rotation about the shared
  34. /// point is the joint angle. You can limit the relative rotation with
  35. /// a joint limit that specifies a lower and upper angle. You can use a motor
  36. /// to drive the relative rotation about the shared point. A maximum motor torque
  37. /// is provided so that infinite forces are not generated.
  38. /// </summary>
  39. public class RevoluteJoint : Joint
  40. {
  41. public Vector2 LocalAnchorA;
  42. public Vector2 LocalAnchorB;
  43. private bool _enableLimit;
  44. private bool _enableMotor;
  45. private Vector3 _impulse;
  46. private LimitState _limitState;
  47. private float _lowerAngle;
  48. private Mat33 _mass; // effective mass for point-to-point constraint.
  49. private float _maxMotorTorque;
  50. private float _motorImpulse;
  51. private float _motorMass; // effective mass for motor/limit angular constraint.
  52. private float _motorSpeed;
  53. private float _referenceAngle;
  54. private float _tmpFloat1;
  55. private Vector2 _tmpVector1, _tmpVector2;
  56. private float _upperAngle;
  57. internal RevoluteJoint()
  58. {
  59. JointType = JointType.Revolute;
  60. }
  61. /// <summary>
  62. /// Initialize the bodies and local anchor.
  63. /// This requires defining an
  64. /// anchor point where the bodies are joined. The definition
  65. /// uses local anchor points so that the initial configuration
  66. /// can violate the constraint slightly. You also need to
  67. /// specify the initial relative angle for joint limits. This
  68. /// helps when saving and loading a game.
  69. /// The local anchor points are measured from the body's origin
  70. /// rather than the center of mass because:
  71. /// 1. you might not know where the center of mass will be.
  72. /// 2. if you add/remove shapes from a body and recompute the mass,
  73. /// the joints will be broken.
  74. /// </summary>
  75. /// <param name="bodyA">The first body.</param>
  76. /// <param name="bodyB">The second body.</param>
  77. /// <param name="localAnchorA">The first body anchor.</param>
  78. /// <param name="localAnchorB">The second anchor.</param>
  79. public RevoluteJoint(Body bodyA, Body bodyB, Vector2 localAnchorA, Vector2 localAnchorB)
  80. : base(bodyA, bodyB)
  81. {
  82. JointType = JointType.Revolute;
  83. // Changed to local coordinates.
  84. LocalAnchorA = localAnchorA;
  85. LocalAnchorB = localAnchorB;
  86. ReferenceAngle = BodyB.Rotation - BodyA.Rotation;
  87. _impulse = Vector3.Zero;
  88. _limitState = LimitState.Inactive;
  89. }
  90. public override Vector2 WorldAnchorA
  91. {
  92. get { return BodyA.GetWorldPoint(LocalAnchorA); }
  93. }
  94. public override Vector2 WorldAnchorB
  95. {
  96. get { return BodyB.GetWorldPoint(LocalAnchorB); }
  97. set { Debug.Assert(false, "You can't set the world anchor on this joint type."); }
  98. }
  99. public float ReferenceAngle
  100. {
  101. get { return _referenceAngle; }
  102. set
  103. {
  104. WakeBodies();
  105. _referenceAngle = value;
  106. }
  107. }
  108. /// <summary>
  109. /// Get the current joint angle in radians.
  110. /// </summary>
  111. /// <value></value>
  112. public float JointAngle
  113. {
  114. get { return BodyB.Sweep.A - BodyA.Sweep.A - ReferenceAngle; }
  115. }
  116. /// <summary>
  117. /// Get the current joint angle speed in radians per second.
  118. /// </summary>
  119. /// <value></value>
  120. public float JointSpeed
  121. {
  122. get { return BodyB.AngularVelocityInternal - BodyA.AngularVelocityInternal; }
  123. }
  124. /// <summary>
  125. /// Is the joint limit enabled?
  126. /// </summary>
  127. /// <value><c>true</c> if [limit enabled]; otherwise, <c>false</c>.</value>
  128. public bool LimitEnabled
  129. {
  130. get { return _enableLimit; }
  131. set
  132. {
  133. WakeBodies();
  134. _enableLimit = value;
  135. }
  136. }
  137. /// <summary>
  138. /// Get the lower joint limit in radians.
  139. /// </summary>
  140. /// <value></value>
  141. public float LowerLimit
  142. {
  143. get { return _lowerAngle; }
  144. set
  145. {
  146. WakeBodies();
  147. _lowerAngle = value;
  148. }
  149. }
  150. /// <summary>
  151. /// Get the upper joint limit in radians.
  152. /// </summary>
  153. /// <value></value>
  154. public float UpperLimit
  155. {
  156. get { return _upperAngle; }
  157. set
  158. {
  159. WakeBodies();
  160. _upperAngle = value;
  161. }
  162. }
  163. /// <summary>
  164. /// Is the joint motor enabled?
  165. /// </summary>
  166. /// <value><c>true</c> if [motor enabled]; otherwise, <c>false</c>.</value>
  167. public bool MotorEnabled
  168. {
  169. get { return _enableMotor; }
  170. set
  171. {
  172. WakeBodies();
  173. _enableMotor = value;
  174. }
  175. }
  176. /// <summary>
  177. /// Set the motor speed in radians per second.
  178. /// </summary>
  179. /// <value>The speed.</value>
  180. public float MotorSpeed
  181. {
  182. set
  183. {
  184. WakeBodies();
  185. _motorSpeed = value;
  186. }
  187. get { return _motorSpeed; }
  188. }
  189. /// <summary>
  190. /// Set the maximum motor torque, usually in N-m.
  191. /// </summary>
  192. /// <value>The torque.</value>
  193. public float MaxMotorTorque
  194. {
  195. set
  196. {
  197. WakeBodies();
  198. _maxMotorTorque = value;
  199. }
  200. get { return _maxMotorTorque; }
  201. }
  202. /// <summary>
  203. /// Get the current motor torque, usually in N-m.
  204. /// </summary>
  205. /// <value></value>
  206. public float MotorTorque
  207. {
  208. get { return _motorImpulse; }
  209. set
  210. {
  211. WakeBodies();
  212. _motorImpulse = value;
  213. }
  214. }
  215. public override Vector2 GetReactionForce(float inv_dt)
  216. {
  217. Vector2 P = new Vector2(_impulse.X, _impulse.Y);
  218. return inv_dt * P;
  219. }
  220. public override float GetReactionTorque(float inv_dt)
  221. {
  222. return inv_dt * _impulse.Z;
  223. }
  224. internal override void InitVelocityConstraints(ref TimeStep step)
  225. {
  226. Body b1 = BodyA;
  227. Body b2 = BodyB;
  228. if (_enableMotor || _enableLimit)
  229. {
  230. // You cannot create a rotation limit between bodies that
  231. // both have fixed rotation.
  232. Debug.Assert(b1.InvI > 0.0f || b2.InvI > 0.0f);
  233. }
  234. // Compute the effective mass matrix.
  235. /*Transform xf1, xf2;
  236. b1.GetTransform(out xf1);
  237. b2.GetTransform(out xf2);*/
  238. Vector2 r1 = MathUtils.Multiply(ref b1.Xf.R, LocalAnchorA - b1.LocalCenter);
  239. Vector2 r2 = MathUtils.Multiply(ref b2.Xf.R, LocalAnchorB - b2.LocalCenter);
  240. // J = [-I -r1_skew I r2_skew]
  241. // [ 0 -1 0 1]
  242. // r_skew = [-ry; rx]
  243. // Matlab
  244. // K = [ m1+r1y^2*i1+m2+r2y^2*i2, -r1y*i1*r1x-r2y*i2*r2x, -r1y*i1-r2y*i2]
  245. // [ -r1y*i1*r1x-r2y*i2*r2x, m1+r1x^2*i1+m2+r2x^2*i2, r1x*i1+r2x*i2]
  246. // [ -r1y*i1-r2y*i2, r1x*i1+r2x*i2, i1+i2]
  247. float m1 = b1.InvMass, m2 = b2.InvMass;
  248. float i1 = b1.InvI, i2 = b2.InvI;
  249. _mass.Col1.X = m1 + m2 + r1.Y * r1.Y * i1 + r2.Y * r2.Y * i2;
  250. _mass.Col2.X = -r1.Y * r1.X * i1 - r2.Y * r2.X * i2;
  251. _mass.Col3.X = -r1.Y * i1 - r2.Y * i2;
  252. _mass.Col1.Y = _mass.Col2.X;
  253. _mass.Col2.Y = m1 + m2 + r1.X * r1.X * i1 + r2.X * r2.X * i2;
  254. _mass.Col3.Y = r1.X * i1 + r2.X * i2;
  255. _mass.Col1.Z = _mass.Col3.X;
  256. _mass.Col2.Z = _mass.Col3.Y;
  257. _mass.Col3.Z = i1 + i2;
  258. _motorMass = i1 + i2;
  259. if (_motorMass > 0.0f)
  260. {
  261. _motorMass = 1.0f / _motorMass;
  262. }
  263. if (_enableMotor == false)
  264. {
  265. _motorImpulse = 0.0f;
  266. }
  267. if (_enableLimit)
  268. {
  269. float jointAngle = b2.Sweep.A - b1.Sweep.A - ReferenceAngle;
  270. if (Math.Abs(_upperAngle - _lowerAngle) < 2.0f * Settings.AngularSlop)
  271. {
  272. _limitState = LimitState.Equal;
  273. }
  274. else if (jointAngle <= _lowerAngle)
  275. {
  276. if (_limitState != LimitState.AtLower)
  277. {
  278. _impulse.Z = 0.0f;
  279. }
  280. _limitState = LimitState.AtLower;
  281. }
  282. else if (jointAngle >= _upperAngle)
  283. {
  284. if (_limitState != LimitState.AtUpper)
  285. {
  286. _impulse.Z = 0.0f;
  287. }
  288. _limitState = LimitState.AtUpper;
  289. }
  290. else
  291. {
  292. _limitState = LimitState.Inactive;
  293. _impulse.Z = 0.0f;
  294. }
  295. }
  296. else
  297. {
  298. _limitState = LimitState.Inactive;
  299. }
  300. if (Settings.EnableWarmstarting)
  301. {
  302. // Scale impulses to support a variable time step.
  303. _impulse *= step.dtRatio;
  304. _motorImpulse *= step.dtRatio;
  305. Vector2 P = new Vector2(_impulse.X, _impulse.Y);
  306. b1.LinearVelocityInternal -= m1 * P;
  307. MathUtils.Cross(ref r1, ref P, out _tmpFloat1);
  308. b1.AngularVelocityInternal -= i1 * ( /* r1 x P */_tmpFloat1 + _motorImpulse + _impulse.Z);
  309. b2.LinearVelocityInternal += m2 * P;
  310. MathUtils.Cross(ref r2, ref P, out _tmpFloat1);
  311. b2.AngularVelocityInternal += i2 * ( /* r2 x P */_tmpFloat1 + _motorImpulse + _impulse.Z);
  312. }
  313. else
  314. {
  315. _impulse = Vector3.Zero;
  316. _motorImpulse = 0.0f;
  317. }
  318. }
  319. internal override void SolveVelocityConstraints(ref TimeStep step)
  320. {
  321. Body b1 = BodyA;
  322. Body b2 = BodyB;
  323. Vector2 v1 = b1.LinearVelocityInternal;
  324. float w1 = b1.AngularVelocityInternal;
  325. Vector2 v2 = b2.LinearVelocityInternal;
  326. float w2 = b2.AngularVelocityInternal;
  327. float m1 = b1.InvMass, m2 = b2.InvMass;
  328. float i1 = b1.InvI, i2 = b2.InvI;
  329. // Solve motor constraint.
  330. if (_enableMotor && _limitState != LimitState.Equal)
  331. {
  332. float Cdot = w2 - w1 - _motorSpeed;
  333. float impulse = _motorMass * (-Cdot);
  334. float oldImpulse = _motorImpulse;
  335. float maxImpulse = step.dt * _maxMotorTorque;
  336. _motorImpulse = MathUtils.Clamp(_motorImpulse + impulse, -maxImpulse, maxImpulse);
  337. impulse = _motorImpulse - oldImpulse;
  338. w1 -= i1 * impulse;
  339. w2 += i2 * impulse;
  340. }
  341. // Solve limit constraint.
  342. if (_enableLimit && _limitState != LimitState.Inactive)
  343. {
  344. /*Transform xf1, xf2;
  345. b1.GetTransform(out xf1);
  346. b2.GetTransform(out xf2);*/
  347. Vector2 r1 = MathUtils.Multiply(ref b1.Xf.R, LocalAnchorA - b1.LocalCenter);
  348. Vector2 r2 = MathUtils.Multiply(ref b2.Xf.R, LocalAnchorB - b2.LocalCenter);
  349. // Solve point-to-point constraint
  350. MathUtils.Cross(w2, ref r2, out _tmpVector2);
  351. MathUtils.Cross(w1, ref r1, out _tmpVector1);
  352. Vector2 Cdot1 = v2 + /* w2 x r2 */ _tmpVector2 - v1 - /* w1 x r1 */ _tmpVector1;
  353. float Cdot2 = w2 - w1;
  354. Vector3 Cdot = new Vector3(Cdot1.X, Cdot1.Y, Cdot2);
  355. Vector3 impulse = _mass.Solve33(-Cdot);
  356. if (_limitState == LimitState.Equal)
  357. {
  358. _impulse += impulse;
  359. }
  360. else if (_limitState == LimitState.AtLower)
  361. {
  362. float newImpulse = _impulse.Z + impulse.Z;
  363. if (newImpulse < 0.0f)
  364. {
  365. Vector2 reduced = _mass.Solve22(-Cdot1);
  366. impulse.X = reduced.X;
  367. impulse.Y = reduced.Y;
  368. impulse.Z = -_impulse.Z;
  369. _impulse.X += reduced.X;
  370. _impulse.Y += reduced.Y;
  371. _impulse.Z = 0.0f;
  372. }
  373. }
  374. else if (_limitState == LimitState.AtUpper)
  375. {
  376. float newImpulse = _impulse.Z + impulse.Z;
  377. if (newImpulse > 0.0f)
  378. {
  379. Vector2 reduced = _mass.Solve22(-Cdot1);
  380. impulse.X = reduced.X;
  381. impulse.Y = reduced.Y;
  382. impulse.Z = -_impulse.Z;
  383. _impulse.X += reduced.X;
  384. _impulse.Y += reduced.Y;
  385. _impulse.Z = 0.0f;
  386. }
  387. }
  388. Vector2 P = new Vector2(impulse.X, impulse.Y);
  389. v1 -= m1 * P;
  390. MathUtils.Cross(ref r1, ref P, out _tmpFloat1);
  391. w1 -= i1 * ( /* r1 x P */_tmpFloat1 + impulse.Z);
  392. v2 += m2 * P;
  393. MathUtils.Cross(ref r2, ref P, out _tmpFloat1);
  394. w2 += i2 * ( /* r2 x P */_tmpFloat1 + impulse.Z);
  395. }
  396. else
  397. {
  398. /*Transform xf1, xf2;
  399. b1.GetTransform(out xf1);
  400. b2.GetTransform(out xf2);*/
  401. _tmpVector1 = LocalAnchorA - b1.LocalCenter;
  402. _tmpVector2 = LocalAnchorB - b2.LocalCenter;
  403. Vector2 r1 = MathUtils.Multiply(ref b1.Xf.R, ref _tmpVector1);
  404. Vector2 r2 = MathUtils.Multiply(ref b2.Xf.R, ref _tmpVector2);
  405. // Solve point-to-point constraint
  406. MathUtils.Cross(w2, ref r2, out _tmpVector2);
  407. MathUtils.Cross(w1, ref r1, out _tmpVector1);
  408. Vector2 Cdot = v2 + /* w2 x r2 */ _tmpVector2 - v1 - /* w1 x r1 */ _tmpVector1;
  409. Vector2 impulse = _mass.Solve22(-Cdot);
  410. _impulse.X += impulse.X;
  411. _impulse.Y += impulse.Y;
  412. v1 -= m1 * impulse;
  413. MathUtils.Cross(ref r1, ref impulse, out _tmpFloat1);
  414. w1 -= i1 * /* r1 x impulse */ _tmpFloat1;
  415. v2 += m2 * impulse;
  416. MathUtils.Cross(ref r2, ref impulse, out _tmpFloat1);
  417. w2 += i2 * /* r2 x impulse */ _tmpFloat1;
  418. }
  419. b1.LinearVelocityInternal = v1;
  420. b1.AngularVelocityInternal = w1;
  421. b2.LinearVelocityInternal = v2;
  422. b2.AngularVelocityInternal = w2;
  423. }
  424. internal override bool SolvePositionConstraints()
  425. {
  426. // TODO_ERIN block solve with limit. COME ON ERIN
  427. Body b1 = BodyA;
  428. Body b2 = BodyB;
  429. float angularError = 0.0f;
  430. float positionError;
  431. // Solve angular limit constraint.
  432. if (_enableLimit && _limitState != LimitState.Inactive)
  433. {
  434. float angle = b2.Sweep.A - b1.Sweep.A - ReferenceAngle;
  435. float limitImpulse = 0.0f;
  436. if (_limitState == LimitState.Equal)
  437. {
  438. // Prevent large angular corrections
  439. float C = MathUtils.Clamp(angle - _lowerAngle, -Settings.MaxAngularCorrection,
  440. Settings.MaxAngularCorrection);
  441. limitImpulse = -_motorMass * C;
  442. angularError = Math.Abs(C);
  443. }
  444. else if (_limitState == LimitState.AtLower)
  445. {
  446. float C = angle - _lowerAngle;
  447. angularError = -C;
  448. // Prevent large angular corrections and allow some slop.
  449. C = MathUtils.Clamp(C + Settings.AngularSlop, -Settings.MaxAngularCorrection, 0.0f);
  450. limitImpulse = -_motorMass * C;
  451. }
  452. else if (_limitState == LimitState.AtUpper)
  453. {
  454. float C = angle - _upperAngle;
  455. angularError = C;
  456. // Prevent large angular corrections and allow some slop.
  457. C = MathUtils.Clamp(C - Settings.AngularSlop, 0.0f, Settings.MaxAngularCorrection);
  458. limitImpulse = -_motorMass * C;
  459. }
  460. b1.Sweep.A -= b1.InvI * limitImpulse;
  461. b2.Sweep.A += b2.InvI * limitImpulse;
  462. b1.SynchronizeTransform();
  463. b2.SynchronizeTransform();
  464. }
  465. // Solve point-to-point constraint.
  466. {
  467. /*Transform xf1, xf2;
  468. b1.GetTransform(out xf1);
  469. b2.GetTransform(out xf2);*/
  470. Vector2 r1 = MathUtils.Multiply(ref b1.Xf.R, LocalAnchorA - b1.LocalCenter);
  471. Vector2 r2 = MathUtils.Multiply(ref b2.Xf.R, LocalAnchorB - b2.LocalCenter);
  472. Vector2 C = b2.Sweep.C + r2 - b1.Sweep.C - r1;
  473. positionError = C.Length();
  474. float invMass1 = b1.InvMass, invMass2 = b2.InvMass;
  475. float invI1 = b1.InvI, invI2 = b2.InvI;
  476. // Handle large detachment.
  477. const float k_allowedStretch = 10.0f * Settings.LinearSlop;
  478. if (C.LengthSquared() > k_allowedStretch * k_allowedStretch)
  479. {
  480. // Use a particle solution (no rotation).
  481. Vector2 u = C;
  482. u.Normalize();
  483. float k = invMass1 + invMass2;
  484. Debug.Assert(k > Settings.Epsilon);
  485. float m = 1.0f / k;
  486. Vector2 impulse2 = m * (-C);
  487. const float k_beta = 0.5f;
  488. b1.Sweep.C -= k_beta * invMass1 * impulse2;
  489. b2.Sweep.C += k_beta * invMass2 * impulse2;
  490. C = b2.Sweep.C + r2 - b1.Sweep.C - r1;
  491. }
  492. Mat22 K1 = new Mat22(new Vector2(invMass1 + invMass2, 0.0f), new Vector2(0.0f, invMass1 + invMass2));
  493. Mat22 K2 = new Mat22(new Vector2(invI1 * r1.Y * r1.Y, -invI1 * r1.X * r1.Y),
  494. new Vector2(-invI1 * r1.X * r1.Y, invI1 * r1.X * r1.X));
  495. Mat22 K3 = new Mat22(new Vector2(invI2 * r2.Y * r2.Y, -invI2 * r2.X * r2.Y),
  496. new Vector2(-invI2 * r2.X * r2.Y, invI2 * r2.X * r2.X));
  497. Mat22 Ka;
  498. Mat22.Add(ref K1, ref K2, out Ka);
  499. Mat22 K;
  500. Mat22.Add(ref Ka, ref K3, out K);
  501. Vector2 impulse = K.Solve(-C);
  502. b1.Sweep.C -= b1.InvMass * impulse;
  503. MathUtils.Cross(ref r1, ref impulse, out _tmpFloat1);
  504. b1.Sweep.A -= b1.InvI * /* r1 x impulse */ _tmpFloat1;
  505. b2.Sweep.C += b2.InvMass * impulse;
  506. MathUtils.Cross(ref r2, ref impulse, out _tmpFloat1);
  507. b2.Sweep.A += b2.InvI * /* r2 x impulse */ _tmpFloat1;
  508. b1.SynchronizeTransform();
  509. b2.SynchronizeTransform();
  510. }
  511. return positionError <= Settings.LinearSlop && angularError <= Settings.AngularSlop;
  512. }
  513. }
  514. }