PullyJoint.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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. /// Pulley joint definition. This requires two ground anchors,
  28. /// two dynamic body anchor points, max lengths for each side,
  29. /// and a pulley ratio.
  30. public class PulleyJointDef : JointDef
  31. {
  32. internal const float b2_minPulleyLength = 2.0f;
  33. public PulleyJointDef()
  34. {
  35. type = JointType.Pulley;
  36. groundAnchorA = new Vector2(-1.0f, 1.0f);
  37. groundAnchorB = new Vector2(1.0f, 1.0f);
  38. localAnchorA = new Vector2(-1.0f, 0.0f);
  39. localAnchorB = new Vector2(1.0f, 0.0f);
  40. lengthA = 0.0f;
  41. maxLengthA = 0.0f;
  42. lengthB = 0.0f;
  43. maxLengthB = 0.0f;
  44. ratio = 1.0f;
  45. collideConnected = true;
  46. }
  47. /// Initialize the bodies, anchors, lengths, max lengths, and ratio using the world anchors.
  48. public void Initialize(Body b1, Body b2,
  49. Vector2 ga1, Vector2 ga2,
  50. Vector2 anchor1, Vector2 anchor2,
  51. float r)
  52. {
  53. bodyA = b1;
  54. bodyB = b2;
  55. groundAnchorA = ga1;
  56. groundAnchorB = ga2;
  57. localAnchorA = bodyA.GetLocalPoint(anchor1);
  58. localAnchorB = bodyB.GetLocalPoint(anchor2);
  59. Vector2 d1 = anchor1 - ga1;
  60. lengthA = d1.Length();
  61. Vector2 d2 = anchor2 - ga2;
  62. lengthB = d2.Length();
  63. ratio = r;
  64. Debug.Assert(ratio > Settings.b2_epsilon);
  65. float C = lengthA + ratio * lengthB;
  66. maxLengthA = C - ratio * b2_minPulleyLength;
  67. maxLengthB = (C - b2_minPulleyLength) / ratio;
  68. }
  69. /// The first ground anchor in world coordinates. This point never moves.
  70. public Vector2 groundAnchorA;
  71. /// The second ground anchor in world coordinates. This point never moves.
  72. public Vector2 groundAnchorB;
  73. /// The local anchor point relative to body1's origin.
  74. public Vector2 localAnchorA;
  75. /// The local anchor point relative to body2's origin.
  76. public Vector2 localAnchorB;
  77. /// The a reference length for the segment attached to body1.
  78. public float lengthA;
  79. /// The maximum length of the segment attached to body1.
  80. public float maxLengthA;
  81. /// The a reference length for the segment attached to body2.
  82. public float lengthB;
  83. /// The maximum length of the segment attached to body2.
  84. public float maxLengthB;
  85. /// The pulley ratio, used to simulate a block-and-tackle.
  86. public float ratio;
  87. };
  88. /// The pulley joint is connected to two bodies and two fixed ground points.
  89. /// The pulley supports a ratio such that:
  90. /// length1 + ratio * length2 <= ant
  91. /// Yes, the force transmitted is scaled by the ratio.
  92. /// The pulley also enforces a maximum length limit on both sides. This is
  93. /// useful to prevent one side of the pulley hitting the top.
  94. public class PulleyJoint : Joint
  95. {
  96. public override Vector2 GetAnchorA()
  97. {
  98. return _bodyA.GetWorldPoint(_localAnchor1);
  99. }
  100. public override Vector2 GetAnchorB()
  101. {
  102. return _bodyB.GetWorldPoint(_localAnchor2);
  103. }
  104. public override Vector2 GetReactionForce(float inv_dt)
  105. {
  106. Vector2 P = _impulse * _u2;
  107. return inv_dt * P;
  108. }
  109. public override float GetReactionTorque(float inv_dt)
  110. {
  111. return 0.0f;
  112. }
  113. /// Get the first ground anchor.
  114. public Vector2 GetGroundAnchorA()
  115. {
  116. return _groundAnchor1;
  117. }
  118. /// Get the second ground anchor.
  119. public Vector2 GetGroundAnchorB()
  120. {
  121. return _groundAnchor2;
  122. }
  123. /// Get the current length of the segment attached to body1.
  124. public float GetLength1()
  125. {
  126. Vector2 p = _bodyA.GetWorldPoint(_localAnchor1);
  127. Vector2 s = _groundAnchor1;
  128. Vector2 d = p - s;
  129. return d.Length();
  130. }
  131. /// Get the current length of the segment attached to body2.
  132. public float GetLength2()
  133. {
  134. Vector2 p = _bodyB.GetWorldPoint(_localAnchor2);
  135. Vector2 s = _groundAnchor2;
  136. Vector2 d = p - s;
  137. return d.Length();
  138. }
  139. /// Get the pulley ratio.
  140. public float GetRatio()
  141. {
  142. return _ratio;
  143. }
  144. internal PulleyJoint(PulleyJointDef def)
  145. : base (def)
  146. {
  147. _groundAnchor1 = def.groundAnchorA;
  148. _groundAnchor2 = def.groundAnchorB;
  149. _localAnchor1 = def.localAnchorA;
  150. _localAnchor2 = def.localAnchorB;
  151. Debug.Assert(def.ratio != 0.0f);
  152. _ratio = def.ratio;
  153. _ant = def.lengthA + _ratio * def.lengthB;
  154. _maxLength1 = Math.Min(def.maxLengthA, _ant - _ratio * PulleyJointDef.b2_minPulleyLength);
  155. _maxLength2 = Math.Min(def.maxLengthB, (_ant - PulleyJointDef.b2_minPulleyLength) / _ratio);
  156. _impulse = 0.0f;
  157. _limitImpulse1 = 0.0f;
  158. _limitImpulse2 = 0.0f;
  159. }
  160. internal override void InitVelocityConstraints(ref TimeStep step)
  161. {
  162. Body b1 = _bodyA;
  163. Body b2 = _bodyB;
  164. Transform xf1, xf2;
  165. b1.GetTransform(out xf1);
  166. b2.GetTransform(out xf2);
  167. Vector2 r1 = MathUtils.Multiply(ref xf1.R, _localAnchor1 - b1.GetLocalCenter());
  168. Vector2 r2 = MathUtils.Multiply(ref xf2.R, _localAnchor2 - b2.GetLocalCenter());
  169. Vector2 p1 = b1._sweep.c + r1;
  170. Vector2 p2 = b2._sweep.c + r2;
  171. Vector2 s1 = _groundAnchor1;
  172. Vector2 s2 = _groundAnchor2;
  173. // Get the pulley axes.
  174. _u1 = p1 - s1;
  175. _u2 = p2 - s2;
  176. float length1 = _u1.Length();
  177. float length2 = _u2.Length();
  178. if (length1 > Settings.b2_linearSlop)
  179. {
  180. _u1 *= 1.0f / length1;
  181. }
  182. else
  183. {
  184. _u1 = Vector2.Zero;
  185. }
  186. if (length2 > Settings.b2_linearSlop)
  187. {
  188. _u2 *= 1.0f / length2;
  189. }
  190. else
  191. {
  192. _u2 = Vector2.Zero;
  193. }
  194. float C = _ant - length1 - _ratio * length2;
  195. if (C > 0.0f)
  196. {
  197. _state = LimitState.Inactive;
  198. _impulse = 0.0f;
  199. }
  200. else
  201. {
  202. _state = LimitState.AtUpper;
  203. }
  204. if (length1 < _maxLength1)
  205. {
  206. _limitState1 = LimitState.Inactive;
  207. _limitImpulse1 = 0.0f;
  208. }
  209. else
  210. {
  211. _limitState1 = LimitState.AtUpper;
  212. }
  213. if (length2 < _maxLength2)
  214. {
  215. _limitState2 = LimitState.Inactive;
  216. _limitImpulse2 = 0.0f;
  217. }
  218. else
  219. {
  220. _limitState2 = LimitState.AtUpper;
  221. }
  222. // Compute effective mass.
  223. float cr1u1 = MathUtils.Cross(r1, _u1);
  224. float cr2u2 = MathUtils.Cross(r2, _u2);
  225. _limitMass1 = b1._invMass + b1._invI * cr1u1 * cr1u1;
  226. _limitMass2 = b2._invMass + b2._invI * cr2u2 * cr2u2;
  227. _pulleyMass = _limitMass1 + _ratio * _ratio * _limitMass2;
  228. Debug.Assert(_limitMass1 > Settings.b2_epsilon);
  229. Debug.Assert(_limitMass2 > Settings.b2_epsilon);
  230. Debug.Assert(_pulleyMass > Settings.b2_epsilon);
  231. _limitMass1 = 1.0f / _limitMass1;
  232. _limitMass2 = 1.0f / _limitMass2;
  233. _pulleyMass = 1.0f / _pulleyMass;
  234. if (step.warmStarting)
  235. {
  236. // Scale impulses to support variable time steps.
  237. _impulse *= step.dtRatio;
  238. _limitImpulse1 *= step.dtRatio;
  239. _limitImpulse2 *= step.dtRatio;
  240. // Warm starting.
  241. Vector2 P1 = -(_impulse + _limitImpulse1) * _u1;
  242. Vector2 P2 = (-_ratio * _impulse - _limitImpulse2) * _u2;
  243. b1._linearVelocity += b1._invMass * P1;
  244. b1._angularVelocity += b1._invI * MathUtils.Cross(r1, P1);
  245. b2._linearVelocity += b2._invMass * P2;
  246. b2._angularVelocity += b2._invI * MathUtils.Cross(r2, P2);
  247. }
  248. else
  249. {
  250. _impulse = 0.0f;
  251. _limitImpulse1 = 0.0f;
  252. _limitImpulse2 = 0.0f;
  253. }
  254. }
  255. internal override void SolveVelocityConstraints(ref TimeStep step)
  256. {
  257. Body b1 = _bodyA;
  258. Body b2 = _bodyB;
  259. Transform xf1, xf2;
  260. b1.GetTransform(out xf1);
  261. b2.GetTransform(out xf2);
  262. Vector2 r1 = MathUtils.Multiply(ref xf1.R, _localAnchor1 - b1.GetLocalCenter());
  263. Vector2 r2 = MathUtils.Multiply(ref xf2.R, _localAnchor2 - b2.GetLocalCenter());
  264. if (_state == LimitState.AtUpper)
  265. {
  266. Vector2 v1 = b1._linearVelocity + MathUtils.Cross(b1._angularVelocity, r1);
  267. Vector2 v2 = b2._linearVelocity + MathUtils.Cross(b2._angularVelocity, r2);
  268. float Cdot = -Vector2.Dot(_u1, v1) - _ratio * Vector2.Dot(_u2, v2);
  269. float impulse = _pulleyMass * (-Cdot);
  270. float oldImpulse = _impulse;
  271. _impulse = Math.Max(0.0f, _impulse + impulse);
  272. impulse = _impulse - oldImpulse;
  273. Vector2 P1 = -impulse * _u1;
  274. Vector2 P2 = -_ratio * impulse * _u2;
  275. b1._linearVelocity += b1._invMass * P1;
  276. b1._angularVelocity += b1._invI * MathUtils.Cross(r1, P1);
  277. b2._linearVelocity += b2._invMass * P2;
  278. b2._angularVelocity += b2._invI * MathUtils.Cross(r2, P2);
  279. }
  280. if (_limitState1 == LimitState.AtUpper)
  281. {
  282. Vector2 v1 = b1._linearVelocity + MathUtils.Cross(b1._angularVelocity, r1);
  283. float Cdot = -Vector2.Dot(_u1, v1);
  284. float impulse = -_limitMass1 * Cdot;
  285. float oldImpulse = _limitImpulse1;
  286. _limitImpulse1 = Math.Max(0.0f, _limitImpulse1 + impulse);
  287. impulse = _limitImpulse1 - oldImpulse;
  288. Vector2 P1 = -impulse * _u1;
  289. b1._linearVelocity += b1._invMass * P1;
  290. b1._angularVelocity += b1._invI * MathUtils.Cross(r1, P1);
  291. }
  292. if (_limitState2 == LimitState.AtUpper)
  293. {
  294. Vector2 v2 = b2._linearVelocity + MathUtils.Cross(b2._angularVelocity, r2);
  295. float Cdot = -Vector2.Dot(_u2, v2);
  296. float impulse = -_limitMass2 * Cdot;
  297. float oldImpulse = _limitImpulse2;
  298. _limitImpulse2 = Math.Max(0.0f, _limitImpulse2 + impulse);
  299. impulse = _limitImpulse2 - oldImpulse;
  300. Vector2 P2 = -impulse * _u2;
  301. b2._linearVelocity += b2._invMass * P2;
  302. b2._angularVelocity += b2._invI * MathUtils.Cross(r2, P2);
  303. }
  304. }
  305. internal override bool SolvePositionConstraints(float baumgarte)
  306. {
  307. Body b1 = _bodyA;
  308. Body b2 = _bodyB;
  309. Vector2 s1 = _groundAnchor1;
  310. Vector2 s2 = _groundAnchor2;
  311. float linearError = 0.0f;
  312. if (_state == LimitState.AtUpper)
  313. {
  314. Transform xf1, xf2;
  315. b1.GetTransform(out xf1);
  316. b2.GetTransform(out xf2);
  317. Vector2 r1 = MathUtils.Multiply(ref xf1.R, _localAnchor1 - b1.GetLocalCenter());
  318. Vector2 r2 = MathUtils.Multiply(ref xf2.R, _localAnchor2 - b2.GetLocalCenter());
  319. Vector2 p1 = b1._sweep.c + r1;
  320. Vector2 p2 = b2._sweep.c + r2;
  321. // Get the pulley axes.
  322. _u1 = p1 - s1;
  323. _u2 = p2 - s2;
  324. float length1 = _u1.Length();
  325. float length2 = _u2.Length();
  326. if (length1 > Settings.b2_linearSlop)
  327. {
  328. _u1 *= 1.0f / length1;
  329. }
  330. else
  331. {
  332. _u1 = Vector2.Zero;
  333. }
  334. if (length2 > Settings.b2_linearSlop)
  335. {
  336. _u2 *= 1.0f / length2;
  337. }
  338. else
  339. {
  340. _u2 = Vector2.Zero;
  341. }
  342. float C = _ant - length1 - _ratio * length2;
  343. linearError = Math.Max(linearError, -C);
  344. C = MathUtils.Clamp(C + Settings.b2_linearSlop, -Settings.b2_maxLinearCorrection, 0.0f);
  345. float impulse = -_pulleyMass * C;
  346. Vector2 P1 = -impulse * _u1;
  347. Vector2 P2 = -_ratio * impulse * _u2;
  348. b1._sweep.c += b1._invMass * P1;
  349. b1._sweep.a += b1._invI * MathUtils.Cross(r1, P1);
  350. b2._sweep.c += b2._invMass * P2;
  351. b2._sweep.a += b2._invI * MathUtils.Cross(r2, P2);
  352. b1.SynchronizeTransform();
  353. b2.SynchronizeTransform();
  354. }
  355. if (_limitState1 == LimitState.AtUpper)
  356. {
  357. Transform xf1;
  358. b1.GetTransform(out xf1);
  359. Vector2 r1 = MathUtils.Multiply(ref xf1.R, _localAnchor1 - b1.GetLocalCenter());
  360. Vector2 p1 = b1._sweep.c + r1;
  361. _u1 = p1 - s1;
  362. float length1 = _u1.Length();
  363. if (length1 > Settings.b2_linearSlop)
  364. {
  365. _u1 *= 1.0f / length1;
  366. }
  367. else
  368. {
  369. _u1 = Vector2.Zero;
  370. }
  371. float C = _maxLength1 - length1;
  372. linearError = Math.Max(linearError, -C);
  373. C = MathUtils.Clamp(C + Settings.b2_linearSlop, -Settings.b2_maxLinearCorrection, 0.0f);
  374. float impulse = -_limitMass1 * C;
  375. Vector2 P1 = -impulse * _u1;
  376. b1._sweep.c += b1._invMass * P1;
  377. b1._sweep.a += b1._invI * MathUtils.Cross(r1, P1);
  378. b1.SynchronizeTransform();
  379. }
  380. if (_limitState2 == LimitState.AtUpper)
  381. {
  382. Transform xf2;
  383. b2.GetTransform(out xf2);
  384. Vector2 r2 = MathUtils.Multiply(ref xf2.R, _localAnchor2 - b2.GetLocalCenter());
  385. Vector2 p2 = b2._sweep.c + r2;
  386. _u2 = p2 - s2;
  387. float length2 = _u2.Length();
  388. if (length2 > Settings.b2_linearSlop)
  389. {
  390. _u2 *= 1.0f / length2;
  391. }
  392. else
  393. {
  394. _u2 = Vector2.Zero;
  395. }
  396. float C = _maxLength2 - length2;
  397. linearError = Math.Max(linearError, -C);
  398. C = MathUtils.Clamp(C + Settings.b2_linearSlop, -Settings.b2_maxLinearCorrection, 0.0f);
  399. float impulse = -_limitMass2 * C;
  400. Vector2 P2 = -impulse * _u2;
  401. b2._sweep.c += b2._invMass * P2;
  402. b2._sweep.a += b2._invI * MathUtils.Cross(r2, P2);
  403. b2.SynchronizeTransform();
  404. }
  405. return linearError < Settings.b2_linearSlop;
  406. }
  407. internal Vector2 _groundAnchor1;
  408. internal Vector2 _groundAnchor2;
  409. internal Vector2 _localAnchor1;
  410. internal Vector2 _localAnchor2;
  411. internal Vector2 _u1;
  412. internal Vector2 _u2;
  413. internal float _ant;
  414. internal float _ratio;
  415. internal float _maxLength1;
  416. internal float _maxLength2;
  417. // Effective masses
  418. internal float _pulleyMass;
  419. internal float _limitMass1;
  420. internal float _limitMass2;
  421. // Impulses for accumulation/warm starting.
  422. internal float _impulse;
  423. internal float _limitImpulse1;
  424. internal float _limitImpulse2;
  425. internal LimitState _state;
  426. internal LimitState _limitState1;
  427. internal LimitState _limitState2;
  428. };
  429. }