RevoluteJoint.cs 19 KB

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