PageRenderTime 465ms CodeModel.GetById 91ms app.highlight 260ms RepoModel.GetById 53ms app.codeStats 1ms

/PhysicsEngines/JigLib/JigLibPhysics.cs

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