/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}