FixedRevoluteJoint.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  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 FixedRevoluteJoint : Joint
  40. {
  41. private bool _enableLimit;
  42. private bool _enableMotor;
  43. private Vector3 _impulse;
  44. private LimitState _limitState;
  45. private float _lowerAngle;
  46. private Mat33 _mass; // effective mass for point-to-point constraint.
  47. private float _maxMotorTorque;
  48. private float _motorImpulse;
  49. private float _motorMass; // effective mass for motor/limit angular constraint.
  50. private float _motorSpeed;
  51. private float _upperAngle;
  52. private Vector2 _worldAnchor;
  53. /// <summary>
  54. /// Initialize the bodies, anchors, and reference angle using the world
  55. /// anchor.
  56. /// This requires defining an
  57. /// anchor point where the bodies are joined. The definition
  58. /// uses local anchor points so that the initial configuration
  59. /// can violate the constraint slightly. You also need to
  60. /// specify the initial relative angle for joint limits. This
  61. /// helps when saving and loading a game.
  62. /// The local anchor points are measured from the body's origin
  63. /// rather than the center of mass because:
  64. /// 1. you might not know where the center of mass will be.
  65. /// 2. if you add/remove shapes from a body and recompute the mass,
  66. /// the joints will be broken.
  67. /// </summary>
  68. /// <param name="body">The body.</param>
  69. /// <param name="bodyAnchor">The body anchor.</param>
  70. /// <param name="worldAnchor">The world anchor.</param>
  71. public FixedRevoluteJoint(Body body, Vector2 bodyAnchor, Vector2 worldAnchor)
  72. : base(body)
  73. {
  74. JointType = JointType.FixedRevolute;
  75. // Changed to local coordinates.
  76. LocalAnchorA = bodyAnchor;
  77. _worldAnchor = worldAnchor;
  78. ReferenceAngle = -BodyA.Rotation;
  79. _impulse = Vector3.Zero;
  80. _limitState = LimitState.Inactive;
  81. }
  82. public override Vector2 WorldAnchorA
  83. {
  84. get { return BodyA.GetWorldPoint(LocalAnchorA); }
  85. }
  86. public override Vector2 WorldAnchorB
  87. {
  88. get { return _worldAnchor; }
  89. set { _worldAnchor = value; }
  90. }
  91. public Vector2 LocalAnchorA { get; set; }
  92. public float ReferenceAngle { get; set; }
  93. /// <summary>
  94. /// Get the current joint angle in radians.
  95. /// </summary>
  96. /// <value></value>
  97. public float JointAngle
  98. {
  99. get { return BodyA.Sweep.A - ReferenceAngle; }
  100. }
  101. /// <summary>
  102. /// Get the current joint angle speed in radians per second.
  103. /// </summary>
  104. /// <value></value>
  105. public float JointSpeed
  106. {
  107. get { return BodyA.AngularVelocityInternal; }
  108. }
  109. /// <summary>
  110. /// Is the joint limit enabled?
  111. /// </summary>
  112. /// <value><c>true</c> if [limit enabled]; otherwise, <c>false</c>.</value>
  113. public bool LimitEnabled
  114. {
  115. get { return _enableLimit; }
  116. set
  117. {
  118. WakeBodies();
  119. _enableLimit = value;
  120. }
  121. }
  122. /// <summary>
  123. /// Get the lower joint limit in radians.
  124. /// </summary>
  125. /// <value></value>
  126. public float LowerLimit
  127. {
  128. get { return _lowerAngle; }
  129. set
  130. {
  131. WakeBodies();
  132. _lowerAngle = value;
  133. }
  134. }
  135. /// <summary>
  136. /// Get the upper joint limit in radians.
  137. /// </summary>
  138. /// <value></value>
  139. public float UpperLimit
  140. {
  141. get { return _upperAngle; }
  142. set
  143. {
  144. WakeBodies();
  145. _upperAngle = value;
  146. }
  147. }
  148. /// <summary>
  149. /// Is the joint motor enabled?
  150. /// </summary>
  151. /// <value><c>true</c> if [motor enabled]; otherwise, <c>false</c>.</value>
  152. public bool MotorEnabled
  153. {
  154. get { return _enableMotor; }
  155. set
  156. {
  157. WakeBodies();
  158. _enableMotor = value;
  159. }
  160. }
  161. /// <summary>
  162. /// Set the motor speed in radians per second.
  163. /// </summary>
  164. /// <value>The speed.</value>
  165. public float MotorSpeed
  166. {
  167. set
  168. {
  169. WakeBodies();
  170. _motorSpeed = value;
  171. }
  172. get { return _motorSpeed; }
  173. }
  174. /// <summary>
  175. /// Set the maximum motor torque, usually in N-m.
  176. /// </summary>
  177. /// <value>The torque.</value>
  178. public float MaxMotorTorque
  179. {
  180. set
  181. {
  182. WakeBodies();
  183. _maxMotorTorque = value;
  184. }
  185. get { return _maxMotorTorque; }
  186. }
  187. /// <summary>
  188. /// Get the current motor torque, usually in N-m.
  189. /// </summary>
  190. /// <value></value>
  191. public float MotorTorque
  192. {
  193. get { return _motorImpulse; }
  194. set
  195. {
  196. WakeBodies();
  197. _motorImpulse = value;
  198. }
  199. }
  200. public override Vector2 GetReactionForce(float inv_dt)
  201. {
  202. return inv_dt * new Vector2(_impulse.X, _impulse.Y);
  203. }
  204. public override float GetReactionTorque(float inv_dt)
  205. {
  206. return inv_dt * _impulse.Z;
  207. }
  208. internal override void InitVelocityConstraints(ref TimeStep step)
  209. {
  210. Body b1 = BodyA;
  211. if (_enableMotor || _enableLimit)
  212. {
  213. // You cannot create a rotation limit between bodies that
  214. // both have fixed rotation.
  215. Debug.Assert(b1.InvI > 0.0f /* || b2._invI > 0.0f*/);
  216. }
  217. // Compute the effective mass matrix.
  218. Transform xf1;
  219. b1.GetTransform(out xf1);
  220. Vector2 r1 = MathUtils.Multiply(ref xf1.R, LocalAnchorA - b1.LocalCenter);
  221. Vector2 r2 = _worldAnchor; // MathUtils.Multiply(ref xf2.R, LocalAnchorB - b2.LocalCenter);
  222. // J = [-I -r1_skew I r2_skew]
  223. // [ 0 -1 0 1]
  224. // r_skew = [-ry; rx]
  225. // Matlab
  226. // K = [ m1+r1y^2*i1+m2+r2y^2*i2, -r1y*i1*r1x-r2y*i2*r2x, -r1y*i1-r2y*i2]
  227. // [ -r1y*i1*r1x-r2y*i2*r2x, m1+r1x^2*i1+m2+r2x^2*i2, r1x*i1+r2x*i2]
  228. // [ -r1y*i1-r2y*i2, r1x*i1+r2x*i2, i1+i2]
  229. float m1 = b1.InvMass;
  230. const float m2 = 0;
  231. float i1 = b1.InvI;
  232. const float i2 = 0;
  233. _mass.Col1.X = m1 + m2 + r1.Y * r1.Y * i1 + r2.Y * r2.Y * i2;
  234. _mass.Col2.X = -r1.Y * r1.X * i1 - r2.Y * r2.X * i2;
  235. _mass.Col3.X = -r1.Y * i1 - r2.Y * i2;
  236. _mass.Col1.Y = _mass.Col2.X;
  237. _mass.Col2.Y = m1 + m2 + r1.X * r1.X * i1 + r2.X * r2.X * i2;
  238. _mass.Col3.Y = r1.X * i1 + r2.X * i2;
  239. _mass.Col1.Z = _mass.Col3.X;
  240. _mass.Col2.Z = _mass.Col3.Y;
  241. _mass.Col3.Z = i1 + i2;
  242. _motorMass = i1 + i2;
  243. if (_motorMass > 0.0f)
  244. {
  245. _motorMass = 1.0f / _motorMass;
  246. }
  247. if (_enableMotor == false)
  248. {
  249. _motorImpulse = 0.0f;
  250. }
  251. if (_enableLimit)
  252. {
  253. float jointAngle = 0 - b1.Sweep.A - ReferenceAngle;
  254. if (Math.Abs(_upperAngle - _lowerAngle) < 2.0f * Settings.AngularSlop)
  255. {
  256. _limitState = LimitState.Equal;
  257. }
  258. else if (jointAngle <= _lowerAngle)
  259. {
  260. if (_limitState != LimitState.AtLower)
  261. {
  262. _impulse.Z = 0.0f;
  263. }
  264. _limitState = LimitState.AtLower;
  265. }
  266. else if (jointAngle >= _upperAngle)
  267. {
  268. if (_limitState != LimitState.AtUpper)
  269. {
  270. _impulse.Z = 0.0f;
  271. }
  272. _limitState = LimitState.AtUpper;
  273. }
  274. else
  275. {
  276. _limitState = LimitState.Inactive;
  277. _impulse.Z = 0.0f;
  278. }
  279. }
  280. else
  281. {
  282. _limitState = LimitState.Inactive;
  283. }
  284. if (Settings.EnableWarmstarting)
  285. {
  286. // Scale impulses to support a variable time step.
  287. _impulse *= step.dtRatio;
  288. _motorImpulse *= step.dtRatio;
  289. Vector2 P = new Vector2(_impulse.X, _impulse.Y);
  290. b1.LinearVelocityInternal -= m1 * P;
  291. b1.AngularVelocityInternal -= i1 * (MathUtils.Cross(r1, P) + _motorImpulse + _impulse.Z);
  292. }
  293. else
  294. {
  295. _impulse = Vector3.Zero;
  296. _motorImpulse = 0.0f;
  297. }
  298. }
  299. internal override void SolveVelocityConstraints(ref TimeStep step)
  300. {
  301. Body b1 = BodyA;
  302. Vector2 v1 = b1.LinearVelocityInternal;
  303. float w1 = b1.AngularVelocityInternal;
  304. Vector2 v2 = Vector2.Zero;
  305. const float w2 = 0;
  306. float m1 = b1.InvMass;
  307. float i1 = b1.InvI;
  308. // Solve motor constraint.
  309. if (_enableMotor && _limitState != LimitState.Equal)
  310. {
  311. float Cdot = w2 - w1 - _motorSpeed;
  312. float impulse = _motorMass * (-Cdot);
  313. float oldImpulse = _motorImpulse;
  314. float maxImpulse = step.dt * _maxMotorTorque;
  315. _motorImpulse = MathUtils.Clamp(_motorImpulse + impulse, -maxImpulse, maxImpulse);
  316. impulse = _motorImpulse - oldImpulse;
  317. w1 -= i1 * impulse;
  318. }
  319. // Solve limit constraint.
  320. if (_enableLimit && _limitState != LimitState.Inactive)
  321. {
  322. Transform xf1;
  323. b1.GetTransform(out xf1);
  324. Vector2 r1 = MathUtils.Multiply(ref xf1.R, LocalAnchorA - b1.LocalCenter);
  325. Vector2 r2 = _worldAnchor;
  326. // Solve point-to-point constraint
  327. Vector2 Cdot1 = v2 + MathUtils.Cross(w2, r2) - v1 - MathUtils.Cross(w1, r1);
  328. float Cdot2 = w2 - w1;
  329. Vector3 Cdot = new Vector3(Cdot1.X, Cdot1.Y, Cdot2);
  330. Vector3 impulse = _mass.Solve33(-Cdot);
  331. if (_limitState == LimitState.Equal)
  332. {
  333. _impulse += impulse;
  334. }
  335. else if (_limitState == LimitState.AtLower)
  336. {
  337. float newImpulse = _impulse.Z + impulse.Z;
  338. if (newImpulse < 0.0f)
  339. {
  340. Vector2 reduced = _mass.Solve22(-Cdot1);
  341. impulse.X = reduced.X;
  342. impulse.Y = reduced.Y;
  343. impulse.Z = -_impulse.Z;
  344. _impulse.X += reduced.X;
  345. _impulse.Y += reduced.Y;
  346. _impulse.Z = 0.0f;
  347. }
  348. }
  349. else if (_limitState == LimitState.AtUpper)
  350. {
  351. float newImpulse = _impulse.Z + impulse.Z;
  352. if (newImpulse > 0.0f)
  353. {
  354. Vector2 reduced = _mass.Solve22(-Cdot1);
  355. impulse.X = reduced.X;
  356. impulse.Y = reduced.Y;
  357. impulse.Z = -_impulse.Z;
  358. _impulse.X += reduced.X;
  359. _impulse.Y += reduced.Y;
  360. _impulse.Z = 0.0f;
  361. }
  362. }
  363. Vector2 P = new Vector2(impulse.X, impulse.Y);
  364. v1 -= m1 * P;
  365. w1 -= i1 * (MathUtils.Cross(r1, P) + impulse.Z);
  366. }
  367. else
  368. {
  369. Transform xf1;
  370. b1.GetTransform(out xf1);
  371. Vector2 r1 = MathUtils.Multiply(ref xf1.R, LocalAnchorA - b1.LocalCenter);
  372. Vector2 r2 = _worldAnchor;
  373. // Solve point-to-point constraint
  374. Vector2 Cdot = v2 + MathUtils.Cross(w2, r2) - v1 - MathUtils.Cross(w1, r1);
  375. Vector2 impulse = _mass.Solve22(-Cdot);
  376. _impulse.X += impulse.X;
  377. _impulse.Y += impulse.Y;
  378. v1 -= m1 * impulse;
  379. w1 -= i1 * MathUtils.Cross(r1, impulse);
  380. }
  381. b1.LinearVelocityInternal = v1;
  382. b1.AngularVelocityInternal = w1;
  383. }
  384. internal override bool SolvePositionConstraints()
  385. {
  386. // TODO_ERIN block solve with limit. COME ON ERIN
  387. Body b1 = BodyA;
  388. float angularError = 0.0f;
  389. float positionError;
  390. // Solve angular limit constraint.
  391. if (_enableLimit && _limitState != LimitState.Inactive)
  392. {
  393. float angle = 0 - b1.Sweep.A - ReferenceAngle;
  394. float limitImpulse = 0.0f;
  395. if (_limitState == LimitState.Equal)
  396. {
  397. // Prevent large angular corrections
  398. float C = MathUtils.Clamp(angle - _lowerAngle, -Settings.MaxAngularCorrection,
  399. Settings.MaxAngularCorrection);
  400. limitImpulse = -_motorMass * C;
  401. angularError = Math.Abs(C);
  402. }
  403. else if (_limitState == LimitState.AtLower)
  404. {
  405. float C = angle - _lowerAngle;
  406. angularError = -C;
  407. // Prevent large angular corrections and allow some slop.
  408. C = MathUtils.Clamp(C + Settings.AngularSlop, -Settings.MaxAngularCorrection, 0.0f);
  409. limitImpulse = -_motorMass * C;
  410. }
  411. else if (_limitState == LimitState.AtUpper)
  412. {
  413. float C = angle - _upperAngle;
  414. angularError = C;
  415. // Prevent large angular corrections and allow some slop.
  416. C = MathUtils.Clamp(C - Settings.AngularSlop, 0.0f, Settings.MaxAngularCorrection);
  417. limitImpulse = -_motorMass * C;
  418. }
  419. b1.Sweep.A -= b1.InvI * limitImpulse;
  420. b1.SynchronizeTransform();
  421. }
  422. // Solve point-to-point constraint.
  423. {
  424. Transform xf1;
  425. b1.GetTransform(out xf1);
  426. Vector2 r1 = MathUtils.Multiply(ref xf1.R, LocalAnchorA - b1.LocalCenter);
  427. Vector2 r2 = _worldAnchor;
  428. Vector2 C = Vector2.Zero + r2 - b1.Sweep.C - r1;
  429. positionError = C.Length();
  430. float invMass1 = b1.InvMass;
  431. const float invMass2 = 0;
  432. float invI1 = b1.InvI;
  433. const float invI2 = 0;
  434. // Handle large detachment.
  435. const float k_allowedStretch = 10.0f * Settings.LinearSlop;
  436. if (C.LengthSquared() > k_allowedStretch * k_allowedStretch)
  437. {
  438. // Use a particle solution (no rotation).
  439. Vector2 u = C;
  440. u.Normalize();
  441. float k = invMass1 + invMass2;
  442. Debug.Assert(k > Settings.Epsilon);
  443. float m = 1.0f / k;
  444. Vector2 impulse2 = m * (-C);
  445. const float k_beta = 0.5f;
  446. b1.Sweep.C -= k_beta * invMass1 * impulse2;
  447. C = Vector2.Zero + r2 - b1.Sweep.C - r1;
  448. }
  449. Mat22 K1 = new Mat22(new Vector2(invMass1 + invMass2, 0.0f), new Vector2(0.0f, invMass1 + invMass2));
  450. Mat22 K2 = new Mat22(new Vector2(invI1 * r1.Y * r1.Y, -invI1 * r1.X * r1.Y),
  451. new Vector2(-invI1 * r1.X * r1.Y, invI1 * r1.X * r1.X));
  452. Mat22 K3 = new Mat22(new Vector2(invI2 * r2.Y * r2.Y, -invI2 * r2.X * r2.Y),
  453. new Vector2(-invI2 * r2.X * r2.Y, invI2 * r2.X * r2.X));
  454. Mat22 Ka;
  455. Mat22.Add(ref K1, ref K2, out Ka);
  456. Mat22 K;
  457. Mat22.Add(ref Ka, ref K3, out K);
  458. Vector2 impulse = K.Solve(-C);
  459. b1.Sweep.C -= b1.InvMass * impulse;
  460. b1.Sweep.A -= b1.InvI * MathUtils.Cross(r1, impulse);
  461. b1.SynchronizeTransform();
  462. }
  463. return positionError <= Settings.LinearSlop && angularError <= Settings.AngularSlop;
  464. }
  465. }
  466. }