b2PrismaticJoint.cpp
上传用户:gb3593
上传日期:2022-01-07
资源大小:3028k
文件大小:16k
源码类别:

游戏引擎

开发平台:

Visual C++

  1. /*
  2. * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty.  In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. * Permission is granted to anyone to use this software for any purpose,
  8. * including commercial applications, and to alter it and redistribute it
  9. * freely, subject to the following restrictions:
  10. * 1. The origin of this software must not be misrepresented; you must not
  11. * claim that you wrote the original software. If you use this software
  12. * in a product, an acknowledgment in the product documentation would be
  13. * appreciated but is not required.
  14. * 2. Altered source versions must be plainly marked as such, and must not be
  15. * misrepresented as being the original software.
  16. * 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include <Box2D/Dynamics/Joints/b2PrismaticJoint.h>
  19. #include <Box2D/Dynamics/b2Body.h>
  20. #include <Box2D/Dynamics/b2TimeStep.h>
  21. // Linear constraint (point-to-line)
  22. // d = p2 - p1 = x2 + r2 - x1 - r1
  23. // C = dot(perp, d)
  24. // Cdot = dot(d, cross(w1, perp)) + dot(perp, v2 + cross(w2, r2) - v1 - cross(w1, r1))
  25. //      = -dot(perp, v1) - dot(cross(d + r1, perp), w1) + dot(perp, v2) + dot(cross(r2, perp), v2)
  26. // J = [-perp, -cross(d + r1, perp), perp, cross(r2,perp)]
  27. //
  28. // Angular constraint
  29. // C = a2 - a1 + a_initial
  30. // Cdot = w2 - w1
  31. // J = [0 0 -1 0 0 1]
  32. //
  33. // K = J * invM * JT
  34. //
  35. // J = [-a -s1 a s2]
  36. //     [0  -1  0  1]
  37. // a = perp
  38. // s1 = cross(d + r1, a) = cross(p2 - x1, a)
  39. // s2 = cross(r2, a) = cross(p2 - x2, a)
  40. // Motor/Limit linear constraint
  41. // C = dot(ax1, d)
  42. // Cdot = = -dot(ax1, v1) - dot(cross(d + r1, ax1), w1) + dot(ax1, v2) + dot(cross(r2, ax1), v2)
  43. // J = [-ax1 -cross(d+r1,ax1) ax1 cross(r2,ax1)]
  44. // Block Solver
  45. // We develop a block solver that includes the joint limit. This makes the limit stiff (inelastic) even
  46. // when the mass has poor distribution (leading to large torques about the joint anchor points).
  47. //
  48. // The Jacobian has 3 rows:
  49. // J = [-uT -s1 uT s2] // linear
  50. //     [0   -1   0  1] // angular
  51. //     [-vT -a1 vT a2] // limit
  52. //
  53. // u = perp
  54. // v = axis
  55. // s1 = cross(d + r1, u), s2 = cross(r2, u)
  56. // a1 = cross(d + r1, v), a2 = cross(r2, v)
  57. // M * (v2 - v1) = JT * df
  58. // J * v2 = bias
  59. //
  60. // v2 = v1 + invM * JT * df
  61. // J * (v1 + invM * JT * df) = bias
  62. // K * df = bias - J * v1 = -Cdot
  63. // K = J * invM * JT
  64. // Cdot = J * v1 - bias
  65. //
  66. // Now solve for f2.
  67. // df = f2 - f1
  68. // K * (f2 - f1) = -Cdot
  69. // f2 = invK * (-Cdot) + f1
  70. //
  71. // Clamp accumulated limit impulse.
  72. // lower: f2(3) = max(f2(3), 0)
  73. // upper: f2(3) = min(f2(3), 0)
  74. //
  75. // Solve for correct f2(1:2)
  76. // K(1:2, 1:2) * f2(1:2) = -Cdot(1:2) - K(1:2,3) * f2(3) + K(1:2,1:3) * f1
  77. //                       = -Cdot(1:2) - K(1:2,3) * f2(3) + K(1:2,1:2) * f1(1:2) + K(1:2,3) * f1(3)
  78. // K(1:2, 1:2) * f2(1:2) = -Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3)) + K(1:2,1:2) * f1(1:2)
  79. // f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) + f1(1:2)
  80. //
  81. // Now compute impulse to be applied:
  82. // df = f2 - f1
  83. void b2PrismaticJointDef::Initialize(b2Body* b1, b2Body* b2, const b2Vec2& anchor, const b2Vec2& axis)
  84. {
  85. bodyA = b1;
  86. bodyB = b2;
  87. localAnchorA = bodyA->GetLocalPoint(anchor);
  88. localAnchorB = bodyB->GetLocalPoint(anchor);
  89. localAxis1 = bodyA->GetLocalVector(axis);
  90. referenceAngle = bodyB->GetAngle() - bodyA->GetAngle();
  91. }
  92. b2PrismaticJoint::b2PrismaticJoint(const b2PrismaticJointDef* def)
  93. : b2Joint(def)
  94. {
  95. m_localAnchor1 = def->localAnchorA;
  96. m_localAnchor2 = def->localAnchorB;
  97. m_localXAxis1 = def->localAxis1;
  98. m_localYAxis1 = b2Cross(1.0f, m_localXAxis1);
  99. m_refAngle = def->referenceAngle;
  100. m_impulse.SetZero();
  101. m_motorMass = 0.0;
  102. m_motorImpulse = 0.0f;
  103. m_lowerTranslation = def->lowerTranslation;
  104. m_upperTranslation = def->upperTranslation;
  105. m_maxMotorForce = def->maxMotorForce;
  106. m_motorSpeed = def->motorSpeed;
  107. m_enableLimit = def->enableLimit;
  108. m_enableMotor = def->enableMotor;
  109. m_limitState = e_inactiveLimit;
  110. m_axis.SetZero();
  111. m_perp.SetZero();
  112. }
  113. void b2PrismaticJoint::InitVelocityConstraints(const b2TimeStep& step)
  114. {
  115. b2Body* b1 = m_bodyA;
  116. b2Body* b2 = m_bodyB;
  117. m_localCenterA = b1->GetLocalCenter();
  118. m_localCenterB = b2->GetLocalCenter();
  119. b2Transform xf1 = b1->GetTransform();
  120. b2Transform xf2 = b2->GetTransform();
  121. // Compute the effective masses.
  122. b2Vec2 r1 = b2Mul(xf1.R, m_localAnchor1 - m_localCenterA);
  123. b2Vec2 r2 = b2Mul(xf2.R, m_localAnchor2 - m_localCenterB);
  124. b2Vec2 d = b2->m_sweep.c + r2 - b1->m_sweep.c - r1;
  125. m_invMassA = b1->m_invMass;
  126. m_invIA = b1->m_invI;
  127. m_invMassB = b2->m_invMass;
  128. m_invIB = b2->m_invI;
  129. // Compute motor Jacobian and effective mass.
  130. {
  131. m_axis = b2Mul(xf1.R, m_localXAxis1);
  132. m_a1 = b2Cross(d + r1, m_axis);
  133. m_a2 = b2Cross(r2, m_axis);
  134. m_motorMass = m_invMassA + m_invMassB + m_invIA * m_a1 * m_a1 + m_invIB * m_a2 * m_a2;
  135. if (m_motorMass > b2_epsilon)
  136. {
  137. m_motorMass = 1.0f / m_motorMass;
  138. }
  139. }
  140. // Prismatic constraint.
  141. {
  142. m_perp = b2Mul(xf1.R, m_localYAxis1);
  143. m_s1 = b2Cross(d + r1, m_perp);
  144. m_s2 = b2Cross(r2, m_perp);
  145. float32 m1 = m_invMassA, m2 = m_invMassB;
  146. float32 i1 = m_invIA, i2 = m_invIB;
  147. float32 k11 = m1 + m2 + i1 * m_s1 * m_s1 + i2 * m_s2 * m_s2;
  148. float32 k12 = i1 * m_s1 + i2 * m_s2;
  149. float32 k13 = i1 * m_s1 * m_a1 + i2 * m_s2 * m_a2;
  150. float32 k22 = i1 + i2;
  151. float32 k23 = i1 * m_a1 + i2 * m_a2;
  152. float32 k33 = m1 + m2 + i1 * m_a1 * m_a1 + i2 * m_a2 * m_a2;
  153. m_K.col1.Set(k11, k12, k13);
  154. m_K.col2.Set(k12, k22, k23);
  155. m_K.col3.Set(k13, k23, k33);
  156. }
  157. // Compute motor and limit terms.
  158. if (m_enableLimit)
  159. {
  160. float32 jointTranslation = b2Dot(m_axis, d);
  161. if (b2Abs(m_upperTranslation - m_lowerTranslation) < 2.0f * b2_linearSlop)
  162. {
  163. m_limitState = e_equalLimits;
  164. }
  165. else if (jointTranslation <= m_lowerTranslation)
  166. {
  167. if (m_limitState != e_atLowerLimit)
  168. {
  169. m_limitState = e_atLowerLimit;
  170. m_impulse.z = 0.0f;
  171. }
  172. }
  173. else if (jointTranslation >= m_upperTranslation)
  174. {
  175. if (m_limitState != e_atUpperLimit)
  176. {
  177. m_limitState = e_atUpperLimit;
  178. m_impulse.z = 0.0f;
  179. }
  180. }
  181. else
  182. {
  183. m_limitState = e_inactiveLimit;
  184. m_impulse.z = 0.0f;
  185. }
  186. }
  187. else
  188. {
  189. m_limitState = e_inactiveLimit;
  190. m_impulse.z = 0.0f;
  191. }
  192. if (m_enableMotor == false)
  193. {
  194. m_motorImpulse = 0.0f;
  195. }
  196. if (step.warmStarting)
  197. {
  198. // Account for variable time step.
  199. m_impulse *= step.dtRatio;
  200. m_motorImpulse *= step.dtRatio;
  201. b2Vec2 P = m_impulse.x * m_perp + (m_motorImpulse + m_impulse.z) * m_axis;
  202. float32 L1 = m_impulse.x * m_s1 + m_impulse.y + (m_motorImpulse + m_impulse.z) * m_a1;
  203. float32 L2 = m_impulse.x * m_s2 + m_impulse.y + (m_motorImpulse + m_impulse.z) * m_a2;
  204. b1->m_linearVelocity -= m_invMassA * P;
  205. b1->m_angularVelocity -= m_invIA * L1;
  206. b2->m_linearVelocity += m_invMassB * P;
  207. b2->m_angularVelocity += m_invIB * L2;
  208. }
  209. else
  210. {
  211. m_impulse.SetZero();
  212. m_motorImpulse = 0.0f;
  213. }
  214. }
  215. void b2PrismaticJoint::SolveVelocityConstraints(const b2TimeStep& step)
  216. {
  217. b2Body* b1 = m_bodyA;
  218. b2Body* b2 = m_bodyB;
  219. b2Vec2 v1 = b1->m_linearVelocity;
  220. float32 w1 = b1->m_angularVelocity;
  221. b2Vec2 v2 = b2->m_linearVelocity;
  222. float32 w2 = b2->m_angularVelocity;
  223. // Solve linear motor constraint.
  224. if (m_enableMotor && m_limitState != e_equalLimits)
  225. {
  226. float32 Cdot = b2Dot(m_axis, v2 - v1) + m_a2 * w2 - m_a1 * w1;
  227. float32 impulse = m_motorMass * (m_motorSpeed - Cdot);
  228. float32 oldImpulse = m_motorImpulse;
  229. float32 maxImpulse = step.dt * m_maxMotorForce;
  230. m_motorImpulse = b2Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse);
  231. impulse = m_motorImpulse - oldImpulse;
  232. b2Vec2 P = impulse * m_axis;
  233. float32 L1 = impulse * m_a1;
  234. float32 L2 = impulse * m_a2;
  235. v1 -= m_invMassA * P;
  236. w1 -= m_invIA * L1;
  237. v2 += m_invMassB * P;
  238. w2 += m_invIB * L2;
  239. }
  240. b2Vec2 Cdot1;
  241. Cdot1.x = b2Dot(m_perp, v2 - v1) + m_s2 * w2 - m_s1 * w1;
  242. Cdot1.y = w2 - w1;
  243. if (m_enableLimit && m_limitState != e_inactiveLimit)
  244. {
  245. // Solve prismatic and limit constraint in block form.
  246. float32 Cdot2;
  247. Cdot2 = b2Dot(m_axis, v2 - v1) + m_a2 * w2 - m_a1 * w1;
  248. b2Vec3 Cdot(Cdot1.x, Cdot1.y, Cdot2);
  249. b2Vec3 f1 = m_impulse;
  250. b2Vec3 df =  m_K.Solve33(-Cdot);
  251. m_impulse += df;
  252. if (m_limitState == e_atLowerLimit)
  253. {
  254. m_impulse.z = b2Max(m_impulse.z, 0.0f);
  255. }
  256. else if (m_limitState == e_atUpperLimit)
  257. {
  258. m_impulse.z = b2Min(m_impulse.z, 0.0f);
  259. }
  260. // f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) + f1(1:2)
  261. b2Vec2 b = -Cdot1 - (m_impulse.z - f1.z) * b2Vec2(m_K.col3.x, m_K.col3.y);
  262. b2Vec2 f2r = m_K.Solve22(b) + b2Vec2(f1.x, f1.y);
  263. m_impulse.x = f2r.x;
  264. m_impulse.y = f2r.y;
  265. df = m_impulse - f1;
  266. b2Vec2 P = df.x * m_perp + df.z * m_axis;
  267. float32 L1 = df.x * m_s1 + df.y + df.z * m_a1;
  268. float32 L2 = df.x * m_s2 + df.y + df.z * m_a2;
  269. v1 -= m_invMassA * P;
  270. w1 -= m_invIA * L1;
  271. v2 += m_invMassB * P;
  272. w2 += m_invIB * L2;
  273. }
  274. else
  275. {
  276. // Limit is inactive, just solve the prismatic constraint in block form.
  277. b2Vec2 df = m_K.Solve22(-Cdot1);
  278. m_impulse.x += df.x;
  279. m_impulse.y += df.y;
  280. b2Vec2 P = df.x * m_perp;
  281. float32 L1 = df.x * m_s1 + df.y;
  282. float32 L2 = df.x * m_s2 + df.y;
  283. v1 -= m_invMassA * P;
  284. w1 -= m_invIA * L1;
  285. v2 += m_invMassB * P;
  286. w2 += m_invIB * L2;
  287. }
  288. b1->m_linearVelocity = v1;
  289. b1->m_angularVelocity = w1;
  290. b2->m_linearVelocity = v2;
  291. b2->m_angularVelocity = w2;
  292. }
  293. bool b2PrismaticJoint::SolvePositionConstraints(float32 baumgarte)
  294. {
  295. B2_NOT_USED(baumgarte);
  296. b2Body* b1 = m_bodyA;
  297. b2Body* b2 = m_bodyB;
  298. b2Vec2 c1 = b1->m_sweep.c;
  299. float32 a1 = b1->m_sweep.a;
  300. b2Vec2 c2 = b2->m_sweep.c;
  301. float32 a2 = b2->m_sweep.a;
  302. // Solve linear limit constraint.
  303. float32 linearError = 0.0f, angularError = 0.0f;
  304. bool active = false;
  305. float32 C2 = 0.0f;
  306. b2Mat22 R1(a1), R2(a2);
  307. b2Vec2 r1 = b2Mul(R1, m_localAnchor1 - m_localCenterA);
  308. b2Vec2 r2 = b2Mul(R2, m_localAnchor2 - m_localCenterB);
  309. b2Vec2 d = c2 + r2 - c1 - r1;
  310. if (m_enableLimit)
  311. {
  312. m_axis = b2Mul(R1, m_localXAxis1);
  313. m_a1 = b2Cross(d + r1, m_axis);
  314. m_a2 = b2Cross(r2, m_axis);
  315. float32 translation = b2Dot(m_axis, d);
  316. if (b2Abs(m_upperTranslation - m_lowerTranslation) < 2.0f * b2_linearSlop)
  317. {
  318. // Prevent large angular corrections
  319. C2 = b2Clamp(translation, -b2_maxLinearCorrection, b2_maxLinearCorrection);
  320. linearError = b2Abs(translation);
  321. active = true;
  322. }
  323. else if (translation <= m_lowerTranslation)
  324. {
  325. // Prevent large linear corrections and allow some slop.
  326. C2 = b2Clamp(translation - m_lowerTranslation + b2_linearSlop, -b2_maxLinearCorrection, 0.0f);
  327. linearError = m_lowerTranslation - translation;
  328. active = true;
  329. }
  330. else if (translation >= m_upperTranslation)
  331. {
  332. // Prevent large linear corrections and allow some slop.
  333. C2 = b2Clamp(translation - m_upperTranslation - b2_linearSlop, 0.0f, b2_maxLinearCorrection);
  334. linearError = translation - m_upperTranslation;
  335. active = true;
  336. }
  337. }
  338. m_perp = b2Mul(R1, m_localYAxis1);
  339. m_s1 = b2Cross(d + r1, m_perp);
  340. m_s2 = b2Cross(r2, m_perp);
  341. b2Vec3 impulse;
  342. b2Vec2 C1;
  343. C1.x = b2Dot(m_perp, d);
  344. C1.y = a2 - a1 - m_refAngle;
  345. linearError = b2Max(linearError, b2Abs(C1.x));
  346. angularError = b2Abs(C1.y);
  347. if (active)
  348. {
  349. float32 m1 = m_invMassA, m2 = m_invMassB;
  350. float32 i1 = m_invIA, i2 = m_invIB;
  351. float32 k11 = m1 + m2 + i1 * m_s1 * m_s1 + i2 * m_s2 * m_s2;
  352. float32 k12 = i1 * m_s1 + i2 * m_s2;
  353. float32 k13 = i1 * m_s1 * m_a1 + i2 * m_s2 * m_a2;
  354. float32 k22 = i1 + i2;
  355. float32 k23 = i1 * m_a1 + i2 * m_a2;
  356. float32 k33 = m1 + m2 + i1 * m_a1 * m_a1 + i2 * m_a2 * m_a2;
  357. m_K.col1.Set(k11, k12, k13);
  358. m_K.col2.Set(k12, k22, k23);
  359. m_K.col3.Set(k13, k23, k33);
  360. b2Vec3 C;
  361. C.x = C1.x;
  362. C.y = C1.y;
  363. C.z = C2;
  364. impulse = m_K.Solve33(-C);
  365. }
  366. else
  367. {
  368. float32 m1 = m_invMassA, m2 = m_invMassB;
  369. float32 i1 = m_invIA, i2 = m_invIB;
  370. float32 k11 = m1 + m2 + i1 * m_s1 * m_s1 + i2 * m_s2 * m_s2;
  371. float32 k12 = i1 * m_s1 + i2 * m_s2;
  372. float32 k22 = i1 + i2;
  373. m_K.col1.Set(k11, k12, 0.0f);
  374. m_K.col2.Set(k12, k22, 0.0f);
  375. b2Vec2 impulse1 = m_K.Solve22(-C1);
  376. impulse.x = impulse1.x;
  377. impulse.y = impulse1.y;
  378. impulse.z = 0.0f;
  379. }
  380. b2Vec2 P = impulse.x * m_perp + impulse.z * m_axis;
  381. float32 L1 = impulse.x * m_s1 + impulse.y + impulse.z * m_a1;
  382. float32 L2 = impulse.x * m_s2 + impulse.y + impulse.z * m_a2;
  383. c1 -= m_invMassA * P;
  384. a1 -= m_invIA * L1;
  385. c2 += m_invMassB * P;
  386. a2 += m_invIB * L2;
  387. // TODO_ERIN remove need for this.
  388. b1->m_sweep.c = c1;
  389. b1->m_sweep.a = a1;
  390. b2->m_sweep.c = c2;
  391. b2->m_sweep.a = a2;
  392. b1->SynchronizeTransform();
  393. b2->SynchronizeTransform();
  394. return linearError <= b2_linearSlop && angularError <= b2_angularSlop;
  395. }
  396. b2Vec2 b2PrismaticJoint::GetAnchorA() const
  397. {
  398. return m_bodyA->GetWorldPoint(m_localAnchor1);
  399. }
  400. b2Vec2 b2PrismaticJoint::GetAnchorB() const
  401. {
  402. return m_bodyB->GetWorldPoint(m_localAnchor2);
  403. }
  404. b2Vec2 b2PrismaticJoint::GetReactionForce(float32 inv_dt) const
  405. {
  406. return inv_dt * (m_impulse.x * m_perp + (m_motorImpulse + m_impulse.z) * m_axis);
  407. }
  408. float32 b2PrismaticJoint::GetReactionTorque(float32 inv_dt) const
  409. {
  410. return inv_dt * m_impulse.y;
  411. }
  412. float32 b2PrismaticJoint::GetJointTranslation() const
  413. {
  414. b2Body* b1 = m_bodyA;
  415. b2Body* b2 = m_bodyB;
  416. b2Vec2 p1 = b1->GetWorldPoint(m_localAnchor1);
  417. b2Vec2 p2 = b2->GetWorldPoint(m_localAnchor2);
  418. b2Vec2 d = p2 - p1;
  419. b2Vec2 axis = b1->GetWorldVector(m_localXAxis1);
  420. float32 translation = b2Dot(d, axis);
  421. return translation;
  422. }
  423. float32 b2PrismaticJoint::GetJointSpeed() const
  424. {
  425. b2Body* b1 = m_bodyA;
  426. b2Body* b2 = m_bodyB;
  427. b2Vec2 r1 = b2Mul(b1->GetTransform().R, m_localAnchor1 - b1->GetLocalCenter());
  428. b2Vec2 r2 = b2Mul(b2->GetTransform().R, m_localAnchor2 - b2->GetLocalCenter());
  429. b2Vec2 p1 = b1->m_sweep.c + r1;
  430. b2Vec2 p2 = b2->m_sweep.c + r2;
  431. b2Vec2 d = p2 - p1;
  432. b2Vec2 axis = b1->GetWorldVector(m_localXAxis1);
  433. b2Vec2 v1 = b1->m_linearVelocity;
  434. b2Vec2 v2 = b2->m_linearVelocity;
  435. float32 w1 = b1->m_angularVelocity;
  436. float32 w2 = b2->m_angularVelocity;
  437. float32 speed = b2Dot(d, b2Cross(w1, axis)) + b2Dot(axis, v2 + b2Cross(w2, r2) - v1 - b2Cross(w1, r1));
  438. return speed;
  439. }
  440. bool b2PrismaticJoint::IsLimitEnabled() const
  441. {
  442. return m_enableLimit;
  443. }
  444. void b2PrismaticJoint::EnableLimit(bool flag)
  445. {
  446. m_bodyA->SetAwake(true);
  447. m_bodyB->SetAwake(true);
  448. m_enableLimit = flag;
  449. }
  450. float32 b2PrismaticJoint::GetLowerLimit() const
  451. {
  452. return m_lowerTranslation;
  453. }
  454. float32 b2PrismaticJoint::GetUpperLimit() const
  455. {
  456. return m_upperTranslation;
  457. }
  458. void b2PrismaticJoint::SetLimits(float32 lower, float32 upper)
  459. {
  460. b2Assert(lower <= upper);
  461. m_bodyA->SetAwake(true);
  462. m_bodyB->SetAwake(true);
  463. m_lowerTranslation = lower;
  464. m_upperTranslation = upper;
  465. }
  466. bool b2PrismaticJoint::IsMotorEnabled() const
  467. {
  468. return m_enableMotor;
  469. }
  470. void b2PrismaticJoint::EnableMotor(bool flag)
  471. {
  472. m_bodyA->SetAwake(true);
  473. m_bodyB->SetAwake(true);
  474. m_enableMotor = flag;
  475. }
  476. void b2PrismaticJoint::SetMotorSpeed(float32 speed)
  477. {
  478. m_bodyA->SetAwake(true);
  479. m_bodyB->SetAwake(true);
  480. m_motorSpeed = speed;
  481. }
  482. void b2PrismaticJoint::SetMaxMotorForce(float32 force)
  483. {
  484. m_bodyA->SetAwake(true);
  485. m_bodyB->SetAwake(true);
  486. m_maxMotorForce = force;
  487. }
  488. float32 b2PrismaticJoint::GetMotorForce() const
  489. {
  490. return m_motorImpulse;
  491. }