PageRenderTime 669ms CodeModel.GetById 657ms RepoModel.GetById 0ms app.codeStats 0ms

/PhysicsEngines/Farseer/FarseerBody.cs

#
C# | 618 lines | 401 code | 62 blank | 155 comment | 3 complexity | 3663254d62ecb841413f79b5f99682af MD5 | raw file
Possible License(s): Apache-2.0
  1. using System.Collections.Generic;
  2. using Delta.PhysicsEngines.Enums;
  3. using Delta.Utilities.Datatypes;
  4. using Delta.Utilities.Helpers;
  5. using FarseerPhysics.Common;
  6. using FarseerPhysics.Common.Decomposition;
  7. using FarseerPhysics.Dynamics;
  8. using FarseerPhysics.Dynamics.Contacts;
  9. using FarseerPhysics.Factories;
  10. namespace Delta.PhysicsEngines.Farseer
  11. {
  12. /// <summary>
  13. /// Farseer body implementation.
  14. /// </summary>
  15. internal class FarseerBody : PhysicsBody
  16. {
  17. #region Constants
  18. /// <summary>
  19. /// Default bounding box.
  20. /// </summary>
  21. private static readonly BoundingBox DefaultBoundingBox =
  22. new BoundingBox(Vector.Zero, Vector.One);
  23. #endregion
  24. #region Body (Public)
  25. /// <summary>
  26. /// Gets the Farseer native body (read-only).
  27. /// </summary>
  28. public Body Body
  29. {
  30. get
  31. {
  32. return farseerBody;
  33. }
  34. }
  35. #endregion
  36. #region Position (Public)
  37. /// <summary>
  38. /// Position given in world space.
  39. /// </summary>
  40. public override Vector Position
  41. {
  42. get
  43. {
  44. FarseerDatatypesMapping.Convert(farseerBody.Position, out position);
  45. return position * FarseerPhysics.InvScaleFactor;
  46. }
  47. set
  48. {
  49. farseerBody.Position = FarseerDatatypesMapping.Convert(ref value) *
  50. FarseerPhysics.ScaleFactor;
  51. }
  52. }
  53. #endregion
  54. #region Position2D (Public)
  55. /// <summary>
  56. /// Position 2D
  57. /// </summary>
  58. public override Point Position2D
  59. {
  60. get
  61. {
  62. var farseerVector2 = farseerBody.Position;
  63. return new Point(farseerVector2.X, farseerVector2.Y) *
  64. FarseerPhysics.InvScaleFactor;
  65. }
  66. set
  67. {
  68. farseerBody.Position = FarseerDatatypesMapping.Convert(ref value) *
  69. FarseerPhysics.ScaleFactor;
  70. }
  71. }
  72. #endregion
  73. #region Rotation (Public)
  74. /// <summary>
  75. /// Rotation angle in degrees for 2D bodies.
  76. /// </summary>
  77. public override float Rotation
  78. {
  79. get
  80. {
  81. return farseerBody.Rotation.RadiansToDegrees();
  82. }
  83. set
  84. {
  85. farseerBody.Rotation = value.DegreeToRadians();
  86. }
  87. }
  88. #endregion
  89. #region LinearVelocity (Public)
  90. /// <summary>
  91. /// The velocity of the body.
  92. /// </summary>
  93. /// <value>
  94. /// The linear velocity.
  95. /// </value>
  96. public override Vector LinearVelocity
  97. {
  98. get
  99. {
  100. FarseerDatatypesMapping.Convert(farseerBody.LinearVelocity,
  101. out linearVelocity);
  102. return linearVelocity;
  103. }
  104. set
  105. {
  106. farseerBody.LinearVelocity =
  107. FarseerDatatypesMapping.Convert(ref value);
  108. }
  109. }
  110. #endregion
  111. #region AngularVelocity (Public)
  112. /// <summary>
  113. /// Gets or Sets the angular velocity of the body.
  114. /// </summary>
  115. /// <remarks>
  116. /// For 2D physics simulation only X component is used.
  117. /// </remarks>
  118. public override Vector AngularVelocity
  119. {
  120. get
  121. {
  122. angularVelocity.X = farseerBody.AngularVelocity.RadiansToDegrees();
  123. return angularVelocity;
  124. }
  125. set
  126. {
  127. farseerBody.AngularVelocity = value.X.DegreeToRadians();
  128. }
  129. }
  130. #endregion
  131. #region AngularVelocity2D (Public)
  132. /// <summary>
  133. /// Angular velocity 2D as a float, for 2D only the .x component is used!
  134. /// </summary>
  135. public override float AngularVelocity2D
  136. {
  137. get
  138. {
  139. // Convert Farseers Radians into degrees as used by the engine
  140. return farseerBody.AngularVelocity.RadiansToDegrees();
  141. }
  142. set
  143. {
  144. // Convert to Farseers Radians
  145. farseerBody.AngularVelocity = value.DegreeToRadians();
  146. }
  147. }
  148. #endregion
  149. #region Mass (Public)
  150. /// <summary>
  151. /// Gets or sets the mass. Usually in kilograms (kg).
  152. /// </summary>
  153. public override float Mass
  154. {
  155. get
  156. {
  157. return base.Mass;
  158. }
  159. set
  160. {
  161. farseerBody.Mass = value;
  162. }
  163. }
  164. #endregion
  165. #region Restitution (Public)
  166. /// <summary>
  167. /// Gets the restitution of the body.
  168. /// </summary>
  169. public override float Restitution
  170. {
  171. get
  172. {
  173. return base.Restitution;
  174. }
  175. set
  176. {
  177. farseerBody.Restitution = value;
  178. base.Restitution = value;
  179. }
  180. }
  181. #endregion
  182. #region BoundingBox (Public)
  183. /// <summary>
  184. /// Gets the BoundingBox of the body.
  185. /// </summary>
  186. public override BoundingBox BoundingBox
  187. {
  188. get
  189. {
  190. // Zero bounding box
  191. return DefaultBoundingBox;
  192. }
  193. }
  194. #endregion
  195. #region Private
  196. #region farseerBody (Private)
  197. /// <summary>
  198. /// Farseer body.
  199. /// </summary>
  200. private readonly Body farseerBody;
  201. #endregion
  202. #region farseerFixtures (Private)
  203. /// <summary>
  204. /// List of Farseer fixtures.
  205. /// </summary>
  206. private List<Fixture> farseerFixtures;
  207. #endregion
  208. #region physicsManager (Private)
  209. /// <summary>
  210. /// Our Farseer physics implementation.
  211. /// </summary>
  212. private FarseerPhysics physicsManager;
  213. #endregion
  214. #region position (Private)
  215. private Vector position;
  216. #endregion
  217. #region linearVelocity (Private)
  218. private Vector linearVelocity;
  219. #endregion
  220. #region angularVelocity (Private)
  221. private Vector angularVelocity;
  222. #endregion
  223. #endregion
  224. #region Constructors
  225. /// <summary>
  226. /// Initializes a new instance of the <see cref="FarseerBody"/> class.
  227. /// </summary>
  228. /// <param name="physicsManager">The physics manager.</param>
  229. /// <param name="shape">The shape.</param>
  230. /// <param name="initialPosition">Body initial position.</param>
  231. public FarseerBody(FarseerPhysics physicsManager,
  232. PhysicsShape shape, Vector initialPosition)
  233. : base(true, shape, initialPosition)
  234. {
  235. this.physicsManager = physicsManager;
  236. farseerFixtures = new List<Fixture>();
  237. // First create body.
  238. farseerBody = BodyFactory.CreateBody(physicsManager.farseerPhysicsWorld);
  239. farseerBody.Position =
  240. FarseerDatatypesMapping.Convert(ref initialPosition);
  241. farseerBody.Mass = base.mass;
  242. farseerBody.Restitution = base.restitution;
  243. // Now create shape.
  244. CreateShape();
  245. // Attach events
  246. Initialize();
  247. // By default is not static.
  248. SetIsStatic(false);
  249. }
  250. #endregion
  251. #region Dispose (Public)
  252. /// <summary>
  253. /// Handle disposing.
  254. /// </summary>
  255. public override void Dispose()
  256. {
  257. Remove();
  258. }
  259. #endregion
  260. #region ApplyForce (Public)
  261. /// <summary>
  262. /// Applies a force at the center of mass.
  263. /// </summary>
  264. /// <param name="force">
  265. /// Vector containing force data to apply to body in 3D space.
  266. /// </param>
  267. public override void ApplyForce(Vector force)
  268. {
  269. var farseerForce = FarseerDatatypesMapping.Convert(ref force);
  270. farseerBody.ApplyForce(ref farseerForce);
  271. }
  272. /// <summary>
  273. /// Apply a force at a world point. If the force is not
  274. /// applied at the center of mass, it will generate a torque and
  275. /// affect the angular velocity. This wakes up the body.
  276. /// </summary>
  277. /// <param name="force">
  278. /// Vector containing force data to apply to body in 3D space.
  279. /// </param>
  280. /// <param name="position">
  281. /// Relative position from where to apply force.
  282. /// </param>
  283. public override void ApplyForce(Vector force, Vector position)
  284. {
  285. var farseerForce = FarseerDatatypesMapping.Convert(ref force);
  286. var farseerPosition = FarseerDatatypesMapping.Convert(ref position);
  287. farseerBody.ApplyForce(ref farseerForce, ref farseerPosition);
  288. }
  289. #endregion
  290. #region ApplyTorque (Public)
  291. /// <summary>
  292. /// Apply a torque. This affects the angular velocity without affecting the
  293. /// linear velocity of the center of mass.
  294. /// </summary>
  295. /// <remarks>
  296. /// This wakes up the body.
  297. /// </remarks>
  298. /// <param name="torque">
  299. /// Vector containing torque data for both 2D and 3D shapes.
  300. /// </param>
  301. public override void ApplyTorque(Vector torque)
  302. {
  303. // For 2D stuff use only the X component.
  304. farseerBody.ApplyTorque(torque.X);
  305. }
  306. #endregion
  307. #region ApplyLinearImpulse (Public)
  308. /// <summary>
  309. /// Apply an impulse at a point. This immediately modifies the velocity.
  310. /// </summary>
  311. /// <remarks>
  312. /// This wakes up the body.
  313. /// </remarks>
  314. /// <param name="impulse">The impulse vector in 3D space.</param>
  315. public override void ApplyLinearImpulse(Vector impulse)
  316. {
  317. var farseerImpulse = FarseerDatatypesMapping.Convert(ref impulse) *
  318. FarseerPhysics.ScaleFactor;
  319. farseerBody.ApplyLinearImpulse(ref farseerImpulse);
  320. }
  321. /// <summary>
  322. /// Apply an impulse at a point. This immediately modifies the velocity.
  323. /// It also modifies the angular velocity if the point of application
  324. /// is not at the center of mass.
  325. /// </summary>
  326. /// <remarks>
  327. /// This wakes up the body.
  328. /// </remarks>
  329. /// <param name="impulse">The impulse vector in 3D space.</param>
  330. /// <param name="position">Relative position from where to apply.</param>
  331. public override void ApplyLinearImpulse(Vector impulse, Vector position)
  332. {
  333. var farseerImpulse =
  334. FarseerDatatypesMapping.Convert(ref impulse) *
  335. FarseerPhysics.ScaleFactor;
  336. var farseerPosition =
  337. FarseerDatatypesMapping.Convert(ref position);
  338. farseerBody.ApplyLinearImpulse(ref farseerImpulse, ref farseerPosition);
  339. }
  340. #endregion
  341. #region ApplyAngularImpulse (Public)
  342. public override void ApplyAngularImpulse(Vector impulse)
  343. {
  344. farseerBody.ApplyAngularImpulse(impulse.X);
  345. }
  346. #endregion
  347. #region Remove (Public)
  348. /// <summary>
  349. /// Remove
  350. /// </summary>
  351. public override void Remove()
  352. {
  353. farseerBody.OnCollision -= OnColliding;
  354. farseerBody.OnSeparation -= OnSeparation;
  355. base.Remove();
  356. }
  357. #endregion
  358. #region Methods (Private)
  359. #region CreateShape
  360. /// <summary>
  361. /// Creates shape from given properties.
  362. /// </summary>
  363. private void CreateShape()
  364. {
  365. float scaleFactor = FarseerPhysics.ScaleFactor;
  366. Dictionary<PhysicsShape.PropertyType, object> properties = Shape.Properties;
  367. Point tempPoint;
  368. switch (Shape.ShapeType)
  369. {
  370. // Circle
  371. case ShapeType.Circle:
  372. farseerFixtures.Add(FixtureFactory.AttachCircle(
  373. GetSafeFloat(properties, PhysicsShape.PropertyType.Radius) * scaleFactor,
  374. GetSafeFloat(properties, PhysicsShape.PropertyType.Density),
  375. farseerBody));
  376. break;
  377. // Rectangle
  378. case ShapeType.Rectangle:
  379. tempPoint = ArrayHelper.SafeGet<PhysicsShape.PropertyType, Point>(
  380. properties, PhysicsShape.PropertyType.Offset);
  381. farseerFixtures.Add(FixtureFactory.AttachRectangle(
  382. GetSafeFloat(properties, PhysicsShape.PropertyType.Width) * scaleFactor,
  383. GetSafeFloat(properties, PhysicsShape.PropertyType.Height) * scaleFactor,
  384. GetSafeFloat(properties, PhysicsShape.PropertyType.Density),
  385. FarseerDatatypesMapping.Convert(ref tempPoint),
  386. farseerBody));
  387. break;
  388. case ShapeType.Ellipse:
  389. farseerFixtures.Add(FixtureFactory.AttachEllipse(
  390. GetSafeFloat(properties, PhysicsShape.PropertyType.RadiusX) * scaleFactor,
  391. GetSafeFloat(properties, PhysicsShape.PropertyType.RadiusY) * scaleFactor,
  392. ArrayHelper.SafeGet<PhysicsShape.PropertyType, int>(
  393. properties, PhysicsShape.PropertyType.Edges),
  394. GetSafeFloat(properties, PhysicsShape.PropertyType.Density),
  395. farseerBody));
  396. break;
  397. case ShapeType.Polygon:
  398. IList<Point> verticesList =
  399. ArrayHelper.SafeGet<PhysicsShape.PropertyType, IList<Point>>(
  400. properties, PhysicsShape.PropertyType.Vertices);
  401. farseerFixtures.Add(FixtureFactory.AttachPolygon(
  402. new Vertices(FarseerDatatypesMapping.Convert(verticesList)),
  403. GetSafeFloat(properties, PhysicsShape.PropertyType.Density),
  404. farseerBody));
  405. break;
  406. case ShapeType.Compound:
  407. List<IList<Point>> verticesArrayList =
  408. ArrayHelper.SafeGet<PhysicsShape.PropertyType, List<IList<Point>>>(
  409. properties, PhysicsShape.PropertyType.Vertices);
  410. farseerFixtures.Add(FixtureFactory.AttachCompoundPolygon(
  411. FarseerDatatypesMapping.Convert(verticesArrayList),
  412. GetSafeFloat(properties, PhysicsShape.PropertyType.Density),
  413. farseerBody));
  414. break;
  415. case ShapeType.Gear:
  416. farseerFixtures = CreateGear(
  417. GetSafeFloat(properties, PhysicsShape.PropertyType.Radius) * scaleFactor,
  418. ArrayHelper.SafeGet<PhysicsShape.PropertyType, int>(
  419. properties, PhysicsShape.PropertyType.NumberOfTeeth),
  420. GetSafeFloat(properties, PhysicsShape.PropertyType.TipPercentage),
  421. GetSafeFloat(properties, PhysicsShape.PropertyType.ToothHeight) *
  422. scaleFactor,
  423. GetSafeFloat(properties, PhysicsShape.PropertyType.Density),
  424. farseerBody);
  425. break;
  426. }
  427. // Initialize Tag for later retrieve
  428. foreach (Fixture fixture in farseerFixtures)
  429. {
  430. fixture.UserData = this;
  431. }
  432. }
  433. #endregion
  434. #region GetSafeFloat
  435. /// <summary>
  436. /// Private helper method to get a float value from the properties.
  437. /// </summary>
  438. /// <param name="properties">Shape properties.</param>
  439. /// <param name="type">Property type to get.</param>
  440. /// <returns>Float value from the properties.</returns>
  441. private static float GetSafeFloat(
  442. Dictionary<PhysicsShape.PropertyType, object> properties,
  443. PhysicsShape.PropertyType type)
  444. {
  445. return ArrayHelper.SafeGet<PhysicsShape.PropertyType, float>(properties, type);
  446. }
  447. #endregion
  448. #region CreateGear
  449. /// <summary>
  450. /// Helper method for creating gear fixture list.
  451. /// </summary>
  452. /// <param name="radius">The radius.</param>
  453. /// <param name="numberOfTeeth">The number of teeth.</param>
  454. /// <param name="tipPercentage">The tip percentage.</param>
  455. /// <param name="toothHeight">Height of the tooth.</param>
  456. /// <param name="density">The density.</param>
  457. /// <param name="body">The body.</param>
  458. /// <returns>List containing all fixtures used for Gear shape.</returns>
  459. private static List<Fixture> CreateGear(float radius, int numberOfTeeth,
  460. float tipPercentage, float toothHeight, float density, Body body)
  461. {
  462. Vertices gearPolygon = PolygonTools.CreateGear(radius, numberOfTeeth,
  463. tipPercentage, toothHeight);
  464. //Gears can in some cases be convex
  465. if (gearPolygon.IsConvex() == false)
  466. {
  467. //Decompose the gear:
  468. List<Vertices> list = EarclipDecomposer.ConvexPartition(gearPolygon);
  469. return FixtureFactory.AttachCompoundPolygon(list, density, body,
  470. null);
  471. }
  472. List<Fixture> fixtures = new List<Fixture>();
  473. fixtures.Add(FixtureFactory.AttachPolygon(gearPolygon, density, body,
  474. null));
  475. return fixtures;
  476. }
  477. #endregion
  478. #region OnColliding
  479. /// <summary>
  480. /// On colliding farseeer handle event.
  481. /// </summary>
  482. /// <param name="fixtureA">Farseer fixture A.</param>
  483. /// <param name="fixtureB">Farseer fixture B.</param>
  484. /// <param name="contacts">Farseer contact data.</param>
  485. /// <returns>True to response on farseer collision event.</returns>
  486. private bool OnColliding(Fixture fixtureA, Fixture fixtureB, Contact contacts)
  487. {
  488. PhysicsBody other = (PhysicsBody)fixtureB.UserData;
  489. // Fire event
  490. OnCollisionBegin(other);
  491. return true;
  492. }
  493. #endregion
  494. #region OnSeparation
  495. private void OnSeparation(Fixture fixtureA, Fixture fixtureB)
  496. {
  497. PhysicsBody other = (PhysicsBody)fixtureB.UserData;
  498. // Fire event
  499. OnCollisionEnd(other);
  500. }
  501. #endregion
  502. #region Initialize
  503. /// <summary>
  504. /// Initialize
  505. /// </summary>
  506. protected void Initialize()
  507. {
  508. farseerBody.OnCollision += OnColliding;
  509. farseerBody.OnSeparation += OnSeparation;
  510. }
  511. #endregion
  512. #region SetIsStatic
  513. protected override void SetIsStatic(bool value)
  514. {
  515. farseerBody.IsStatic = value;
  516. farseerBody.BodyType = value
  517. ? BodyType.Static
  518. : BodyType.Dynamic;
  519. }
  520. #endregion
  521. #region SetIsActive
  522. protected override void SetIsActive(bool value)
  523. {
  524. farseerBody.Enabled = value;
  525. }
  526. #endregion
  527. #region SetFriction
  528. protected override void SetFriction(float value)
  529. {
  530. foreach (Fixture fixture in farseerBody.FixtureList)
  531. {
  532. fixture.Friction = value;
  533. }
  534. }
  535. #endregion
  536. #region GetDebugInfo
  537. /// <summary>
  538. /// Return optional debug information text with details about this body!
  539. /// </summary>
  540. /// <returns>Extra information about inertia, flags, etc.</returns>
  541. public override string GetDebugInfo()
  542. {
  543. return "FarseerBody: " +
  544. "IsBullet=" + farseerBody.IsBullet +
  545. ", Inertia=" + farseerBody.Inertia +
  546. ", Mass=" + farseerBody.Mass +
  547. ", LinearVelocity=" + farseerBody.LinearVelocity +
  548. ", LinearDamping=" + farseerBody.LinearDamping +
  549. ", IgnoreGravity=" + farseerBody.IgnoreGravity +
  550. ", Revolutions=" + farseerBody.Revolutions +
  551. ", Time.Delta=" + Delta.Engine.Time.Delta;
  552. }
  553. #endregion
  554. #endregion
  555. }
  556. }