PageRenderTime 40ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/PhysicsEngines/JigLib/JigLibPhysics.cs

#
C# | 554 lines | 350 code | 66 blank | 138 comment | 25 complexity | 49c303a8f201af4d2e22a5f58a85d2f3 MD5 | raw file
Possible License(s): Apache-2.0
  1. using System.Collections.Generic;
  2. using Delta.PhysicsEngines.Enums;
  3. using Delta.Utilities;
  4. using Delta.Utilities.Datatypes;
  5. using JigLibX.Collision;
  6. using JigLibX.Geometry;
  7. using JigLibX.Physics;
  8. using NUnit.Framework;
  9. using Ray = Delta.Utilities.Datatypes.Ray;
  10. using XnaVector3 = Microsoft.Xna.Framework.Vector3;
  11. namespace Delta.PhysicsEngines.JigLib
  12. {
  13. /// <summary>
  14. /// JigLib carries out all the requested functionality from the interface
  15. /// IPhysic3D. At this point we can see in detail the whole engine without
  16. /// restriction but is not visible for the user since the design is based on
  17. /// using a common and friendly interface accessible to the user who doesn't
  18. /// have to know any implementation detail of the physic engines
  19. /// </summary>
  20. public class JigLibPhysics : Physics
  21. {
  22. #region GatherObjectsPredicate Class
  23. /// <summary>
  24. /// Gather object predicate
  25. /// </summary>
  26. private class GatherObjectsPredicate : CollisionSkinPredicate1
  27. {
  28. #region Internal
  29. #region SkinsToIgnore (Internal)
  30. /// <summary>
  31. /// Skins to ignore
  32. /// </summary>
  33. internal readonly List<CollisionSkin> SkinsToIgnore =
  34. new List<CollisionSkin>();
  35. #endregion
  36. #endregion
  37. #region ConsiderSkin (Public)
  38. /// <summary>
  39. /// This method is used internally by Delta.PhysicsEngines.JigLib to determine if the ray
  40. /// should "stop" at this object or not. Here we use it to add the object
  41. /// in our list and continue looking for more objects.
  42. /// </summary>
  43. /// <param name="skin0">Skin to check against.</param>
  44. /// <returns>True if skin is on list to be ignored.</returns>
  45. public override bool ConsiderSkin(CollisionSkin skin0)
  46. {
  47. foreach (CollisionSkin skin in SkinsToIgnore)
  48. {
  49. if (skin0 == skin)
  50. {
  51. return false;
  52. }
  53. }
  54. return true;
  55. }
  56. #endregion
  57. #region Methods (Private)
  58. #region Clear
  59. /// <summary>
  60. /// Clear objet list
  61. /// </summary>
  62. internal void Clear()
  63. {
  64. SkinsToIgnore.Clear();
  65. }
  66. #endregion
  67. #endregion
  68. }
  69. #endregion
  70. #region Constants
  71. /// <summary>
  72. /// Ray predicate
  73. /// </summary>
  74. private static readonly GatherObjectsPredicate rayPredicate =
  75. new GatherObjectsPredicate();
  76. // MaximumDeltaTime
  77. private const float PlaneHeight = 1.0f;
  78. #endregion
  79. #region MaximumDeltaTime (Public)
  80. /// <summary>
  81. /// The maximum allowed delta time value for updating the physics module.
  82. /// This variable is needed since large delta values due to other reasons
  83. /// (content loading, etc) can cause considerable instabilities to the
  84. /// physics system
  85. /// </summary>
  86. /// <value>
  87. /// The maximum delta time.
  88. /// </value>
  89. public float MaximumDeltaTime
  90. {
  91. get;
  92. set;
  93. }
  94. #endregion
  95. #region Internal
  96. #region jigLibPhysicsSimulator (Internal)
  97. /// <summary>
  98. /// Delta.PhysicsEngines.JigLib Physics Simulator
  99. /// </summary>
  100. internal PhysicsSystem jigLibPhysicsSimulator;
  101. #endregion
  102. #region skinBodiesMap (Internal)
  103. internal Dictionary<CollisionSkin, PhysicsBody> skinBodiesMap =
  104. new Dictionary<CollisionSkin, PhysicsBody>();
  105. #endregion
  106. #endregion
  107. #region Constructors
  108. /// <summary>
  109. /// Create a JiglibPhysics object
  110. /// </summary>
  111. public JigLibPhysics()
  112. // Use time as the parent module to make sure the Time.Delta can be
  113. // used for the current frame (Time is always the first module to
  114. // be created and has IApplication as its parent).
  115. : base(false, "JigLib")
  116. {
  117. Initialize();
  118. // set maximum acceptable value
  119. MaximumDeltaTime = 0.05f;
  120. }
  121. #endregion
  122. #region IsShapeSupported (Public)
  123. /// <summary>
  124. /// Gets whether the current physics module supports given shape type.
  125. /// </summary>
  126. /// <param name="shapeType">Type of the shape.</param>
  127. /// <returns>
  128. /// <c>true</c> if [is shape supported] [the specified shape type]; otherwise, <c>false</c>.
  129. /// </returns>
  130. public override bool IsShapeSupported(ShapeType shapeType)
  131. {
  132. switch (shapeType)
  133. {
  134. case ShapeType.Box:
  135. case ShapeType.Sphere:
  136. case ShapeType.Capsule:
  137. case ShapeType.Triangle:
  138. return true;
  139. }
  140. return false;
  141. }
  142. #endregion
  143. #region IsJointSupported (Public)
  144. /// <summary>
  145. /// Gets whether the current physics module supports given joint type.
  146. /// </summary>
  147. /// <param name="jointType">Type of the shape.</param>
  148. /// <returns>
  149. /// <c>true</c> if the specified shape type is supported, <c>false</c> otherwise.
  150. /// </returns>
  151. public override bool IsJointSupported(JointType shapeType)
  152. {
  153. switch (shapeType)
  154. {
  155. case JointType.PointOnPoint:
  156. return true;
  157. }
  158. return false;
  159. }
  160. #endregion
  161. #region IsFeatureSupported (Public)
  162. /// <summary>
  163. /// Gets whether the current physics module supports given feature.
  164. /// </summary>
  165. /// <param name="support">The support.</param>
  166. /// <returns>
  167. /// <c>true</c> if [is feature supported] [the specified support]; otherwise, <c>false</c>.
  168. /// </returns>
  169. public override bool IsFeatureSupported(FeatureSupport support)
  170. {
  171. switch (support)
  172. {
  173. case FeatureSupport.Joint:
  174. case FeatureSupport.Controller:
  175. return true;
  176. }
  177. return false;
  178. }
  179. #endregion
  180. #region SetGroundPlane (Public)
  181. /// <summary>
  182. /// Implementation of SetGroundPlane
  183. /// </summary>
  184. /// <param name="enable">True to enable plane, false otherwise.</param>
  185. /// <param name="height">Plane height.</param>
  186. public override void SetGroundPlane(bool enable, float height)
  187. {
  188. Vector position = new Vector(0, 0, height);
  189. if (groundBody == null)
  190. {
  191. groundBody = new JigLibBody(this);
  192. groundBody.IsStatic = true;
  193. }
  194. groundBody.Position = position;
  195. Body jigLibBody = ((JigLibBody)groundBody).Body;
  196. // Check if we need to add it
  197. if (enable)
  198. {
  199. jigLibBody.EnableBody();
  200. }
  201. else
  202. {
  203. jigLibBody.DisableBody();
  204. }
  205. }
  206. #endregion
  207. #region Methods (Private)
  208. #region CreateGroundBody
  209. /// <summary>
  210. /// Create the body that will be used as ground.
  211. /// </summary>
  212. private void CreateGroundBody()
  213. {
  214. groundBody = new JigLibBody(this);
  215. }
  216. #endregion
  217. #region Initialize
  218. /// <summary>
  219. /// Initialize the physic simulator and apply default gravity (zero)
  220. /// </summary>
  221. private void Initialize()
  222. {
  223. // create the physics system
  224. jigLibPhysicsSimulator = new PhysicsSystem();
  225. // set up the physic simulator to be working with SAP broad phase
  226. jigLibPhysicsSimulator.CollisionSystem = new CollisionSystemSAP();
  227. // use sweep collision tests
  228. // NOTE: Delta.PhysicsEngines.JigLib does not support "real" sweep tests however it is
  229. // still an improvement over static tests
  230. jigLibPhysicsSimulator.CollisionSystem.UseSweepTests = true;
  231. // disable object freezing (i.e. disabling of objects that have not
  232. // changed position for some time)
  233. // in general freezing is quite useful for performance, however it causes
  234. // some problems at startup and it is more advisable to disable it for now
  235. jigLibPhysicsSimulator.EnableFreezing = false;
  236. // setup the numerical solver and the respective parameters
  237. jigLibPhysicsSimulator.SolverType = PhysicsSystem.Solver.Normal;
  238. jigLibPhysicsSimulator.NumCollisionIterations = 8;
  239. jigLibPhysicsSimulator.NumContactIterations = 8;
  240. jigLibPhysicsSimulator.NumPenetrationRelaxtionTimesteps = 12;
  241. // Set initial gravity
  242. jigLibPhysicsSimulator.Gravity = new XnaVector3(DefaultGravity.X,
  243. DefaultGravity.Y, DefaultGravity.Z);
  244. }
  245. #endregion
  246. #region CreateBody
  247. protected override PhysicsBody CreateBody(bool is2DBody,
  248. PhysicsShape shape, Vector initialPosition)
  249. {
  250. if (is2DBody)
  251. {
  252. Log.Warning("JibLibX does not support 2D bodies.");
  253. return null;
  254. }
  255. PhysicsBody body = new JigLibBody(this, shape, initialPosition);
  256. bodies.Add(body);
  257. return body;
  258. }
  259. #endregion
  260. #region CreateJoint
  261. public override PhysicsJoint CreateJoint(
  262. JointType jointType,
  263. PhysicsBody bodyA,
  264. PhysicsBody bodyB,
  265. object[] args)
  266. {
  267. if (IsJointSupported(jointType) == false)
  268. {
  269. Log.Warning("Current module does not support " +
  270. "the type of joint " + jointType);
  271. return null;
  272. }
  273. PhysicsJoint joint = new JitterJoint(this, jointType, bodyA, bodyB, args);
  274. if (joint != null)
  275. {
  276. joints.Add(joint);
  277. }
  278. return joint;
  279. }
  280. #endregion
  281. #region CreateControllerImpl
  282. protected override IController CreateControllerImpl(
  283. PhysicsBody controlledObject,
  284. float setMoveSpeed, float setJumpStrength, float allowedGroundDistance)
  285. {
  286. return new JigLibController(
  287. this,
  288. controlledObject as JigLibBody,
  289. setMoveSpeed, setJumpStrength, allowedGroundDistance);
  290. }
  291. #endregion
  292. #region RemoveBodyImpl
  293. protected override void RemoveBodyImpl(PhysicsBody body)
  294. {
  295. jigLibPhysicsSimulator.RemoveBody(
  296. (body as JigLibBody).Body
  297. );
  298. }
  299. #endregion
  300. #region RemoveJointImpl
  301. protected override void RemoveJointImpl(PhysicsJoint joint)
  302. {
  303. }
  304. #endregion
  305. #region GetTotalPhysicsTime
  306. protected override double GetTotalPhysicsTime()
  307. {
  308. return 0.0;
  309. }
  310. #endregion
  311. #region SetMultithreading
  312. protected override void SetMultithreading(bool enable)
  313. {
  314. // Not supported
  315. if (enable)
  316. {
  317. Log.Warning("JigLibX does not support multi-threading yet!");
  318. }
  319. }
  320. #endregion
  321. #region SetGravity
  322. /// <summary>
  323. /// Set Gravity
  324. /// </summary>
  325. protected override void SetGravity(Vector gravity)
  326. {
  327. jigLibPhysicsSimulator.Gravity =
  328. JigLibDatatypesMapping.Convert(ref gravity);
  329. }
  330. #endregion
  331. #region UpdateSimulation
  332. /// <summary>
  333. /// Update a physic3D system. The engine carries out the simulation of our
  334. /// defined system each tick.
  335. /// </summary>
  336. protected override void UpdateSimulation(float timeStep)
  337. {
  338. if (IsPaused == false)
  339. {
  340. jigLibPhysicsSimulator.Integrate(timeStep);
  341. }
  342. }
  343. #endregion
  344. #region RayCastImpl
  345. /// <summary>
  346. /// FindRay cast implementation
  347. /// </summary>
  348. /// <param name="ray">The ray to perform.</param>
  349. /// <param name="checkGround">Whether to check agains ground too.</param>
  350. /// <param name="foundBody">PhysicsBody or null if no intersection happen.</param>
  351. /// <param name="surfaceNormal">
  352. /// The normals of the surfaces in the intersection points.
  353. /// </param>
  354. /// <param name="fraction">
  355. /// Intersection fraction value or zero if no intersection.
  356. /// </param>
  357. /// <param name="userData">Optional user data.</param>
  358. /// <returns>True if any intersection happen, false otherwise.</returns>
  359. protected override bool RayCastImpl(Ray ray, bool checkGround,
  360. out PhysicsBody foundBody, out Vector surfaceNormal, out float fraction,
  361. out object userData)
  362. {
  363. foundBody = null;
  364. fraction = 0.0f;
  365. surfaceNormal = Vector.Zero;
  366. userData = null;
  367. // create a line segment in JigLib format
  368. Segment seg = new Segment(
  369. JigLibDatatypesMapping.Convert(ref ray.Position),
  370. JigLibDatatypesMapping.Convert(ref ray.Direction) * 100000);
  371. // list with intersection points
  372. List<float> fractionList = new List<float>();
  373. List<Vector> intersectList = new List<Vector>();
  374. List<Vector> normalsList = new List<Vector>();
  375. // jiglib ray casting information
  376. CollisionSkin skin;
  377. XnaVector3 pos, normal;
  378. // reset variables for ray cast checks
  379. rayPredicate.Clear();
  380. bool continueSearching = true;
  381. // start casting as rays in necessary the ray
  382. while (continueSearching)
  383. {
  384. jigLibPhysicsSimulator.CollisionSystem.SegmentIntersect(
  385. out fraction, out skin, out pos, out normal, seg, rayPredicate);
  386. fractionList.Add(fraction);
  387. Vector tempVector;
  388. JigLibDatatypesMapping.Convert(ref normal, out tempVector);
  389. normalsList.Add(tempVector);
  390. JigLibDatatypesMapping.Convert(ref pos, out tempVector);
  391. intersectList.Add(tempVector);
  392. if (skin != null)
  393. {
  394. rayPredicate.SkinsToIgnore.Add(skin);
  395. }
  396. else
  397. {
  398. continueSearching = false;
  399. }
  400. }
  401. if (fractionList.Count == 0)
  402. {
  403. return false;
  404. }
  405. fraction = fractionList[0];
  406. surfaceNormal = normalsList[0];
  407. userData = intersectList.ToArray();
  408. // if at least 1 object intersects with the ray...
  409. if (rayPredicate.SkinsToIgnore.Count > 0)
  410. {
  411. // find the IPhysics3DObjects that corresponds to the collided skins
  412. for (int i = 0; i < rayPredicate.SkinsToIgnore.Count; i++)
  413. {
  414. skin = rayPredicate.SkinsToIgnore[i];
  415. // find the physics object with this skin
  416. foreach (JigLibBody jigBody in bodies)
  417. {
  418. if (jigBody.Skin == skin)
  419. {
  420. foundBody = jigBody;
  421. break;
  422. }
  423. }
  424. }
  425. return true;
  426. }
  427. return false;
  428. }
  429. #endregion
  430. #endregion
  431. /// <summary>
  432. /// Tests
  433. /// </summary>
  434. internal class JigLibPhysicsTests
  435. {
  436. #region TestInitialization (LongRunning)
  437. /// <summary>
  438. /// Test whether initialization has been done.
  439. /// </summary>
  440. [Test, Category("LongRunning")]
  441. public static void TestInitialization()
  442. {
  443. // check that everything has been OK with Physics initiallization
  444. Assert.NotNull(Bodies);
  445. }
  446. #endregion
  447. #region TestCreate3DBodyWithShape (LongRunning)
  448. /// <summary>
  449. /// Test creation of 3 body with shape.
  450. /// </summary>
  451. [Test, Category("LongRunning")]
  452. public static void TestCreate3DBodyWithShape()
  453. {
  454. PhysicsBody body = CreateBox(Vector.Zero,
  455. 2.0f, 3.0f, 1.0f);
  456. Assert.NotNull(body);
  457. Assert.NotNull(body.Shape);
  458. }
  459. #endregion
  460. #region TestFeatureSupport (LongRunning)
  461. /// <summary>
  462. /// Test whether given feature is supoorted.
  463. /// </summary>
  464. [Test, Category("LongRunning")]
  465. public static void TestFeatureSupport()
  466. {
  467. // JigLib support controllers.
  468. bool isSupported = Instance.IsFeatureSupported(
  469. FeatureSupport.Controller);
  470. Assert.True(isSupported);
  471. }
  472. #endregion
  473. #region TestShapeSupport (LongRunning)
  474. /// <summary>
  475. /// Test whether given shape is supoorted.
  476. /// </summary>
  477. [Test, Category("LongRunning")]
  478. public static void TestShapeSupport()
  479. {
  480. // JigLib does not support cone shape.
  481. bool isSupported = Instance.IsShapeSupported(
  482. ShapeType.Cone);
  483. Assert.False(isSupported);
  484. }
  485. #endregion
  486. }
  487. }
  488. }