/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
- using System.Collections.Generic;
- using Delta.PhysicsEngines.Enums;
- using Delta.Utilities;
- using Delta.Utilities.Datatypes;
- using JigLibX.Collision;
- using JigLibX.Geometry;
- using JigLibX.Physics;
- using NUnit.Framework;
- using Ray = Delta.Utilities.Datatypes.Ray;
- using XnaVector3 = Microsoft.Xna.Framework.Vector3;
-
- namespace Delta.PhysicsEngines.JigLib
- {
- /// <summary>
- /// JigLib carries out all the requested functionality from the interface
- /// IPhysic3D. At this point we can see in detail the whole engine without
- /// restriction but is not visible for the user since the design is based on
- /// using a common and friendly interface accessible to the user who doesn't
- /// have to know any implementation detail of the physic engines
- /// </summary>
- public class JigLibPhysics : Physics
- {
- #region GatherObjectsPredicate Class
- /// <summary>
- /// Gather object predicate
- /// </summary>
- private class GatherObjectsPredicate : CollisionSkinPredicate1
- {
- #region Internal
-
- #region SkinsToIgnore (Internal)
- /// <summary>
- /// Skins to ignore
- /// </summary>
- internal readonly List<CollisionSkin> SkinsToIgnore =
- new List<CollisionSkin>();
- #endregion
-
- #endregion
-
- #region ConsiderSkin (Public)
- /// <summary>
- /// This method is used internally by Delta.PhysicsEngines.JigLib to determine if the ray
- /// should "stop" at this object or not. Here we use it to add the object
- /// in our list and continue looking for more objects.
- /// </summary>
- /// <param name="skin0">Skin to check against.</param>
- /// <returns>True if skin is on list to be ignored.</returns>
- public override bool ConsiderSkin(CollisionSkin skin0)
- {
- foreach (CollisionSkin skin in SkinsToIgnore)
- {
- if (skin0 == skin)
- {
- return false;
- }
- }
-
- return true;
- }
- #endregion
-
- #region Methods (Private)
-
- #region Clear
- /// <summary>
- /// Clear objet list
- /// </summary>
- internal void Clear()
- {
- SkinsToIgnore.Clear();
- }
- #endregion
-
- #endregion
- }
- #endregion
-
- #region Constants
- /// <summary>
- /// Ray predicate
- /// </summary>
- private static readonly GatherObjectsPredicate rayPredicate =
- new GatherObjectsPredicate();
-
- // MaximumDeltaTime
-
- private const float PlaneHeight = 1.0f;
- #endregion
-
- #region MaximumDeltaTime (Public)
- /// <summary>
- /// The maximum allowed delta time value for updating the physics module.
- /// This variable is needed since large delta values due to other reasons
- /// (content loading, etc) can cause considerable instabilities to the
- /// physics system
- /// </summary>
- /// <value>
- /// The maximum delta time.
- /// </value>
- public float MaximumDeltaTime
- {
- get;
- set;
- }
- #endregion
-
- #region Internal
-
- #region jigLibPhysicsSimulator (Internal)
- /// <summary>
- /// Delta.PhysicsEngines.JigLib Physics Simulator
- /// </summary>
- internal PhysicsSystem jigLibPhysicsSimulator;
- #endregion
-
- #region skinBodiesMap (Internal)
- internal Dictionary<CollisionSkin, PhysicsBody> skinBodiesMap =
- new Dictionary<CollisionSkin, PhysicsBody>();
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create a JiglibPhysics object
- /// </summary>
- public JigLibPhysics()
- // Use time as the parent module to make sure the Time.Delta can be
- // used for the current frame (Time is always the first module to
- // be created and has IApplication as its parent).
- : base(false, "JigLib")
- {
- Initialize();
-
- // set maximum acceptable value
- MaximumDeltaTime = 0.05f;
- }
- #endregion
-
- #region IsShapeSupported (Public)
- /// <summary>
- /// Gets whether the current physics module supports given shape type.
- /// </summary>
- /// <param name="shapeType">Type of the shape.</param>
- /// <returns>
- /// <c>true</c> if [is shape supported] [the specified shape type]; otherwise, <c>false</c>.
- /// </returns>
- public override bool IsShapeSupported(ShapeType shapeType)
- {
- switch (shapeType)
- {
- case ShapeType.Box:
- case ShapeType.Sphere:
- case ShapeType.Capsule:
- case ShapeType.Triangle:
- return true;
- }
-
- return false;
- }
- #endregion
-
- #region IsJointSupported (Public)
- /// <summary>
- /// Gets whether the current physics module supports given joint type.
- /// </summary>
- /// <param name="jointType">Type of the shape.</param>
- /// <returns>
- /// <c>true</c> if the specified shape type is supported, <c>false</c> otherwise.
- /// </returns>
- public override bool IsJointSupported(JointType shapeType)
- {
- switch (shapeType)
- {
- case JointType.PointOnPoint:
- return true;
- }
-
- return false;
- }
- #endregion
-
- #region IsFeatureSupported (Public)
- /// <summary>
- /// Gets whether the current physics module supports given feature.
- /// </summary>
- /// <param name="support">The support.</param>
- /// <returns>
- /// <c>true</c> if [is feature supported] [the specified support]; otherwise, <c>false</c>.
- /// </returns>
- public override bool IsFeatureSupported(FeatureSupport support)
- {
- switch (support)
- {
- case FeatureSupport.Joint:
- case FeatureSupport.Controller:
- return true;
- }
-
- return false;
- }
- #endregion
-
- #region SetGroundPlane (Public)
- /// <summary>
- /// Implementation of SetGroundPlane
- /// </summary>
- /// <param name="enable">True to enable plane, false otherwise.</param>
- /// <param name="height">Plane height.</param>
- public override void SetGroundPlane(bool enable, float height)
- {
- Vector position = new Vector(0, 0, height);
- if (groundBody == null)
- {
- groundBody = new JigLibBody(this);
- groundBody.IsStatic = true;
- }
-
- groundBody.Position = position;
- Body jigLibBody = ((JigLibBody)groundBody).Body;
- // Check if we need to add it
- if (enable)
- {
- jigLibBody.EnableBody();
- }
- else
- {
- jigLibBody.DisableBody();
- }
- }
- #endregion
-
- #region Methods (Private)
-
- #region CreateGroundBody
- /// <summary>
- /// Create the body that will be used as ground.
- /// </summary>
- private void CreateGroundBody()
- {
- groundBody = new JigLibBody(this);
- }
- #endregion
-
- #region Initialize
- /// <summary>
- /// Initialize the physic simulator and apply default gravity (zero)
- /// </summary>
- private void Initialize()
- {
- // create the physics system
- jigLibPhysicsSimulator = new PhysicsSystem();
-
- // set up the physic simulator to be working with SAP broad phase
- jigLibPhysicsSimulator.CollisionSystem = new CollisionSystemSAP();
-
- // use sweep collision tests
- // NOTE: Delta.PhysicsEngines.JigLib does not support "real" sweep tests however it is
- // still an improvement over static tests
- jigLibPhysicsSimulator.CollisionSystem.UseSweepTests = true;
-
- // disable object freezing (i.e. disabling of objects that have not
- // changed position for some time)
- // in general freezing is quite useful for performance, however it causes
- // some problems at startup and it is more advisable to disable it for now
- jigLibPhysicsSimulator.EnableFreezing = false;
-
- // setup the numerical solver and the respective parameters
- jigLibPhysicsSimulator.SolverType = PhysicsSystem.Solver.Normal;
- jigLibPhysicsSimulator.NumCollisionIterations = 8;
- jigLibPhysicsSimulator.NumContactIterations = 8;
- jigLibPhysicsSimulator.NumPenetrationRelaxtionTimesteps = 12;
-
- // Set initial gravity
- jigLibPhysicsSimulator.Gravity = new XnaVector3(DefaultGravity.X,
- DefaultGravity.Y, DefaultGravity.Z);
- }
- #endregion
-
- #region CreateBody
- protected override PhysicsBody CreateBody(bool is2DBody,
- PhysicsShape shape, Vector initialPosition)
- {
- if (is2DBody)
- {
- Log.Warning("JibLibX does not support 2D bodies.");
- return null;
- }
-
- PhysicsBody body = new JigLibBody(this, shape, initialPosition);
- bodies.Add(body);
- return body;
- }
- #endregion
-
- #region CreateJoint
- public override PhysicsJoint CreateJoint(
- JointType jointType,
- PhysicsBody bodyA,
- PhysicsBody bodyB,
- object[] args)
- {
- if (IsJointSupported(jointType) == false)
- {
- Log.Warning("Current module does not support " +
- "the type of joint " + jointType);
- return null;
- }
-
- PhysicsJoint joint = new JitterJoint(this, jointType, bodyA, bodyB, args);
- if (joint != null)
- {
- joints.Add(joint);
- }
- return joint;
- }
- #endregion
-
- #region CreateControllerImpl
- protected override IController CreateControllerImpl(
- PhysicsBody controlledObject,
- float setMoveSpeed, float setJumpStrength, float allowedGroundDistance)
- {
- return new JigLibController(
- this,
- controlledObject as JigLibBody,
- setMoveSpeed, setJumpStrength, allowedGroundDistance);
- }
- #endregion
-
- #region RemoveBodyImpl
- protected override void RemoveBodyImpl(PhysicsBody body)
- {
- jigLibPhysicsSimulator.RemoveBody(
- (body as JigLibBody).Body
- );
- }
- #endregion
-
- #region RemoveJointImpl
- protected override void RemoveJointImpl(PhysicsJoint joint)
- {
- }
- #endregion
-
- #region GetTotalPhysicsTime
- protected override double GetTotalPhysicsTime()
- {
- return 0.0;
- }
- #endregion
-
- #region SetMultithreading
- protected override void SetMultithreading(bool enable)
- {
- // Not supported
- if (enable)
- {
- Log.Warning("JigLibX does not support multi-threading yet!");
- }
- }
- #endregion
-
- #region SetGravity
- /// <summary>
- /// Set Gravity
- /// </summary>
- protected override void SetGravity(Vector gravity)
- {
- jigLibPhysicsSimulator.Gravity =
- JigLibDatatypesMapping.Convert(ref gravity);
- }
- #endregion
-
- #region UpdateSimulation
- /// <summary>
- /// Update a physic3D system. The engine carries out the simulation of our
- /// defined system each tick.
- /// </summary>
- protected override void UpdateSimulation(float timeStep)
- {
- if (IsPaused == false)
- {
- jigLibPhysicsSimulator.Integrate(timeStep);
- }
- }
- #endregion
-
- #region RayCastImpl
- /// <summary>
- /// FindRay cast implementation
- /// </summary>
- /// <param name="ray">The ray to perform.</param>
- /// <param name="checkGround">Whether to check agains ground too.</param>
- /// <param name="foundBody">PhysicsBody or null if no intersection happen.</param>
- /// <param name="surfaceNormal">
- /// The normals of the surfaces in the intersection points.
- /// </param>
- /// <param name="fraction">
- /// Intersection fraction value or zero if no intersection.
- /// </param>
- /// <param name="userData">Optional user data.</param>
- /// <returns>True if any intersection happen, false otherwise.</returns>
- protected override bool RayCastImpl(Ray ray, bool checkGround,
- out PhysicsBody foundBody, out Vector surfaceNormal, out float fraction,
- out object userData)
- {
- foundBody = null;
- fraction = 0.0f;
- surfaceNormal = Vector.Zero;
- userData = null;
-
- // create a line segment in JigLib format
- Segment seg = new Segment(
- JigLibDatatypesMapping.Convert(ref ray.Position),
- JigLibDatatypesMapping.Convert(ref ray.Direction) * 100000);
-
- // list with intersection points
- List<float> fractionList = new List<float>();
- List<Vector> intersectList = new List<Vector>();
- List<Vector> normalsList = new List<Vector>();
-
- // jiglib ray casting information
- CollisionSkin skin;
- XnaVector3 pos, normal;
-
- // reset variables for ray cast checks
- rayPredicate.Clear();
- bool continueSearching = true;
-
- // start casting as rays in necessary the ray
- while (continueSearching)
- {
- jigLibPhysicsSimulator.CollisionSystem.SegmentIntersect(
- out fraction, out skin, out pos, out normal, seg, rayPredicate);
-
- fractionList.Add(fraction);
- Vector tempVector;
- JigLibDatatypesMapping.Convert(ref normal, out tempVector);
- normalsList.Add(tempVector);
- JigLibDatatypesMapping.Convert(ref pos, out tempVector);
- intersectList.Add(tempVector);
-
- if (skin != null)
- {
- rayPredicate.SkinsToIgnore.Add(skin);
- }
- else
- {
- continueSearching = false;
- }
- }
-
- if (fractionList.Count == 0)
- {
- return false;
- }
-
- fraction = fractionList[0];
- surfaceNormal = normalsList[0];
- userData = intersectList.ToArray();
-
- // if at least 1 object intersects with the ray...
- if (rayPredicate.SkinsToIgnore.Count > 0)
- {
- // find the IPhysics3DObjects that corresponds to the collided skins
- for (int i = 0; i < rayPredicate.SkinsToIgnore.Count; i++)
- {
- skin = rayPredicate.SkinsToIgnore[i];
-
- // find the physics object with this skin
- foreach (JigLibBody jigBody in bodies)
- {
- if (jigBody.Skin == skin)
- {
- foundBody = jigBody;
- break;
- }
- }
- }
-
- return true;
- }
-
- return false;
- }
- #endregion
-
- #endregion
-
- /// <summary>
- /// Tests
- /// </summary>
- internal class JigLibPhysicsTests
- {
- #region TestInitialization (LongRunning)
- /// <summary>
- /// Test whether initialization has been done.
- /// </summary>
- [Test, Category("LongRunning")]
- public static void TestInitialization()
- {
- // check that everything has been OK with Physics initiallization
- Assert.NotNull(Bodies);
- }
- #endregion
-
- #region TestCreate3DBodyWithShape (LongRunning)
- /// <summary>
- /// Test creation of 3 body with shape.
- /// </summary>
- [Test, Category("LongRunning")]
- public static void TestCreate3DBodyWithShape()
- {
- PhysicsBody body = CreateBox(Vector.Zero,
- 2.0f, 3.0f, 1.0f);
- Assert.NotNull(body);
- Assert.NotNull(body.Shape);
- }
- #endregion
-
- #region TestFeatureSupport (LongRunning)
- /// <summary>
- /// Test whether given feature is supoorted.
- /// </summary>
- [Test, Category("LongRunning")]
- public static void TestFeatureSupport()
- {
- // JigLib support controllers.
- bool isSupported = Instance.IsFeatureSupported(
- FeatureSupport.Controller);
-
- Assert.True(isSupported);
- }
- #endregion
-
- #region TestShapeSupport (LongRunning)
- /// <summary>
- /// Test whether given shape is supoorted.
- /// </summary>
- [Test, Category("LongRunning")]
- public static void TestShapeSupport()
- {
- // JigLib does not support cone shape.
- bool isSupported = Instance.IsShapeSupported(
- ShapeType.Cone);
-
- Assert.False(isSupported);
- }
- #endregion
- }
- }
- }