PageRenderTime 147ms CodeModel.GetById 60ms app.highlight 43ms RepoModel.GetById 37ms app.codeStats 1ms

/PhysicsEngines/Farseer/FarseerBody.cs

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