private bool _drawDebugInfo; // true if the collision shapes should be drawn for debugging. public ContentPipelineSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; SetCamera(new Vector3F(0, 1, 10), 0, 0); // Initialize collision detection. // Note: The physics Simulation also has a collision domain (Simulation.CollisionDomain) // which we could use and which is updated together with the Simulation in // SampleGame.cs. But in this example we create our own CollisionDomain for demonstration // purposes. _collisionDomain = new CollisionDomain(new CollisionDetection()); // Register CollisionDomain in service container. Services.Register(typeof(CollisionDomain), null, _collisionDomain); // Add game objects which manage graphics models and their collision representations. _saucerObject = new SaucerObject(Services) { Name = "Saucer" }; _shipObjectA = new ShipObject(Services) { Name = "ShipA" }; _shipObjectB = new ShipObject(Services) { Name = "ShipB" }; GameObjectService.Objects.Add(_saucerObject); GameObjectService.Objects.Add(_shipObjectA); GameObjectService.Objects.Add(_shipObjectB); // Position the second ship right of the first ship with an arbitrary rotation. _shipObjectB.Pose = new Pose(new Vector3F(2, 0, 0), QuaternionF.CreateRotationY(0.7f) * QuaternionF.CreateRotationX(1.2f)); // Position the saucer left of the first ship with an arbitrary rotation. _saucerObject.Pose = new Pose(new Vector3F(-2.5f, 0, 0), QuaternionF.CreateRotationY(0.2f) * QuaternionF.CreateRotationX(0.4f)); }
public void UpdateSingle() { CollisionDomain domain = new CollisionDomain(new CollisionDetection()); // Add 100 random objects. for (int i = 0; i < 100; i++) { domain.CollisionObjects.Add(new CollisionObject(new GeometricObject(new SphereShape(1), new Pose(RandomHelper.Random.NextVector3F(0, 20))))); } for (int i = 0; i < 100; i++) { domain.Update(domain.CollisionObjects[33]); for (int j = 0; j < domain.CollisionObjects.Count; j++) { var a = domain.CollisionObjects[j]; for (int k = j + 1; k < domain.CollisionObjects.Count; k++) { var b = domain.CollisionObjects[k]; Assert.AreEqual(domain.InternalBroadPhase.CandidatePairs.Contains(a, b), GeometryHelper.HaveContact(a.GeometricObject.Aabb, b.GeometricObject.Aabb)); } } // Set new random position for one. ((GeometricObject)domain.CollisionObjects[33].GeometricObject).Pose = new Pose(RandomHelper.Random.NextVector3F(0, 20)); Console.WriteLine("Candidate pairs: " + domain.InternalBroadPhase.CandidatePairs.Count); } }
public FigurePickerObject(IGraphicsService graphicsService, Scene scene, Editor2DCameraObject cameraObject, DebugRenderer debugRenderer) { _cameraObject = cameraObject; _scene = scene; _debugRenderer = debugRenderer; // Create a collision domain which manages all collision objects used for // picking: the picking object and the collision objects for figure nodes. _collisionDomain = new CollisionDomain(new CollisionDetection()); // Create the picking object: // The picking object represents the mouse cursor or the reticle. Usually // a ray is used, but in this example we want to use a cylinder/cone. This // allows to check which objects within a certain radius of the reticle. A // picking cylinder/cone is helpful for touch devices where the picking is // done with an imprecise input method like the human finger. // We want to pick objects in 10 pixel radius around the reticle. To determine // the world space size of the required cylinder/cone, we can use the projection // and the viewport. const float pickingRadius = 0.25f; var projection = _cameraObject.CameraNode.Camera.Projection; var viewport = graphicsService.GraphicsDevice.Viewport; Shape pickingShape; if (projection is OrthographicProjection) { // Use cylinder for orthographic projections: // The cylinder is centered at the camera position and reaches from the // camera position to the camera far plane. A TransformedShape is used // to rotate and translate the cylinder. float radius = projection.Width / viewport.Width * pickingRadius; pickingShape = new TransformedShape( new GeometricObject( new CylinderShape(radius, projection.Far), new Pose(new Vector3F(0, 0, -projection.Far / 2), Matrix33F.CreateRotationX(ConstantsF.PiOver2)))); } else { // Use cone for perspective projections: // The cone tip is at the camera position and the cone base is at the // camera far plane. // Compute the radius at the far plane that projects to 10 pixels in screen space. float radius = viewport.Unproject( new Vector3(viewport.Width / 2.0f + pickingRadius, viewport.Height / 2.0f, 1), (Matrix)_cameraObject.CameraNode.Camera.Projection.ToMatrix44F(), Matrix.Identity, Matrix.Identity).X; // A transformed shape is used to rotate and translate the cone. pickingShape = new TransformedShape( new GeometricObject( new ConeShape(radius, projection.Far), new Pose(new Vector3F(0, 0, -projection.Far), Matrix33F.CreateRotationX(ConstantsF.PiOver2)))); } // Create collision object with the picking shape. _pickingObject = new CollisionObject(new GeometricObject(pickingShape, _cameraObject.CameraNode.PoseWorld)); }
//-------------------------------------------------------------- #region Creation & Cleanup //-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="Scene"/> class. /// </summary> public Scene() { Children = new SceneNodeCollection(); if ((GlobalSettings.ValidationLevelInternal & GlobalSettings.ValidationLevelUserHighExpensive) != 0) { SceneChanged += OnSceneChangedValidation; } // Create a default collision filter. var filter = new SceneNodeCollisionFilter(this); filter.Set(1, 1, false); // Ignore camera vs. camera. filter.Set(2, 3, false); // Ignore light vs. lens flare. filter.Set(3, 3, false); // Ignore lens flare vs. lens flare. filter.Set(3, 4, false); // Ignore lens flare vs. mesh. filter.Set(4, 4, false); // Ignore mesh vs. mesh. _filter = filter; _collisionObjectFilter = new SceneCollisionObjectFilter(filter); var collisionDetection = new CollisionDetection { CollisionFilter = null, }; // DualPartition is better for frustum culling. _collisionDomain = new CollisionDomain(collisionDetection) { BroadPhase = new DualPartition <CollisionObject> { Filter = _collisionObjectFilter }, }; }
public MassCollisionsTest(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; SetCamera(new Vector3F(0, 0, 20), 0, 0); // We use one collision domain that manages all objects. _domain = new CollisionDomain { EnableMultithreading = false }; //_domain.BroadPhase = new AabbTree<CollisionObject>(); //_domain.BroadPhase = new DynamicAabbTree<CollisionObject> { EnableMotionPrediction = true, OptimizationPerFrame = 0.01f }; //_domain.BroadPhase = new DualPartition<CollisionObject>(); //_domain.BroadPhase = new DualPartition<CollisionObject>(new AdaptiveAabbTree<CollisionObject>(), new AdaptiveAabbTree<CollisionObject>()); //_domain.BroadPhase = new DualPartition<CollisionObject>(new AdaptiveAabbTree<CollisionObject>(), new DynamicAabbTree<CollisionObject> { EnableMotionPrediction = true, OptimizationPerFrame = 0.01f }); //_domain.BroadPhase = new DebugSpatialPartition<CollisionObject>(); //_domain.BroadPhase = new AdaptiveAabbTree<CollisionObject>(); // Create the 6 box planes. CreateBoundaryPlanes(); // Create a lot of random objects. CreateRandomObjects(); }
public void TestEnabledDisabledStochastic() { // Test random Enabled and Pose values. int numberOfObjects = 20; int numberOfSteps = 1000; Shape shape = new SphereShape(1); //var meshShape = new TriangleMeshShape(shape.GetMesh(0.01f, 4)); //meshShape.Partition = new AabbTree<int>(); //shape = meshShape; var geometricObjects = new GeometricObject[numberOfObjects]; var collisionObjects = new CollisionObject[numberOfObjects]; var domain = new CollisionDomain(new CollisionDetection()); for (int i = 0; i < numberOfObjects; i++) { geometricObjects[i] = new GeometricObject(shape); collisionObjects[i] = new CollisionObject(geometricObjects[i]); domain.CollisionObjects.Add(collisionObjects[i]); } for (int i = 0; i < numberOfSteps; i++) { for (int j = 0; j < numberOfObjects; j++) { collisionObjects[j].Enabled = RandomHelper.Random.NextBool(); domain.Update(0); if (RandomHelper.Random.NextFloat(0, 1) > 0.5f) { geometricObjects[j].Pose = new Pose(RandomHelper.Random.NextVector3(-2, 2)); } domain.Update(0); } domain.Update(0); domain.Update(0); domain.Update(0); domain.Update(0); domain.Update(0); domain.Update(0); domain.Update(0); // Compare result with brute-force check. for (int j = 0; j < numberOfObjects; j++) { for (int k = j + 1; k < numberOfObjects; k++) { var haveContact = domain.CollisionDetection.HaveContact(collisionObjects[j], collisionObjects[k]); Assert.AreEqual(haveContact, domain.ContactSets.GetContacts(collisionObjects[j], collisionObjects[k]) != null); } } } }
public CharacterControllerSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; // Create a camera. var projection = new PerspectiveProjection(); projection.SetFieldOfView( ConstantsF.PiOver4, GraphicsService.GraphicsDevice.Viewport.AspectRatio, 0.1f, 1000.0f); _cameraNode = new CameraNode(new Camera(projection)); GraphicsScreen.CameraNode = _cameraNode; // We use one collision domain that computes collision info for all game objects. _domain = new CollisionDomain(new CollisionDetection()); // Create collision objects for a test level. CharacterControllerLevel.Load(_domain); // Add collision filter: // The _domain contains a lot of collision objects for obstacles in the level. // We do not need to compute contacts between these static obstacles. To avoid // this, the CharacterControllerLevel puts all level collision objects into // the collision group 1. We add a broad phase collision filter which filters out // collision checks between objects of collision group 1. _domain.BroadPhase.Filter = new DelegatePairFilter <CollisionObject>( pair => { if (pair.First.CollisionGroup == 1 && pair.Second.CollisionGroup == 1) { return(false); } return(true); }); // Create character controller. _character = new CharacterController(_domain); _character.Position = new Vector3F(0, 0, 1); // Create the trigger volume. _triggerVolume = new CollisionObject( new GeometricObject(new SphereShape(3), new Pose(new Vector3F(-5, 0, 5)))) { // We do not want to compute detailed contact information (contact points, contact // normal vectors, etc.). We are only interested if the object touches another object or not. // Therefore, we set the collision object type to "trigger". Trigger objects are better for // performance than normal collision objects. Additionally, the character controller should // be able to walk through the trigger volume. The character controller treats objects as // solids if it finds contact information (contact positions with contact normal vectors). Type = CollisionObjectType.Trigger }; _domain.CollisionObjects.Add(_triggerVolume); }
public void ToStringTest() { CollisionDomain cd = new CollisionDomain(new CollisionDetection()); cd.CollisionObjects.Add(new CollisionObject()); cd.CollisionObjects.Add(new CollisionObject()); Assert.AreEqual("CollisionDomain { Count = 2 }", cd.ToString()); }
public void Test3() { CollisionDomain domain = new CollisionDomain(new CollisionDetection()); RandomHelper.Random = new Random(123456); const float testAreaSize = 20; // Add 100 random objects. for (int i = 0; i < 100; i++) { domain.CollisionObjects.Add(new CollisionObject(new GeometricObject(new SphereShape(1), new Pose(RandomHelper.Random.NextVector3F(0, testAreaSize))))); } for (int i = 0; i < 100; i++) { domain.Update(0.03f); for (int j = 0; j < domain.CollisionObjects.Count; j++) { var a = domain.CollisionObjects[j]; for (int k = j + 1; k < domain.CollisionObjects.Count; k++) { var b = domain.CollisionObjects[k]; bool contained = domain.InternalBroadPhase.CandidatePairs.Contains(a, b); bool haveContact = GeometryHelper.HaveContact(a.GeometricObject.Aabb, b.GeometricObject.Aabb); //if (contained != haveContact) //Debugger.Break(); Assert.AreEqual(contained, haveContact); } // Set new random position for a few. if (RandomHelper.Random.NextFloat(0, 1) < 0.7f) { ((GeometricObject)a.GeometricObject).Pose = new Pose(RandomHelper.Random.NextVector3F(0, testAreaSize)); } } // Add new object. domain.CollisionObjects.Add(new CollisionObject { GeometricObject = new GeometricObject { Shape = new SphereShape(1), Pose = new Pose(RandomHelper.Random.NextVector3F(0, testAreaSize)), } }); // Remove random object. domain.CollisionObjects.Remove(domain.CollisionObjects[RandomHelper.Random.NextInteger(0, domain.CollisionObjects.Count - 1)]); Console.WriteLine("Candidate pairs: " + domain.InternalBroadPhase.CandidatePairs.Count); } }
/// <overloads> /// <summary> /// Advances the simulation by the given time. /// </summary> /// </overloads> /// /// <summary> /// Advances the simulation by the given time. /// </summary> /// <param name="deltaTime">The size of the time step.</param> /// <remarks> /// See <see cref="Simulation"/> for more information. /// </remarks> public void Update(TimeSpan deltaTime) { if (deltaTime == TimeSpan.Zero) return; // Apply speedup factor. deltaTime = new TimeSpan((long)(deltaTime.Ticks * TimeScaling)); _fixedTimeStep = Settings.Timing.FixedTimeStep; TimeSpan fixedTimeStep = new TimeSpan((long)(_fixedTimeStep * TimeSpan.TicksPerSecond)); // Negative time steps are not allowed. MaxNumberOfSteps limits the max allowed time step. // If deltaTime is larger, then some time is lost. TimeSpan minTimeStep = TimeSpan.Zero; TimeSpan maxTimeStep = new TimeSpan(fixedTimeStep.Ticks * Settings.Timing.MaxNumberOfSteps); deltaTime = MathHelper.Clamp(deltaTime, minTimeStep, maxTimeStep); // Update target time. TargetTime += deltaTime; //if (Settings.Timing.UseFixedTimeStep) // TimeStep = Settings.Timing.FixedTimeStep; //else // TimeStep = deltaTime; // Loop until target time is reached or the difference to target time is less than // the time step size. while (TargetTime - Time >= fixedTimeStep) { if (Settings.EnableMultithreading) { SubTimeStep_Multithreaded(fixedTimeStep); } else { SubTimeStep_Singlethreaded(fixedTimeStep); } } if (Settings.SynchronizeCollisionDomain) { // Update collision domain so that user sees new contacts. But don't recycle // the old contact sets because they are still referenced by contact constraints. CollisionDomain.Update(0, false); } // Reset user forces. { int numberOfRigidBodies = RigidBodies.Count; for (int i = 0; i < numberOfRigidBodies; i++) { RigidBody body = RigidBodies[i]; body.ClearForces(); } } }
public CharacterControllerSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; // Create a camera. var projection = new PerspectiveProjection(); projection.SetFieldOfView( ConstantsF.PiOver4, GraphicsService.GraphicsDevice.Viewport.AspectRatio, 0.1f, 1000.0f); _cameraNode = new CameraNode(new Camera(projection)); GraphicsScreen.CameraNode = _cameraNode; // We use one collision domain that computes collision info for all game objects. _domain = new CollisionDomain(new CollisionDetection()); // Create collision objects for a test level. CharacterControllerLevel.Load(_domain); // Add collision filter: // The _domain contains a lot of collision objects for obstacles in the level. // We do not need to compute contacts between these static obstacles. To avoid // this, the CharacterControllerLevel puts all level collision objects into // the collision group 1. We add a broad phase collision filter which filters out // collision checks between objects of collision group 1. _domain.BroadPhase.Filter = new DelegatePairFilter<CollisionObject>( pair => { if (pair.First.CollisionGroup == 1 && pair.Second.CollisionGroup == 1) return false; return true; }); // Create character controller. _character = new CharacterController(_domain); _character.Position = new Vector3F(0, 0, 1); // Create the trigger volume. _triggerVolume = new CollisionObject( new GeometricObject(new SphereShape(3), new Pose(new Vector3F(-5, 0, 5)))) { // We do not want to compute detailed contact information (contact points, contact // normal vectors, etc.). We are only interested if the object touches another object or not. // Therefore, we set the collision object type to "trigger". Trigger objects are better for // performance than normal collision objects. Additionally, the character controller should // be able to walk through the trigger volume. The character controller treats objects as // solids if it finds contact information (contact positions with contact normal vectors). Type = CollisionObjectType.Trigger }; _domain.CollisionObjects.Add(_triggerVolume); }
public CollisionDetectionSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; SetCamera(new Vector3F(0, 1, 10), 0, 0); // ----- Initialize collision detection and create objects. // Create a geometric object with a box shape. // Position it on the left with an arbitrary rotation. var geometricObjectA = new GeometricObject( new BoxShape(1, 2, 3), new Pose(new Vector3F(-2, -1, 0), Matrix33F.CreateRotationZ(0.1f))); // Create a geometric object with a capsule shape. // Position it on the right with an arbitrary rotation. var geometricObjectB = new GeometricObject( new CapsuleShape(1, 3), new Pose(new Vector3F(2, -1, 0), Matrix33F.CreateRotationZ(-0.2f))); // Create a geometric object with a complex shape that is the convex hull of // a circle and a rectangle. Position it on the top with an arbitrary rotation. // (A ConvexHullOfShapes is a collection of different shapes with different // positions and orientations. The ConvexHullOfShapes combines these shapes // into a single shape by building their convex hull.) var complexShape = new ConvexHullOfShapes(); complexShape.Children.Add(new GeometricObject(new RectangleShape(1, 1), new Pose(new Vector3F(0, 0, 1)))); complexShape.Children.Add(new GeometricObject(new CircleShape(1), new Pose(new Vector3F(0, 0, -1)))); var geometricObjectC = new GeometricObject( complexShape, new Pose(new Vector3F(0, 2, 0), QuaternionF.CreateRotation(Vector3F.UnitZ, new Vector3F(1, 1, 1)))); // Create collision objects for the geometric objects. // (A collision object is just a wrapper around the geometric object that // stores additional information that is required by the collision detection.) _collisionObjectA = new CollisionObject(geometricObjectA); _collisionObjectB = new CollisionObject(geometricObjectB); _collisionObjectC = new CollisionObject(geometricObjectC); // Create a collision detection. // (The CollisionDetection stores general parameters and it can be used to // perform closest-point and contact queries.) _collisionDetection = new CollisionDetection(); // Create a new collision domain and add the collision objects. // (A CollisionDomain manages multiple collision objects. It improves the // performance of contact queries by reusing results of the last frame.) _domain = new CollisionDomain(_collisionDetection); _domain.CollisionObjects.Add(_collisionObjectA); _domain.CollisionObjects.Add(_collisionObjectB); _domain.CollisionObjects.Add(_collisionObjectC); }
public void TestEnabledDisabled() { var shape = new SphereShape(1); var goA = new GeometricObject(shape, Pose.Identity); var coA = new CollisionObject(goA); var goB = new GeometricObject(shape, Pose.Identity); var coB = new CollisionObject(goB); var cd = new CollisionDomain(new CollisionDetection()); cd.CollisionObjects.Add(coA); cd.CollisionObjects.Add(coB); // not touching goB.Pose = new Pose(new Vector3(3, 3, 0)); cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // not touching, disabled coB.Enabled = false; cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // touching, disabled goB.Pose = new Pose(new Vector3(1, 1, 1)); cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // touching, enabled coB.Enabled = true; cd.Update(0); Assert.AreEqual(1, cd.ContactSets.Count); // not touching - but broadphase overlap, enabled goB.Pose = new Pose(new Vector3(1.8f, 1.8f, 0)); cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // not touching, disabled coB.Enabled = false; cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // touching, disabled goB.Pose = new Pose(new Vector3(1, 1, 1)); cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // touching, enabled coB.Enabled = true; cd.Update(0); Assert.AreEqual(1, cd.ContactSets.Count); }
public void ValidateNewObjectWithInvalidShape() { GlobalSettings.ValidationLevel = 0xff; CollisionDomain cd = new CollisionDomain(new CollisionDetection()); var shape = new SphereShape(1); var geometricObject = new GeometricObject(shape, Pose.Identity); var co = new CollisionObject(geometricObject); shape.Radius = float.NaN; Assert.Throws <GeometryException>(() => cd.CollisionObjects.Add(co)); }
/// <summary> /// Initializes a new instance of the <see cref="CollisionDetectionBroadPhase"/> class. /// </summary> public CollisionDetectionBroadPhase(CollisionDomain collisionDomain) { _collisionDomain = collisionDomain; // Register event handler. _collisionDomain.CollisionObjects.CollectionChanged += OnCollisionObjectsChanged; // Per default we use Sweep and Prune. SpatialPartition = new SweepAndPruneSpace <CollisionObject>(); CandidatePairs = new ContactSetCollection(); }
public void ValidateInvalidScaleChange() { GlobalSettings.ValidationLevel = 0xff; CollisionDomain cd = new CollisionDomain(new CollisionDetection()); var shape = new SphereShape(1); var geometricObject = new GeometricObject(shape, Pose.Identity); var co = new CollisionObject(geometricObject); cd.CollisionObjects.Add(co); Assert.Throws <GeometryException>(() => geometricObject.Scale = new Vector3(1, 1, float.NaN)); }
/// <summary> /// Resets the collision object. (For internal use only.) /// </summary> internal void ResetInternal() { Changed = true; CollisionGroup = 0; _domain = null; Enabled = true; _geometricObject = null; _type = CollisionObjectType.Default; _shapeType = ShapeType.Default; _shape = null; ShapeTypeChanged = true; }
private void ComputeTimeOfImpact_Singlethreaded(ContactSet contactSet) { RigidBody bodyA = contactSet.ObjectA.GeometricObject as RigidBody; RigidBody bodyB = contactSet.ObjectB.GeometricObject as RigidBody; // Note: We do CCD only between rigid bodies. // TODO: Support CCD between RigidBody and general CollisionObject of the user. if (bodyA != null && bodyB != null) { // Check if at least one object needs CCD. if (bodyA.IsCcdActive || bodyB.IsCcdActive) { // Check CCD filter. (We check this filter first because it hopefully filters out a lot // of unnecessary checks (e.g. body against debris).) Func<RigidBody, RigidBody, bool> ccdFilter = Settings.Motion.CcdFilter; if (ccdFilter != null && !ccdFilter(bodyA, bodyB)) return; // Check collision filter. (Using the internal method CanCollide that uses a cache.) if (!CollisionDomain.CanCollide(contactSet)) return; // If convex bodies are touching we do not need to compute TOI because they are either // moving more toward each other (TOI = 0) or separating (TOI = 1). If they are moving // toward each other, then the contact constraint has failed and CCD is not the problem. // For objects vs. concave objects we still have to make a check because the bullet // could be separating and colliding with another part of the concave object. // To avoid that objects stick: GetTimeOfImpact() should not return 0 for separating // objects and we use 2 * AllowedPenetration so that the current contact does not // count. if (!(bodyA.Shape is ConvexShape) || !(bodyB.Shape is ConvexShape) || !CollisionDomain.HaveContact(contactSet.ObjectA, contactSet.ObjectB)) { // Get time of impact. float timeOfImpact = CollisionDomain.CollisionDetection.GetTimeOfImpact( contactSet.ObjectA, bodyA.IsCcdActive ? bodyA.TargetPose : bodyA.Pose, contactSet.ObjectB, bodyB.IsCcdActive ? bodyB.TargetPose : bodyB.Pose, Settings.Constraints.AllowedPenetration * 2f); // Store minimal time of impact. if (timeOfImpact < bodyA.TimeOfImpact) bodyA.TimeOfImpact = timeOfImpact; if (timeOfImpact < bodyB.TimeOfImpact) bodyB.TimeOfImpact = timeOfImpact; } } } }
//-------------------------------------------------------------- #region Methods //-------------------------------------------------------------- #region ----- Internal ----- // The following methods are used internally by the collision detection to make direct // changes to a CollisionObject during collision checks. /// <summary> /// Copies the data from the specified <see cref="CollisionObject"/> and sets the specified /// <see cref="IGeometricObject"/>. (For internal use only.) /// </summary> /// <param name="collisionObject">The collision object.</param> /// <param name="geometricObject">The geometric object.</param> internal void SetInternal(CollisionObject collisionObject, IGeometricObject geometricObject) { Changed = collisionObject.Changed; CollisionGroup = collisionObject.CollisionGroup; _domain = collisionObject._domain; Enabled = collisionObject.Enabled; _geometricObject = geometricObject; _type = collisionObject._type; _shapeType = collisionObject._shapeType; _shape = geometricObject.Shape; ShapeTypeChanged = collisionObject.ShapeTypeChanged; }
//-------------------------------------------------------------- #region Creation & Cleanup //-------------------------------------------------------------- public CharacterController(CollisionDomain domain) { _collisionDomain = domain; // Create a game object for the character controller. GeometricObject = new GeometricObject( new CapsuleShape(Width / 2, Height), new Pose(new Vector3F(0, Height / 2, 0))); // Create a collision object for the game object and add it to the collision domain. CollisionObject = new CollisionObject(GeometricObject); _collisionDomain.CollisionObjects.Add(CollisionObject); }
public RuneTriggerVolume(Simulation simulation, Shape shape, string nameOfTarget) { this.collisionDomain = simulation.CollisionDomain; this.nameOfTarget = nameOfTarget; this.GeometricObject = new GeometricObject(shape, new Pose(simulation.World.Aabb.Center)); this.CollisionObject = new CollisionObject(GeometricObject) { Type = CollisionObjectType.Trigger }; simulation.CollisionDomain.CollisionObjects.Add(CollisionObject); }
public void NoValidation1() { GlobalSettings.ValidationLevel = 0; CollisionDomain cd = new CollisionDomain(new CollisionDetection()); var shape = new SphereShape(1); var geometricObject = new GeometricObject(shape, Pose.Identity); var co = new CollisionObject(geometricObject); // No exception with validation level 0. shape.Radius = float.NaN; cd.CollisionObjects.Add(co); }
public void Test2() { // All objects touch. CollisionObject a = new CollisionObject(new GeometricObject(new SphereShape(1), new Pose(new Vector3F(0, 0, 0)))); CollisionObject b = new CollisionObject(new GeometricObject(new SphereShape(1), new Pose(new Vector3F(1, 1, 0)))); CollisionObject c = new CollisionObject(new GeometricObject(new SphereShape(1), new Pose(new Vector3F(10, 10, 10)))); CollisionObject d = new CollisionObject(new GeometricObject(new SphereShape(1), new Pose(new Vector3F(0, 0, -1)))); CollisionDomain domain = new CollisionDomain(new CollisionDetection()); Assert.AreEqual(0, domain.InternalBroadPhase.CandidatePairs.Count); domain.InternalBroadPhase.Update(); Assert.AreEqual(0, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Add(a); domain.InternalBroadPhase.Update(); Assert.AreEqual(0, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Add(b); domain.InternalBroadPhase.Update(); Assert.AreEqual(1, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Add(c); domain.InternalBroadPhase.Update(); Assert.AreEqual(1, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Add(d); domain.InternalBroadPhase.Update(); Assert.AreEqual(3, domain.InternalBroadPhase.CandidatePairs.Count); foreach (ContactSet set in domain.InternalBroadPhase.CandidatePairs) { Assert.AreNotEqual(set.ObjectA, set.ObjectB); } domain.CollisionObjects.Remove(b); domain.InternalBroadPhase.Update(); Assert.AreEqual(1, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Remove(a); domain.CollisionObjects.Remove(a); domain.InternalBroadPhase.Update(); Assert.AreEqual(0, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Remove(d); domain.CollisionObjects.Remove(d); domain.InternalBroadPhase.Update(); Assert.AreEqual(0, domain.InternalBroadPhase.CandidatePairs.Count); }
// Adds a game object and adds a collision object to the collision domain. private static void AddObject(string name, Pose pose, Shape shape, CollisionDomain collisionDomain) { // Create game object. var geometricObject = new GeometricObject(shape, pose); // Create collision object. var collisionObject = new CollisionObject(geometricObject) { CollisionGroup = 1, }; // Add collision object to collision domain. collisionDomain.CollisionObjects.Add(collisionObject); }
public void Test1() { // All objects touch. CollisionObject a = new CollisionObject { GeometricObject = new GeometricObject { Shape = new SphereShape(1) } }; CollisionObject b = new CollisionObject { GeometricObject = new GeometricObject { Shape = new SphereShape(1) } }; CollisionObject c = new CollisionObject { GeometricObject = new GeometricObject { Shape = new SphereShape(1) } }; CollisionObject d = new CollisionObject { GeometricObject = new GeometricObject { Shape = new SphereShape(1) } }; CollisionDomain domain = new CollisionDomain(new CollisionDetection()); Assert.AreEqual(0, domain.InternalBroadPhase.CandidatePairs.Count); domain.InternalBroadPhase.Update(); Assert.AreEqual(0, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Add(a); domain.InternalBroadPhase.Update(); Assert.AreEqual(0, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Add(b); domain.InternalBroadPhase.Update(); Assert.AreEqual(1, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Add(c); domain.InternalBroadPhase.Update(); Assert.AreEqual(3, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Add(d); domain.InternalBroadPhase.Update(); Assert.AreEqual(6, domain.InternalBroadPhase.CandidatePairs.Count); foreach (ContactSet set in domain.InternalBroadPhase.CandidatePairs) Assert.AreNotEqual(set.ObjectA, set.ObjectB); domain.CollisionObjects.Remove(b); domain.CollisionObjects.Remove(b); domain.InternalBroadPhase.Update(); Assert.AreEqual(3, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Remove(a); domain.CollisionObjects.Remove(a); domain.InternalBroadPhase.Update(); Assert.AreEqual(1, domain.InternalBroadPhase.CandidatePairs.Count); domain.CollisionObjects.Remove(d); domain.CollisionObjects.Remove(d); domain.InternalBroadPhase.Update(); Assert.AreEqual(0, domain.InternalBroadPhase.CandidatePairs.Count); }
public MassCollisionsSample(Microsoft.Xna.Framework.Game game) : base(game) { GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; SetCamera(new Vector3F(0, 0, 20), 0, 0); // We use one collision domain that manages all objects. _domain = new CollisionDomain(new CollisionDetection()); // Create the 6 box planes. CreateBoundaryPlanes(); // Create a lot of random objects. CreateRandomObjects(); }
public void ValidateInvalidPoseChange() { GlobalSettings.ValidationLevel = 0xff; CollisionDomain cd = new CollisionDomain(new CollisionDetection()); var shape = new SphereShape(1); var geometricObject = new GeometricObject(shape, Pose.Identity); var co = new CollisionObject(geometricObject); cd.CollisionObjects.Add(co); var matrix = Matrix33F.Identity; matrix.M11 = float.NaN; Assert.Throws <GeometryException>(() => geometricObject.Pose = new Pose(new Vector3F(), matrix)); }
public MassCollisionsSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; SetCamera(new Vector3F(0, 0, 20), 0, 0); // We use one collision domain that manages all objects. _domain = new CollisionDomain(new CollisionDetection()); // Create the 6 box planes. CreateBoundaryPlanes(); // Create a lot of random objects. CreateRandomObjects(); }
public void RayCastStopsAtFirstHitWhenChangingFilter() { CollisionDomain domain = new CollisionDomain(new CollisionDetection()); domain.CollisionDetection.CollisionFilter = new CollisionFilter(); // 1 ray: at origin shooting into +x CollisionObject ray = new CollisionObject(); ((GeometricObject)ray.GeometricObject).Shape = new RayShape(new Vector3(), new Vector3(1, 0, 0), 100) { StopsAtFirstHit = true, }; //ray.Name = "Ray"; // 2 spheres: at at x=10, b at x=20 CollisionObject a = new CollisionObject(); ((GeometricObject)a.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)a.GeometricObject).Pose = new Pose(new Vector3(10, 0, 0f)); //a.Name = "b"; CollisionObject b = new CollisionObject(); ((GeometricObject)b.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3(20, 0, 0f)); //b.Name = "c"; domain.CollisionObjects.Add(ray); domain.CollisionObjects.Add(a); domain.CollisionObjects.Add(b); // Ray touches b. domain.Update(0.01f); Assert.AreEqual(1, domain.GetContacts(ray).Count()); Assert.AreEqual(true, domain.HaveContact(ray, a)); Assert.AreEqual(false, domain.HaveContact(ray, b)); // Disable collisions between ray and a. // Then ray must hit b. ((CollisionFilter)domain.CollisionDetection.CollisionFilter).Set(ray, a, false); domain.Update(0.01f); Assert.AreEqual(1, domain.GetContacts(ray).Count()); Assert.AreEqual(false, domain.HaveContact(ray, a)); Assert.AreEqual(true, domain.HaveContact(ray, b)); }
private bool _drawDebugInfo; // true if the collision shapes should be drawn for debugging. public ContentPipelineSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; SetCamera(new Vector3(0, 1, 10), 0, 0); // Initialize collision detection. // Note: The physics Simulation also has a collision domain (Simulation.CollisionDomain) // which we could use and which is updated together with the Simulation in // SampleGame.cs. But in this example we create our own CollisionDomain for demonstration // purposes. _collisionDomain = new CollisionDomain(new CollisionDetection()); // Register CollisionDomain in service container. Services.Register(typeof(CollisionDomain), null, _collisionDomain); // Add game objects which manage graphics models and their collision representations. _saucerObject = new SaucerObject(Services) { Name = "Saucer" }; _shipObjectA = new ShipObject(Services) { Name = "ShipA" }; _shipObjectB = new ShipObject(Services) { Name = "ShipB" }; GameObjectService.Objects.Add(_saucerObject); GameObjectService.Objects.Add(_shipObjectA); GameObjectService.Objects.Add(_shipObjectB); // Position the second ship right of the first ship with an arbitrary rotation. _shipObjectB.Pose = new Pose(new Vector3(2, 0, 0), Quaternion.CreateRotationY(0.7f) * Quaternion.CreateRotationX(1.2f)); // Position the saucer left of the first ship with an arbitrary rotation. _saucerObject.Pose = new Pose(new Vector3(-2.5f, 0, 0), Quaternion.CreateRotationY(0.2f) * Quaternion.CreateRotationX(0.4f)); }
public void Filtering() { CollisionDomain domain = new CollisionDomain(new CollisionDetection()); domain.CollisionDetection.CollisionFilter = new CollisionFilter(); CollisionObject a = new CollisionObject(); ((GeometricObject)a.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)a.GeometricObject).Pose = new Pose(new Vector3(0, 0, 0)); //a.Name = "a"; CollisionObject b = new CollisionObject(); ((GeometricObject)b.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3(1, 0, 0f)); //b.Name = "b"; domain.CollisionObjects.Add(a); domain.CollisionObjects.Add(b); domain.Update(0.01f); Assert.AreEqual(1, domain.ContactSets.Count); b.Enabled = false; domain.Update(0.01f); Assert.AreEqual(0, domain.ContactSets.Count); a.Enabled = false; b.Enabled = true; domain.Update(0.01f); Assert.AreEqual(0, domain.ContactSets.Count); a.Enabled = true; ((CollisionFilter)domain.CollisionDetection.CollisionFilter).Set(a, b, false); domain.Update(0.01f); Assert.AreEqual(0, domain.ContactSets.Count); ((CollisionFilter)domain.CollisionDetection.CollisionFilter).Set(a, b, true); domain.Update(0.01f); Assert.AreEqual(1, domain.ContactSets.Count); }
public void Filtering() { CollisionDomain domain = new CollisionDomain(new CollisionDetection()); domain.CollisionDetection.CollisionFilter = new CollisionFilter(); CollisionObject a = new CollisionObject(); ((GeometricObject)a.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)a.GeometricObject).Pose = new Pose(new Vector3F(0, 0, 0)); //a.Name = "a"; CollisionObject b = new CollisionObject(); ((GeometricObject)b.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(1, 0, 0f)); //b.Name = "b"; domain.CollisionObjects.Add(a); domain.CollisionObjects.Add(b); domain.Update(0.01f); Assert.AreEqual(1, domain.ContactSets.Count); b.Enabled = false; domain.Update(0.01f); Assert.AreEqual(0, domain.ContactSets.Count); a.Enabled = false; b.Enabled = true; domain.Update(0.01f); Assert.AreEqual(0, domain.ContactSets.Count); a.Enabled = true; ((CollisionFilter) domain.CollisionDetection.CollisionFilter).Set(a, b, false); domain.Update(0.01f); Assert.AreEqual(0, domain.ContactSets.Count); ((CollisionFilter) domain.CollisionDetection.CollisionFilter).Set(a, b, true); domain.Update(0.01f); Assert.AreEqual(1, domain.ContactSets.Count); }
//-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="Simulation"/> class. /// </summary> public Simulation() { Settings = new SimulationSettings(); var collisionDetection = new CollisionDetection { CollisionFilter = new CollisionFilter(), FullContactSetPerFrame = true }; CollisionDomain = new CollisionDomain(collisionDetection); ConstraintSolver = new SequentialImpulseBasedSolver(this); Constraints = new ConstraintCollection(); Constraints.CollectionChanged += OnConstraintsChanged; ForceEffects = new ForceEffectCollection(); ForceEffects.CollectionChanged += OnForceEffectsChanged; RigidBodies = new RigidBodyCollection(); RigidBodies.CollectionChanged += OnRigidBodiesChanged; ContactConstraintsInternal = new List<ContactConstraint>(); IslandManager = new SimulationIslandManager(this); // Define the "World" as a rigid body. // - World is an imaginary body that is used to define the space of the simulation. // - The user can use World in constraints e.g. to anchor objects in the world. // - No contacts are computed for World. World = new RigidBody(new BoxShape(20000, 20000, 20000)) { CollisionResponseEnabled = false, CollisionObject = { Type = CollisionObjectType.Trigger }, MotionType = MotionType.Static, Name = "World", Simulation = this, }; // Remove "World" from the collision domain. CollisionDomain.CollisionObjects.Remove(World.CollisionObject); #if STOPWATCH Diagnostics = new SimulationDiagnostics(); #endif // Store delegate methods to avoid garbage when multithreading. _updateVelocityMethod = i => { var body = RigidBodies[i]; body.UpdateVelocity(_fixedTimeStep); }; _solveIslandMethod = SolveIsland; _updatePoseMethod = i => { var body = RigidBodies[i]; body.UpdatePose(_fixedTimeStep); }; _computeTimeOfImpactMethod = ComputeTimeOfImpact_Multithreaded; _moveToTimeOfImpactMethod = MoveToTimeOfImpact; }
private bool _cullingEnabled = true; // True to use frustum culling. False to disable frustum culling. public FrustumCullingSample(Microsoft.Xna.Framework.Game game) : base(game) { GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; // The top-down camera. var orthographicProjection = new OrthographicProjection(); orthographicProjection.Set( LevelSize * 1.1f * GraphicsService.GraphicsDevice.Viewport.AspectRatio, LevelSize * 1.1f, 1, 10000f); var topDownCamera = new Camera(orthographicProjection); _topDownCameraNode = new CameraNode(topDownCamera) { View = Matrix44F.CreateLookAt(new Vector3F(0, 1000, 0), new Vector3F(0, 0, 0), -Vector3F.UnitZ), }; // The perspective camera moving through the scene. var perspectiveProjection = new PerspectiveProjection(); perspectiveProjection.SetFieldOfView( MathHelper.ToRadians(45), GraphicsService.GraphicsDevice.Viewport.AspectRatio, 1, 500); var sceneCamera = new Camera(perspectiveProjection); _sceneCameraNode = new CameraNode(sceneCamera); // Initialize collision detection. // We use one collision domain that manages all objects. _domain = new CollisionDomain(new CollisionDetection()) { // We exchange the default broad phase with a DualPartition. The DualPartition // has special support for frustum culling. BroadPhase = new DualPartition<CollisionObject>(), }; // Create a lot of random objects and add them to the collision domain. RandomHelper.Random = new Random(12345); for (int i = 0; i < NumberOfObjects; i++) { // A real scene consists of a lot of complex objects such as characters, vehicles, // buildings, lights, etc. When doing frustum culling we need to test each objects against // the viewing frustum. If it intersects with the viewing frustum, the object is visible // from the camera's point of view. However, in practice we do not test the exact object // against the viewing frustum. Each objects is approximated by a simpler shape. In our // example, we assume that each object is approximated with an oriented bounding box. // (We could also use an other shape, such as a bounding sphere.) // Create a random box. Shape randomShape = new BoxShape(RandomHelper.Random.NextVector3F(1, 10)); // Create a random position. Vector3F randomPosition; randomPosition.X = RandomHelper.Random.NextFloat(-LevelSize / 2, LevelSize / 2); randomPosition.Y = RandomHelper.Random.NextFloat(0, 2); randomPosition.Z = RandomHelper.Random.NextFloat(-LevelSize / 2, LevelSize / 2); // Create a random orientation. QuaternionF randomOrientation = RandomHelper.Random.NextQuaternionF(); // Create object and add it to collision domain. var geometricObject = new GeometricObject(randomShape, new Pose(randomPosition, randomOrientation)); var collisionObject = new CollisionObject(geometricObject) { CollisionGroup = 0, }; _domain.CollisionObjects.Add(collisionObject); } // Per default, the collision domain computes collision between all objects. // In this sample we do not need this information and disable it with a collision // filter. // In a real application, we would use this collision information for rendering, // for example, to find out which lights overlap with which meshes, etc. var filter = new CollisionFilter(); // Disable collision between objects in collision group 0. filter.Set(0, 0, false); _domain.CollisionDetection.CollisionFilter = filter; // Start with the scene camera. GraphicsScreen.CameraNode = _sceneCameraNode; // We will collect a few statistics for debugging. Profiler.SetFormat("NoCull", 1000, "Time in ms to submit DebugRenderer draw jobs without frustum culling."); Profiler.SetFormat("WithCull", 1000, "Time in ms to submit DebugRenderer draw jobs with frustum culling."); }
public PickingSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; GraphicsScreen.DrawReticle = true; SetCamera(new Vector3F(0, 1, 10), 0, 0); // ----- Initialize collision detection system. // We use one collision domain that manages all objects. _domain = new CollisionDomain { // Optional: Change the broad phase type. The default type is the SweepAndPruneSpace, // which is very fast for physics simulation. The DualPartition is better for ray casts. // See also http://digitalrune.github.io/DigitalRune-Documentation/html/e32cab3b-cc7c-42ee-8ec9-23dd4467edd0.htm#WhichPartition BroadPhase = new DualPartition<CollisionObject>(), }; // Optional: Set a broad phase filter. // Per default, the collision domain computes contacts between all collision objects. If we // are only interested in ray vs non-ray-shape contacts, we can set a filter to avoid // unnecessary intersection computations and improve performance. _domain.BroadPhase.Filter = new DelegatePairFilter<CollisionObject>( pair => { var firstIsRay = pair.First.GeometricObject.Shape is RayShape; var secondIsRay = pair.Second.GeometricObject.Shape is RayShape; return firstIsRay != secondIsRay; }); // Create a collision object with a box shape at position (0, 0, 0) with a random rotation. _box = new CollisionObject( new GeometricObject( new BoxShape(1, 2, 3), new Pose(new Vector3F(0, 0, 0), RandomHelper.Random.NextQuaternionF()))); // Create a collision object with a sphere shape at position (-5, 0, 0). _sphere = new CollisionObject(new GeometricObject(new SphereShape(1), new Pose(new Vector3F(-5, 0, 0)))); // Create a random list of points. var points = new List<Vector3F>(); for (int i = 0; i < 100; i++) points.Add(RandomHelper.Random.NextVector3F(-1.5f, 1.5f)); // Create a triangle mesh of the convex hull. // (See also the ConvexHullSample for info on convex hull creation.) TriangleMesh triangleMesh = GeometryHelper.CreateConvexHull(points).ToTriangleMesh(); // We use this random triangle mesh to define a shape. TriangleMeshShape meshShape = new TriangleMeshShape(triangleMesh); // Optional: We can use a spatial partitioning method, to speed up collision // detection for large meshes. AABB trees are good for static triangle meshes. // To use spatial partitioning we have to set a valid spatial partition instance // in the Partition property. // The spatial partition will store indices of the mesh triangles, therefore // the generic type argument is "int". meshShape.Partition = new AabbTree<int>() { // Optional: The tree is automatically built using a mixed top-down/bottom-up approach. // Bottom-up building is slower but produces better trees. If the tree building takes too // long, we can lower the BottomUpBuildThreshold (default is 128). BottomUpBuildThreshold = 0, }; // Optional: Build the AABB tree. (This is done automatically when the AABB tree is used for // the first time, but Update can also be called explicitly to control when the tree is built.) meshShape.Partition.Update(false); // Create a collision object with the random triangle mesh shape. _mesh = new CollisionObject(new GeometricObject(meshShape, new Pose(new Vector3F(5, 0, 0)))); // Add collision object to collision domain. _domain.CollisionObjects.Add(_box); _domain.CollisionObjects.Add(_sphere); _domain.CollisionObjects.Add(_mesh); // For picking we create a ray. // The ray shoot from its local origin in +x direction. // (Note: The last parameter is the length of the ray. In theory, rays have // an infinite length. However, in the collision detection we use rays with // a finite length. This increases the performance and improves the numerical // stability of the algorithms.) RayShape rayShape = new RayShape(Vector3F.Zero, Vector3F.Forward, 1000); _ray = new CollisionObject(new GeometricObject(rayShape, Pose.Identity)); // The ray is just one additional collision object in our collision domain. _domain.CollisionObjects.Add(_ray); // The collision domain manages now 4 objects: a box, a sphere, a triangle mesh and a ray. }
public static void Load(CollisionDomain collisionDomain) { // Create a box for the ground. AddObject("Ground", new Pose(new Vector3F(0, -5, 0)), new BoxShape(60, 10, 60), collisionDomain); // Create a small flying sphere to visualize the approx. head height. - This is just // for debugging so that we have a feeling for heights. AddObject("Sphere", new Pose(new Vector3F(0, 1.5f, 0)), new SphereShape(0.2f), collisionDomain); // Create small walls at the level boundary. AddObject("WallLeft", new Pose(new Vector3F(-30, 1, 0)), new BoxShape(0.3f, 2, 60), collisionDomain); AddObject("WallRight", new Pose(new Vector3F(30, 1, 0)), new BoxShape(0.3f, 2, 60), collisionDomain); AddObject("WallFront", new Pose(new Vector3F(0, 1, -30)), new BoxShape(60, 2, 0.3f), collisionDomain); AddObject("WallBack", new Pose(new Vector3F(0, 1, 30)), new BoxShape(60, 2, 0.3f), collisionDomain); // Create a few bigger objects. // We position the boxes so that we have a few corners we can run into. Character controllers // should be stable when the user runs into corners. AddObject("House0", new Pose(new Vector3F(10, 1, -10)), new BoxShape(8, 2, 8f), collisionDomain); AddObject("House1", new Pose(new Vector3F(13, 1, -4)), new BoxShape(2, 2, 4), collisionDomain); AddObject("House2", new Pose(new Vector3F(10, 2, -15), Matrix33F.CreateRotationY(-0.3f)), new BoxShape(8, 4, 2), collisionDomain); // // Create stairs with increasing step height. // // Each step is a box. With this object we can test if our character can climb up // stairs. The character controller has a step height limit. Increasing step heights // let us test if the step height limit works. float startHeight = 0; const float stepDepth = 1f; for (int i = 0; i < 10; i++) { float stepHeight = 0.1f + i * 0.05f; Pose pose = new Pose(new Vector3F(0, startHeight + stepHeight / 2, -2 - i * stepDepth)); BoxShape shape = new BoxShape(2, stepHeight, stepDepth); AddObject("Step" + i, pose, shape, collisionDomain); startHeight += stepHeight; } // // Create a height field. // // Terrain that is uneven is best modeled with a height field. Height fields are faster // than general triangle meshes. // The height direction is the y direction. // The height field lies in the x/z plane. var numberOfSamplesX = 20; var numberOfSamplesZ = 20; var samples = new float[numberOfSamplesX * numberOfSamplesZ]; // Create arbitrary height values. for (int z = 0; z < numberOfSamplesZ; z++) { for (int x = 0; x < numberOfSamplesX; x++) { if (x == 0 || z == 0 || x == 19 || z == 19) { // Set this boundary elements to a low height, so that the height field is connected // to the ground. samples[z * numberOfSamplesX + x] = -1; } else { // A sine/cosine function that creates some interesting waves. samples[z * numberOfSamplesX + x] = 1.0f + (float)(Math.Cos(z / 2f) * Math.Sin(x / 2f) * 1.0f); } } } var heightField = new HeightField(0, 0, 20, 20, samples, numberOfSamplesX, numberOfSamplesZ); AddObject("Heightfield", new Pose(new Vector3F(10, 0, 10)), heightField, collisionDomain); // Create rubble on the floor (small random objects on the floor). // Our character should be able to move over small bumps on the ground. for (int i = 0; i < 50; i++) { Pose pose = new Pose( new Vector3F(RandomHelper.Random.NextFloat(-5, 5), 0, RandomHelper.Random.NextFloat(10, 20)), RandomHelper.Random.NextQuaternionF()); BoxShape shape = new BoxShape(RandomHelper.Random.NextVector3F(0.05f, 0.8f)); AddObject("Stone" + i, pose, shape, collisionDomain); } // Create some slopes to see how our character performs on/under sloped surfaces. // Here we can test how the character controller behaves if the head touches an inclined // ceiling. AddObject("SlopeGround", new Pose(new Vector3F(-2, 1.8f, -12), QuaternionF.CreateRotationX(0.4f)), new BoxShape(2, 0.5f, 10), collisionDomain); AddObject("SlopeRoof", new Pose(new Vector3F(-2, 5.6f, -12), QuaternionF.CreateRotationX(-0.4f)), new BoxShape(2, 0.5f, 10), collisionDomain); // Slopes with different tilt angles. // The character controller has a slope limit. Only flat slopes should be climbable. for (int i = 0; i < 10; i++) { float stepHeight = 0.1f + i * 0.1f; Pose pose = new Pose( new Vector3F(-10, i * 0.5f, -i * 2), Matrix33F.CreateRotationX(MathHelper.ToRadians(10) + i * MathHelper.ToRadians(10))); BoxShape shape = new BoxShape(8 * (1 - i * 0.1f), 0.5f, 30); AddObject("Slope" + i, pose, shape, collisionDomain); startHeight += stepHeight; } // Create a slope with a wall on one side. // This objects let's us test how the character controller behaves while falling and // sliding along a vertical wall. (Run up the slope and then jump down while moving into // the wall.) AddObject("LongSlope", new Pose(new Vector3F(-20, 3, -10), Matrix33F.CreateRotationX(0.4f)), new BoxShape(4, 5f, 30), collisionDomain); AddObject("LongSlopeWall", new Pose(new Vector3F(-22, 5, -10)), new BoxShape(0.5f, 10f, 25), collisionDomain); // Create a mesh object to test walking on triangle meshes. // Normally, the mesh would be loaded from a file. Here, we make a composite shape and // let DigitalRune Geometry compute a mesh for it. Then we throw away the composite // shape and use only the mesh. - We do this to test triangle meshes. Using the composite // shape instead of the triangle mesh would be a lot faster. CompositeShape compositeShape = new CompositeShape(); compositeShape.Children.Add(new GeometricObject(heightField, Pose.Identity)); compositeShape.Children.Add(new GeometricObject(new CylinderShape(1, 2), new Pose(new Vector3F(10, 1, 10)))); compositeShape.Children.Add(new GeometricObject(new SphereShape(3), new Pose(new Vector3F(15, 0, 15)))); compositeShape.Children.Add(new GeometricObject(new BoxShape(1, 2, 3), new Pose(new Vector3F(15, 0, 5)))); ITriangleMesh mesh = compositeShape.GetMesh(0.01f, 3); TriangleMeshShape meshShape = new TriangleMeshShape(mesh); // Collision detection speed for triangle meshes can be improved by using a spatial // partition. Here, we assign an AabbTree to the triangle mesh shape. The tree is // built automatically when needed and it stores triangle indices (therefore the generic // parameter of the AabbTree is int). meshShape.Partition = new AabbTree <int>() { // The tree is automatically built using a mixed top-down/bottom-up approach. Bottom-up // building is slower but produces better trees. If the tree building takes too long, // we can lower the BottomUpBuildThreshold (default is 128). BottomUpBuildThreshold = 0, }; AddObject("Mesh", new Pose(new Vector3F(-30, 0, 10)), meshShape, collisionDomain); }
public static void Load(CollisionDomain collisionDomain) { // Create a box for the ground. AddObject("Ground", new Pose(new Vector3F(0, -5, 0)), new BoxShape(60, 10, 60), collisionDomain); // Create a small flying sphere to visualize the approx. head height. - This is just // for debugging so that we have a feeling for heights. AddObject("Sphere", new Pose(new Vector3F(0, 1.5f, 0)), new SphereShape(0.2f), collisionDomain); // Create small walls at the level boundary. AddObject("WallLeft", new Pose(new Vector3F(-30, 1, 0)), new BoxShape(0.3f, 2, 60), collisionDomain); AddObject("WallRight", new Pose(new Vector3F(30, 1, 0)), new BoxShape(0.3f, 2, 60), collisionDomain); AddObject("WallFront", new Pose(new Vector3F(0, 1, -30)), new BoxShape(60, 2, 0.3f), collisionDomain); AddObject("WallBack", new Pose(new Vector3F(0, 1, 30)), new BoxShape(60, 2, 0.3f), collisionDomain); // Create a few bigger objects. // We position the boxes so that we have a few corners we can run into. Character controllers // should be stable when the user runs into corners. AddObject("House0", new Pose(new Vector3F(10, 1, -10)), new BoxShape(8, 2, 8f), collisionDomain); AddObject("House1", new Pose(new Vector3F(13, 1, -4)), new BoxShape(2, 2, 4), collisionDomain); AddObject("House2", new Pose(new Vector3F(10, 2, -15), Matrix33F.CreateRotationY(-0.3f)), new BoxShape(8, 4, 2), collisionDomain); // // Create stairs with increasing step height. // // Each step is a box. With this object we can test if our character can climb up // stairs. The character controller has a step height limit. Increasing step heights // let us test if the step height limit works. float startHeight = 0; const float stepDepth = 1f; for (int i = 0; i < 10; i++) { float stepHeight = 0.1f + i * 0.05f; Pose pose = new Pose(new Vector3F(0, startHeight + stepHeight / 2, -2 - i * stepDepth)); BoxShape shape = new BoxShape(2, stepHeight, stepDepth); AddObject("Step" + i, pose, shape, collisionDomain); startHeight += stepHeight; } // // Create a height field. // // Terrain that is uneven is best modeled with a height field. Height fields are faster // than general triangle meshes. // The height direction is the y direction. // The height field lies in the x/z plane. var numberOfSamplesX = 20; var numberOfSamplesZ = 20; var samples = new float[numberOfSamplesX * numberOfSamplesZ]; // Create arbitrary height values. for (int z = 0; z < numberOfSamplesZ; z++) { for (int x = 0; x < numberOfSamplesX; x++) { if (x == 0 || z == 0 || x == 19 || z == 19) { // Set this boundary elements to a low height, so that the height field is connected // to the ground. samples[z * numberOfSamplesX + x] = -1; } else { // A sine/cosine function that creates some interesting waves. samples[z * numberOfSamplesX + x] = 1.0f + (float)(Math.Cos(z / 2f) * Math.Sin(x / 2f) * 1.0f); } } } var heightField = new HeightField(0, 0, 20, 20, samples, numberOfSamplesX, numberOfSamplesZ); AddObject("Heightfield", new Pose(new Vector3F(10, 0, 10)), heightField, collisionDomain); // Create rubble on the floor (small random objects on the floor). // Our character should be able to move over small bumps on the ground. for (int i = 0; i < 50; i++) { Pose pose = new Pose( new Vector3F(RandomHelper.Random.NextFloat(-5, 5), 0, RandomHelper.Random.NextFloat(10, 20)), RandomHelper.Random.NextQuaternionF()); BoxShape shape = new BoxShape(RandomHelper.Random.NextVector3F(0.05f, 0.8f)); AddObject("Stone" + i, pose, shape, collisionDomain); } // Create some slopes to see how our character performs on/under sloped surfaces. // Here we can test how the character controller behaves if the head touches an inclined // ceiling. AddObject("SlopeGround", new Pose(new Vector3F(-2, 1.8f, -12), QuaternionF.CreateRotationX(0.4f)), new BoxShape(2, 0.5f, 10), collisionDomain); AddObject("SlopeRoof", new Pose(new Vector3F(-2, 5.6f, -12), QuaternionF.CreateRotationX(-0.4f)), new BoxShape(2, 0.5f, 10), collisionDomain); // Slopes with different tilt angles. // The character controller has a slope limit. Only flat slopes should be climbable. for (int i = 0; i < 10; i++) { float stepHeight = 0.1f + i * 0.1f; Pose pose = new Pose( new Vector3F(-10, i * 0.5f, -i * 2), Matrix33F.CreateRotationX(MathHelper.ToRadians(10) + i * MathHelper.ToRadians(10))); BoxShape shape = new BoxShape(8 * (1 - i * 0.1f), 0.5f, 30); AddObject("Slope" + i, pose, shape, collisionDomain); startHeight += stepHeight; } // Create a slope with a wall on one side. // This objects let's us test how the character controller behaves while falling and // sliding along a vertical wall. (Run up the slope and then jump down while moving into // the wall.) AddObject("LongSlope", new Pose(new Vector3F(-20, 3, -10), Matrix33F.CreateRotationX(0.4f)), new BoxShape(4, 5f, 30), collisionDomain); AddObject("LongSlopeWall", new Pose(new Vector3F(-22, 5, -10)), new BoxShape(0.5f, 10f, 25), collisionDomain); // Create a mesh object to test walking on triangle meshes. // Normally, the mesh would be loaded from a file. Here, we make a composite shape and // let DigitalRune Geometry compute a mesh for it. Then we throw away the composite // shape and use only the mesh. - We do this to test triangle meshes. Using the composite // shape instead of the triangle mesh would be a lot faster. CompositeShape compositeShape = new CompositeShape(); compositeShape.Children.Add(new GeometricObject(heightField, Pose.Identity)); compositeShape.Children.Add(new GeometricObject(new CylinderShape(1, 2), new Pose(new Vector3F(10, 1, 10)))); compositeShape.Children.Add(new GeometricObject(new SphereShape(3), new Pose(new Vector3F(15, 0, 15)))); compositeShape.Children.Add(new GeometricObject(new BoxShape(1, 2, 3), new Pose(new Vector3F(15, 0, 5)))); ITriangleMesh mesh = compositeShape.GetMesh(0.01f, 3); TriangleMeshShape meshShape = new TriangleMeshShape(mesh); // Collision detection speed for triangle meshes can be improved by using a spatial // partition. Here, we assign an AabbTree to the triangle mesh shape. The tree is // built automatically when needed and it stores triangle indices (therefore the generic // parameter of the AabbTree is int). meshShape.Partition = new AabbTree<int>() { // The tree is automatically built using a mixed top-down/bottom-up approach. Bottom-up // building is slower but produces better trees. If the tree building takes too long, // we can lower the BottomUpBuildThreshold (default is 128). BottomUpBuildThreshold = 0, }; AddObject("Mesh", new Pose(new Vector3F(-30, 0, 10)), meshShape, collisionDomain); }
public void RayCastStopsAtFirstHit() { CollisionDomain domain = new CollisionDomain(new CollisionDetection()); CollisionObject ray = new CollisionObject(); ((GeometricObject)ray.GeometricObject).Shape = new RayShape(new Vector3F(), new Vector3F(1, 0, 0), 100) { StopsAtFirstHit = true, }; //ray.Name = "Ray"; CollisionObject b = new CollisionObject(); ((GeometricObject)b.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(-10, 0, 0f)); //b.Name = "b"; CollisionObject c = new CollisionObject(); ((GeometricObject)c.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)c.GeometricObject).Pose = new Pose(new Vector3F(0, 0, 0f)); //c.Name = "c"; CollisionObject d = new CollisionObject(); ((GeometricObject)d.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)d.GeometricObject).Pose = new Pose(new Vector3F(10, 0, 0f)); //d.Name = "d"; CollisionObject e = new CollisionObject(); ((GeometricObject)e.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)e.GeometricObject).Pose = new Pose(new Vector3F(20, 0, 0f)); //e.Name = "e"; CollisionObject f = new CollisionObject(); ((GeometricObject)f.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)f.GeometricObject).Pose = new Pose(new Vector3F(110, 0, 0f)); //f.Name = "f"; // Positions: b=-10, c=0, d=10, e=20, f=110 domain.CollisionObjects.Add(ray); domain.CollisionObjects.Add(b); domain.CollisionObjects.Add(d); domain.CollisionObjects.Add(c); domain.CollisionObjects.Add(e); domain.CollisionObjects.Add(f); domain.Update(0.01f); Assert.AreEqual(1, domain.GetContacts(ray).Count()); Assert.AreEqual(true, domain.HaveContact(ray, c)); ((GeometricObject)c.GeometricObject).Pose = new Pose(new Vector3F(30)); // Positions: b=-10, d=10, e=20, c=30, f=110 domain.Update(0.01f); Assert.AreEqual(1, domain.GetContacts(ray).Count()); Assert.AreEqual(true, domain.HaveContact(ray, d)); ((GeometricObject)d.GeometricObject).Pose = new Pose(new Vector3F(40)); // Positions: b=-10, e=20, c=30, d=40, f=110 domain.Update(0.01f); Assert.AreEqual(1, domain.GetContacts(ray).Count()); Assert.AreEqual(true, domain.HaveContact(ray, e)); ((GeometricObject)ray.GeometricObject).Pose = new Pose(((GeometricObject)ray.GeometricObject).Pose.Position, QuaternionF.CreateRotationZ(ConstantsF.PiOver2)); domain.Update(0.01f); Assert.AreEqual(0, domain.GetContacts(ray).Count()); ((GeometricObject)ray.GeometricObject).Pose = new Pose(((GeometricObject)ray.GeometricObject).Pose.Position, QuaternionF.CreateRotationZ(ConstantsF.Pi)); domain.Update(0.01f); Assert.AreEqual(1, domain.GetContacts(ray).Count()); Assert.AreEqual(true, domain.HaveContact(ray, b)); ((GeometricObject)ray.GeometricObject).Pose = new Pose(((GeometricObject)ray.GeometricObject).Pose.Position, QuaternionF.Identity); domain.Update(0.01f); // Positions: b=-10, e=20, c=30, d=40, f=110 CollisionObject gNotInDomain = new CollisionObject(); ((GeometricObject)gNotInDomain.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)gNotInDomain.GeometricObject).Pose = new Pose(new Vector3F(10, 0, 0f)); Assert.AreEqual(true, domain.HaveContact(ray, gNotInDomain)); Assert.AreEqual(1, domain.GetContacts(gNotInDomain).Count()); Assert.AreEqual(1, domain.GetContacts(ray, gNotInDomain).Count); Assert.AreEqual(true, domain.HasContact(gNotInDomain)); ((GeometricObject)gNotInDomain.GeometricObject).Pose = new Pose(new Vector3F(25, 0, 0f)); // behind e Assert.AreEqual(false, domain.HaveContact(ray, gNotInDomain)); Assert.AreEqual(false, domain.HaveContact(gNotInDomain, ray)); Assert.AreEqual(false, domain.HasContact(gNotInDomain)); Assert.AreEqual(0, domain.GetContacts(gNotInDomain).Count()); Assert.IsNull(domain.GetContacts(ray, gNotInDomain)); Assert.IsNull(domain.GetContacts(gNotInDomain, ray)); // Remove ray from domain. domain.CollisionObjects.Remove(ray); domain.Update(0.01f); Assert.AreEqual(0, domain.ContactSets.Count); // Positions: b=-10, e=20, g=25, c=30, d=40, f=110 domain.Update(0.01f); Assert.AreEqual(1, domain.GetContacts(ray).Count()); Assert.AreEqual(true, domain.HaveContact(ray, e)); Assert.AreEqual(false, domain.HaveContact(ray, c)); Assert.AreEqual(false, domain.HaveContact(ray, gNotInDomain)); Assert.IsNull(domain.GetContacts(ray, gNotInDomain)); }
public void RayCastStopsAtFirstHitWhenChangingFilter() { CollisionDomain domain = new CollisionDomain(new CollisionDetection()); domain.CollisionDetection.CollisionFilter = new CollisionFilter(); // 1 ray: at origin shooting into +x CollisionObject ray = new CollisionObject(); ((GeometricObject)ray.GeometricObject).Shape = new RayShape(new Vector3F(), new Vector3F(1, 0, 0), 100) { StopsAtFirstHit = true, }; //ray.Name = "Ray"; // 2 spheres: at at x=10, b at x=20 CollisionObject a = new CollisionObject(); ((GeometricObject)a.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)a.GeometricObject).Pose = new Pose(new Vector3F(10, 0, 0f)); //a.Name = "b"; CollisionObject b = new CollisionObject(); ((GeometricObject)b.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(20, 0, 0f)); //b.Name = "c"; domain.CollisionObjects.Add(ray); domain.CollisionObjects.Add(a); domain.CollisionObjects.Add(b); // Ray touches b. domain.Update(0.01f); Assert.AreEqual(1, domain.GetContacts(ray).Count()); Assert.AreEqual(true, domain.HaveContact(ray, a)); Assert.AreEqual(false, domain.HaveContact(ray, b)); // Disable collisions between ray and a. // Then ray must hit b. ((CollisionFilter)domain.CollisionDetection.CollisionFilter).Set(ray, a, false); domain.Update(0.01f); Assert.AreEqual(1, domain.GetContacts(ray).Count()); Assert.AreEqual(false, domain.HaveContact(ray, a)); Assert.AreEqual(true, domain.HaveContact(ray, b)); }
public void Test1() { Assert.NotNull(new CollisionDomain().CollisionDetection); Assert.NotNull(new CollisionDomain(null).CollisionDetection); CollisionDomain cd = new CollisionDomain(new CollisionDetection()); CollisionObject a = new CollisionObject(); ((GeometricObject)a.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)a.GeometricObject).Pose = new Pose(new Vector3F(0, 0, 0)); //a.Name = "a"; CollisionObject b = new CollisionObject(); ((GeometricObject)b.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(4, 0, 2f)); //b.Name = "b"; CollisionObject c = new CollisionObject(); ((GeometricObject)c.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)c.GeometricObject).Pose = new Pose(new Vector3F(6, 2, 2f)); //c.Name = "c"; CollisionObject d = new CollisionObject(); ((GeometricObject)d.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)d.GeometricObject).Pose = new Pose(new Vector3F(8, 3f, 4f)); //d.Name = "d"; Assert.AreEqual(0, cd.CollisionObjects.Count); cd.CollisionObjects.Add(a); Assert.AreEqual(1, cd.CollisionObjects.Count); cd.CollisionObjects.Add(b); Assert.AreEqual(2, cd.CollisionObjects.Count); cd.CollisionObjects.Add(c); Assert.AreEqual(3, cd.CollisionObjects.Count); cd.CollisionObjects.Add(d); Assert.AreEqual(4, cd.CollisionObjects.Count); cd.Update(0.01f); Assert.AreEqual(0, cd.ContactSets.Count); ((GeometricObject)a.GeometricObject).Pose = new Pose(((GeometricObject)a.GeometricObject).Pose.Position + new Vector3F(3.5f, 0, 0.5f)); cd.Update(0.01f); Assert.AreEqual(1, cd.ContactSets.Count); ((GeometricObject)c.GeometricObject).Pose = new Pose(((GeometricObject)c.GeometricObject).Pose.Position + new Vector3F(2, 1, 0)); cd.Update(0.01f); Assert.AreEqual(2, cd.ContactSets.Count); ((GeometricObject)a.GeometricObject).Pose = new Pose(((GeometricObject)a.GeometricObject).Pose.Position + new Vector3F(3, 0, 0)); ((GeometricObject)b.GeometricObject).Pose = new Pose(((GeometricObject)b.GeometricObject).Pose.Position + new Vector3F(2, 0, 0)); cd.Update(0.01f); Assert.AreEqual(2, cd.ContactSets.Count); ((GeometricObject)d.GeometricObject).Pose = new Pose(((GeometricObject)d.GeometricObject).Pose.Position + new Vector3F(-0.5f, 0, 0)); ((GeometricObject)b.GeometricObject).Pose = new Pose(((GeometricObject)b.GeometricObject).Pose.Position + new Vector3F(0, 1, 0)); cd.Update(0.01f); Assert.AreEqual(1, cd.ContactSets.Count); ((GeometricObject)a.GeometricObject).Pose = new Pose(new Vector3F(0, 0, 0)); ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(0, 0, 0)); ((GeometricObject)c.GeometricObject).Pose = new Pose(new Vector3F(1, 0, 1)); ((GeometricObject)d.GeometricObject).Pose = new Pose(new Vector3F(0, 1, 0)); cd.Update(0.01f); Assert.AreEqual(6, cd.ContactSets.Count); }
public void Test3() { CollisionDomain domain = new CollisionDomain(new CollisionDetection()); RandomHelper.Random = new Random(123456); const float testAreaSize = 20; // Add 100 random objects. for (int i = 0; i < 100; i++) domain.CollisionObjects.Add(new CollisionObject(new GeometricObject(new SphereShape(1), new Pose(RandomHelper.Random.NextVector3F(0, testAreaSize))))); for (int i = 0; i < 100; i++) { domain.Update(0.03f); for (int j = 0; j < domain.CollisionObjects.Count; j++) { var a = domain.CollisionObjects[j]; for (int k = j + 1; k < domain.CollisionObjects.Count; k++) { var b = domain.CollisionObjects[k]; bool contained = domain.InternalBroadPhase.CandidatePairs.Contains(a, b); bool haveContact = GeometryHelper.HaveContact(a.GeometricObject.Aabb, b.GeometricObject.Aabb); //if (contained != haveContact) //Debugger.Break(); Assert.AreEqual(contained, haveContact); } // Set new random position for a few. if (RandomHelper.Random.NextFloat(0, 1) < 0.7f) ((GeometricObject)a.GeometricObject).Pose = new Pose(RandomHelper.Random.NextVector3F(0, testAreaSize)); } // Add new object. domain.CollisionObjects.Add(new CollisionObject { GeometricObject = new GeometricObject { Shape = new SphereShape(1), Pose = new Pose(RandomHelper.Random.NextVector3F(0, testAreaSize)), } }); // Remove random object. domain.CollisionObjects.Remove(domain.CollisionObjects[RandomHelper.Random.NextInteger(0, domain.CollisionObjects.Count - 1)]); Console.WriteLine("Candidate pairs: " + domain.InternalBroadPhase.CandidatePairs.Count); } }
public void TestEnabledDisabled() { var shape = new SphereShape(1); var goA = new GeometricObject(shape, Pose.Identity); var coA = new CollisionObject(goA); var goB = new GeometricObject(shape, Pose.Identity); var coB = new CollisionObject(goB); var cd = new CollisionDomain(new CollisionDetection()); cd.CollisionObjects.Add(coA); cd.CollisionObjects.Add(coB); // not touching goB.Pose = new Pose(new Vector3F(3, 3, 0)); cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // not touching, disabled coB.Enabled = false; cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // touching, disabled goB.Pose = new Pose(new Vector3F(1, 1, 1)); cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // touching, enabled coB.Enabled = true; cd.Update(0); Assert.AreEqual(1, cd.ContactSets.Count); // not touching - but broadphase overlap, enabled goB.Pose = new Pose(new Vector3F(1.8f, 1.8f, 0)); cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // not touching, disabled coB.Enabled = false; cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // touching, disabled goB.Pose = new Pose(new Vector3F(1, 1, 1)); cd.Update(0); Assert.AreEqual(0, cd.ContactSets.Count); // touching, enabled coB.Enabled = true; cd.Update(0); Assert.AreEqual(1, cd.ContactSets.Count); }
public void UpdateSingle() { CollisionDomain domain = new CollisionDomain(new CollisionDetection()); // Add 100 random objects. for (int i = 0; i < 100; i++) domain.CollisionObjects.Add(new CollisionObject(new GeometricObject(new SphereShape(1), new Pose(RandomHelper.Random.NextVector3F(0, 20))))); for (int i = 0; i < 100; i++) { domain.Update(domain.CollisionObjects[33]); for (int j = 0; j < domain.CollisionObjects.Count; j++) { var a = domain.CollisionObjects[j]; for (int k = j + 1; k < domain.CollisionObjects.Count; k++) { var b = domain.CollisionObjects[k]; Assert.AreEqual(domain.InternalBroadPhase.CandidatePairs.Contains(a, b), GeometryHelper.HaveContact(a.GeometricObject.Aabb, b.GeometricObject.Aabb)); } } // Set new random position for one. ((GeometricObject)domain.CollisionObjects[33].GeometricObject).Pose = new Pose(RandomHelper.Random.NextVector3F(0, 20)); Console.WriteLine("Candidate pairs: " + domain.InternalBroadPhase.CandidatePairs.Count); } }
// The following methods are used internally by the collision detection to make direct // changes to a CollisionObject during collision checks. /// <summary> /// Copies the data from the specified <see cref="CollisionObject"/> and sets the specified /// <see cref="IGeometricObject"/>. (For internal use only.) /// </summary> /// <param name="collisionObject">The collision object.</param> /// <param name="geometricObject">The geometric object.</param> internal void SetInternal(CollisionObject collisionObject, IGeometricObject geometricObject) { Changed = collisionObject.Changed; CollisionGroup = collisionObject.CollisionGroup; _domain = collisionObject._domain; Enabled = collisionObject.Enabled; _geometricObject = geometricObject; _type = collisionObject._type; _shapeType = collisionObject._shapeType; _shape = geometricObject.Shape; ShapeTypeChanged = collisionObject.ShapeTypeChanged; }
public void ValidateInvalidScaleChange() { GlobalSettings.ValidationLevel = 0xff; CollisionDomain cd = new CollisionDomain(new CollisionDetection()); var shape = new SphereShape(1); var geometricObject = new GeometricObject(shape, Pose.Identity); var co = new CollisionObject(geometricObject); cd.CollisionObjects.Add(co); Assert.Throws<GeometryException>(() => geometricObject.Scale = new Vector3F(1, 1, float.NaN)); }
public void ValidateInvalidPoseChange() { GlobalSettings.ValidationLevel = 0xff; CollisionDomain cd = new CollisionDomain(new CollisionDetection()); var shape = new SphereShape(1); var geometricObject = new GeometricObject(shape, Pose.Identity); var co = new CollisionObject(geometricObject); cd.CollisionObjects.Add(co); var matrix = Matrix33F.Identity; matrix.M11 = float.NaN; Assert.Throws<GeometryException>(() => geometricObject.Pose = new Pose(new Vector3F(), matrix)); }
public PickingSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; GraphicsScreen.DrawReticle = true; SetCamera(new Vector3(0, 1, 10), 0, 0); // ----- Initialize collision detection system. // We use one collision domain that manages all objects. _domain = new CollisionDomain { // Optional: Change the broad phase type. The default type is the SweepAndPruneSpace, // which is very fast for physics simulation. The DualPartition is better for ray casts. // See also http://digitalrune.github.io/DigitalRune-Documentation/html/e32cab3b-cc7c-42ee-8ec9-23dd4467edd0.htm#WhichPartition BroadPhase = new DualPartition <CollisionObject>(), }; // Optional: Set a broad phase filter. // Per default, the collision domain computes contacts between all collision objects. If we // are only interested in ray vs non-ray-shape contacts, we can set a filter to avoid // unnecessary intersection computations and improve performance. _domain.BroadPhase.Filter = new DelegatePairFilter <CollisionObject>( pair => { var firstIsRay = pair.First.GeometricObject.Shape is RayShape; var secondIsRay = pair.Second.GeometricObject.Shape is RayShape; return(firstIsRay != secondIsRay); }); // Create a collision object with a box shape at position (0, 0, 0) with a random rotation. _box = new CollisionObject( new GeometricObject( new BoxShape(1, 2, 3), new Pose(new Vector3(0, 0, 0), RandomHelper.Random.NextQuaternion()))); // Create a collision object with a sphere shape at position (-5, 0, 0). _sphere = new CollisionObject(new GeometricObject(new SphereShape(1), new Pose(new Vector3(-5, 0, 0)))); // Create a random list of points. var points = new List <Vector3>(); for (int i = 0; i < 100; i++) { points.Add(RandomHelper.Random.NextVector3(-1.5f, 1.5f)); } // Create a triangle mesh of the convex hull. // (See also the ConvexHullSample for info on convex hull creation.) TriangleMesh triangleMesh = GeometryHelper.CreateConvexHull(points).ToTriangleMesh(); // We use this random triangle mesh to define a shape. TriangleMeshShape meshShape = new TriangleMeshShape(triangleMesh); // Optional: We can use a spatial partitioning method, to speed up collision // detection for large meshes. AABB trees are good for static triangle meshes. // To use spatial partitioning we have to set a valid spatial partition instance // in the Partition property. // The spatial partition will store indices of the mesh triangles, therefore // the generic type argument is "int". meshShape.Partition = new AabbTree <int>() { // Optional: The tree is automatically built using a mixed top-down/bottom-up approach. // Bottom-up building is slower but produces better trees. If the tree building takes too // long, we can lower the BottomUpBuildThreshold (default is 128). BottomUpBuildThreshold = 0, }; // Optional: Build the AABB tree. (This is done automatically when the AABB tree is used for // the first time, but Update can also be called explicitly to control when the tree is built.) meshShape.Partition.Update(false); // Create a collision object with the random triangle mesh shape. _mesh = new CollisionObject(new GeometricObject(meshShape, new Pose(new Vector3(5, 0, 0)))); // Add collision object to collision domain. _domain.CollisionObjects.Add(_box); _domain.CollisionObjects.Add(_sphere); _domain.CollisionObjects.Add(_mesh); // For picking we create a ray. // The ray shoot from its local origin in +x direction. // (Note: The last parameter is the length of the ray. In theory, rays have // an infinite length. However, in the collision detection we use rays with // a finite length. This increases the performance and improves the numerical // stability of the algorithms.) RayShape rayShape = new RayShape(Vector3.Zero, Vector3.Forward, 1000); _ray = new CollisionObject(new GeometricObject(rayShape, Pose.Identity)); // The ray is just one additional collision object in our collision domain. _domain.CollisionObjects.Add(_ray); // The collision domain manages now 4 objects: a box, a sphere, a triangle mesh and a ray. }
public void HaveContact() { CollisionDomain domain = new CollisionDomain(new CollisionDetection()); domain.CollisionDetection.CollisionFilter = new CollisionFilter(); CollisionObject a = new CollisionObject(); ((GeometricObject)a.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)a.GeometricObject).Pose = new Pose(new Vector3F(0, 0, 0)); //a.Name = "a"; CollisionObject b = new CollisionObject(); ((GeometricObject)b.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(1, 0, 0f)); //b.Name = "b"; CollisionObject c = new CollisionObject(); ((GeometricObject)c.GeometricObject).Shape = new SphereShape(1); ((GeometricObject)c.GeometricObject).Pose = new Pose(new Vector3F(1, 0, 0f)); //c.Name = "c"; domain.CollisionObjects.Add(a); domain.CollisionObjects.Add(b); domain.Update(0.01f); Assert.AreEqual(true, domain.HaveContact(a, b)); Assert.AreEqual(true, domain.HaveContact(a, c)); Assert.AreEqual(true, domain.HasContact(a)); Assert.AreEqual(true, domain.HasContact(b)); Assert.AreEqual(true, domain.HasContact(c)); Assert.AreEqual(1, domain.GetContacts(a, b).Count); Assert.AreEqual(1, domain.GetContacts(a, c).Count); Assert.AreEqual(1, domain.GetContacts(a).Count()); Assert.AreEqual(2, domain.GetContacts(c).Count()); Assert.AreEqual(1, domain.ContactSets.Count); b.Enabled = false; domain.Update(0.01f); Assert.AreEqual(false, domain.HaveContact(a, b)); Assert.AreEqual(true, domain.HaveContact(a, c)); Assert.AreEqual(false, domain.HasContact(a)); Assert.AreEqual(false, domain.HasContact(b)); Assert.AreEqual(true, domain.HasContact(c)); Assert.AreEqual(null, domain.GetContacts(a, b)); Assert.AreEqual(1, domain.GetContacts(a, c).Count); Assert.AreEqual(0, domain.GetContacts(a).Count()); Assert.AreEqual(1, domain.GetContacts(c).Count()); Assert.AreEqual(0, domain.ContactSets.Count); a.Enabled = false; b.Enabled = true; domain.Update(0.01f); Assert.AreEqual(false, domain.HaveContact(a, b)); Assert.AreEqual(false, domain.HaveContact(a, c)); Assert.AreEqual(false, domain.HasContact(a)); Assert.AreEqual(false, domain.HasContact(b)); Assert.AreEqual(true, domain.HasContact(c)); Assert.AreEqual(null, domain.GetContacts(a, b)); Assert.AreEqual(null, domain.GetContacts(a, c)); Assert.AreEqual(0, domain.GetContacts(a).Count()); Assert.AreEqual(1, domain.GetContacts(c).Count()); Assert.AreEqual(0, domain.ContactSets.Count); c.Enabled = false; domain.Update(0.01f); Assert.AreEqual(false, domain.HaveContact(a, b)); Assert.AreEqual(false, domain.HaveContact(a, c)); Assert.AreEqual(false, domain.HasContact(a)); Assert.AreEqual(false, domain.HasContact(b)); Assert.AreEqual(false, domain.HasContact(c)); Assert.AreEqual(null, domain.GetContacts(a, b)); Assert.AreEqual(null, domain.GetContacts(a, c)); Assert.AreEqual(0, domain.GetContacts(a).Count()); Assert.AreEqual(0, domain.GetContacts(c).Count()); Assert.AreEqual(0, domain.ContactSets.Count); a.Enabled = true; c.Enabled = true; ((CollisionFilter) domain.CollisionDetection.CollisionFilter).Set(a, b, false); domain.Update(0.01f); Assert.AreEqual(false, domain.HaveContact(a, b)); Assert.AreEqual(true, domain.HaveContact(a, c)); Assert.AreEqual(false, domain.HasContact(a)); Assert.AreEqual(false, domain.HasContact(b)); Assert.AreEqual(true, domain.HasContact(c)); Assert.AreEqual(null, domain.GetContacts(a, b)); Assert.AreEqual(1, domain.GetContacts(a, c).Count); Assert.AreEqual(0, domain.GetContacts(a).Count()); Assert.AreEqual(2, domain.GetContacts(c).Count()); Assert.AreEqual(0, domain.ContactSets.Count); ((CollisionFilter) domain.CollisionDetection.CollisionFilter).Set(a, b, true); domain.Update(0.01f); Assert.AreEqual(true, domain.HaveContact(a, b)); Assert.AreEqual(true, domain.HaveContact(a, c)); Assert.AreEqual(true, domain.HasContact(a)); Assert.AreEqual(true, domain.HasContact(b)); Assert.AreEqual(true, domain.HasContact(c)); Assert.AreEqual(1, domain.GetContacts(a, b).Count); Assert.AreEqual(1, domain.GetContacts(a, c).Count); Assert.AreEqual(1, domain.GetContacts(a).Count()); Assert.AreEqual(2, domain.GetContacts(c).Count()); Assert.AreEqual(1, domain.ContactSets.Count); c.Enabled = false; domain.Update(0.01f); Assert.AreEqual(true, domain.HaveContact(a, b)); Assert.AreEqual(false, domain.HaveContact(a, c)); Assert.AreEqual(true, domain.HasContact(a)); Assert.AreEqual(true, domain.HasContact(b)); Assert.AreEqual(false, domain.HasContact(c)); Assert.AreEqual(1, domain.GetContacts(a, b).Count); Assert.AreEqual(null, domain.GetContacts(a, c)); Assert.AreEqual(1, domain.GetContacts(a).Count()); Assert.AreEqual(0, domain.GetContacts(c).Count()); Assert.AreEqual(1, domain.ContactSets.Count); }
public FigurePickerObject(IGraphicsService graphicsService, Scene scene, CameraObject cameraObject, DebugRenderer debugRenderer) { _cameraObject = cameraObject; _scene = scene; _debugRenderer = debugRenderer; // Create a collision domain which manages all collision objects used for // picking: the picking object and the collision objects for figure nodes. _collisionDomain = new CollisionDomain(new CollisionDetection()); // Create the picking object: // The picking object represents the mouse cursor or the reticle. Usually // a ray is used, but in this example we want to use a cylinder/cone. This // allows to check which objects within a certain radius of the reticle. A // picking cylinder/cone is helpful for touch devices where the picking is // done with an imprecise input method like the human finger. // We want to pick objects in 10 pixel radius around the reticle. To determine // the world space size of the required cylinder/cone, we can use the projection // and the viewport. const float pickingRadius = 10; var projection = _cameraObject.CameraNode.Camera.Projection; var viewport = graphicsService.GraphicsDevice.Viewport; Shape pickingShape; if (projection is OrthographicProjection) { // Use cylinder for orthographic projections: // The cylinder is centered at the camera position and reaches from the // camera position to the camera far plane. A TransformedShape is used // to rotate and translate the cylinder. float radius = projection.Width / viewport.Width * pickingRadius; pickingShape = new TransformedShape( new GeometricObject( new CylinderShape(radius, projection.Far), new Pose(new Vector3F(0, 0, -projection.Far / 2), Matrix33F.CreateRotationX(ConstantsF.PiOver2)))); } else { // Use cone for perspective projections: // The cone tip is at the camera position and the cone base is at the // camera far plane. // Compute the radius at the far plane that projects to 10 pixels in screen space. float radius = viewport.Unproject( new Vector3(viewport.Width / 2.0f + pickingRadius, viewport.Height / 2.0f, 1), (Matrix)_cameraObject.CameraNode.Camera.Projection.ToMatrix44F(), Matrix.Identity, Matrix.Identity).X; // A transformed shape is used to rotate and translate the cone. pickingShape = new TransformedShape( new GeometricObject( new ConeShape(radius, projection.Far), new Pose(new Vector3F(0, 0, -projection.Far), Matrix33F.CreateRotationX(ConstantsF.PiOver2)))); } // Create collision object with the picking shape. _pickingObject = new CollisionObject(new GeometricObject(pickingShape, _cameraObject.CameraNode.PoseWorld)); }
public void ValidateNewObjectWithInvalidShape() { GlobalSettings.ValidationLevel = 0xff; CollisionDomain cd = new CollisionDomain(new CollisionDetection()); var shape = new SphereShape(1); var geometricObject = new GeometricObject(shape, Pose.Identity); var co = new CollisionObject(geometricObject); shape.Radius = float.NaN; Assert.Throws<GeometryException>(() => cd.CollisionObjects.Add(co)); }
private bool _cullingEnabled = true; // True to use frustum culling. False to disable frustum culling. public FrustumCullingSample(Microsoft.Xna.Framework.Game game) : base(game) { GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; // The top-down camera. var orthographicProjection = new OrthographicProjection(); orthographicProjection.Set( LevelSize * 1.1f * GraphicsService.GraphicsDevice.Viewport.AspectRatio, LevelSize * 1.1f, 1, 10000f); var topDownCamera = new Camera(orthographicProjection); _topDownCameraNode = new CameraNode(topDownCamera) { View = Matrix44F.CreateLookAt(new Vector3F(0, 1000, 0), new Vector3F(0, 0, 0), -Vector3F.UnitZ), }; // The perspective camera moving through the scene. var perspectiveProjection = new PerspectiveProjection(); perspectiveProjection.SetFieldOfView( MathHelper.ToRadians(45), GraphicsService.GraphicsDevice.Viewport.AspectRatio, 1, 500); var sceneCamera = new Camera(perspectiveProjection); _sceneCameraNode = new CameraNode(sceneCamera); // Initialize collision detection. // We use one collision domain that manages all objects. _domain = new CollisionDomain(new CollisionDetection()) { // We exchange the default broad phase with a DualPartition. The DualPartition // has special support for frustum culling. BroadPhase = new DualPartition <CollisionObject>(), }; // Create a lot of random objects and add them to the collision domain. RandomHelper.Random = new Random(12345); for (int i = 0; i < NumberOfObjects; i++) { // A real scene consists of a lot of complex objects such as characters, vehicles, // buildings, lights, etc. When doing frustum culling we need to test each objects against // the viewing frustum. If it intersects with the viewing frustum, the object is visible // from the camera's point of view. However, in practice we do not test the exact object // against the viewing frustum. Each objects is approximated by a simpler shape. In our // example, we assume that each object is approximated with an oriented bounding box. // (We could also use an other shape, such as a bounding sphere.) // Create a random box. Shape randomShape = new BoxShape(RandomHelper.Random.NextVector3F(1, 10)); // Create a random position. Vector3F randomPosition; randomPosition.X = RandomHelper.Random.NextFloat(-LevelSize / 2, LevelSize / 2); randomPosition.Y = RandomHelper.Random.NextFloat(0, 2); randomPosition.Z = RandomHelper.Random.NextFloat(-LevelSize / 2, LevelSize / 2); // Create a random orientation. QuaternionF randomOrientation = RandomHelper.Random.NextQuaternionF(); // Create object and add it to collision domain. var geometricObject = new GeometricObject(randomShape, new Pose(randomPosition, randomOrientation)); var collisionObject = new CollisionObject(geometricObject) { CollisionGroup = 0, }; _domain.CollisionObjects.Add(collisionObject); } // Per default, the collision domain computes collision between all objects. // In this sample we do not need this information and disable it with a collision // filter. // In a real application, we would use this collision information for rendering, // for example, to find out which lights overlap with which meshes, etc. var filter = new CollisionFilter(); // Disable collision between objects in collision group 0. filter.Set(0, 0, false); _domain.CollisionDetection.CollisionFilter = filter; // Start with the top-down camera. GraphicsScreen.CameraNode = _topDownCameraNode; // We will collect a few statistics for debugging. Profiler.SetFormat("NoCull", 1000, "Time in ms to submit DebugRenderer draw jobs without frustum culling."); Profiler.SetFormat("WithCull", 1000, "Time in ms to submit DebugRenderer draw jobs with frustum culling."); }
public void TestEnabledDisabledStochastic() { // Test random Enabled and Pose values. int numberOfObjects = 20; int numberOfSteps = 1000; Shape shape = new SphereShape(1); //var meshShape = new TriangleMeshShape(shape.GetMesh(0.01f, 4)); //meshShape.Partition = new AabbTree<int>(); //shape = meshShape; var geometricObjects = new GeometricObject[numberOfObjects]; var collisionObjects = new CollisionObject[numberOfObjects]; var domain = new CollisionDomain(new CollisionDetection()); for (int i = 0; i < numberOfObjects; i++) { geometricObjects[i] = new GeometricObject(shape); collisionObjects[i] = new CollisionObject(geometricObjects[i]); domain.CollisionObjects.Add(collisionObjects[i]); } for (int i = 0; i < numberOfSteps; i++) { for (int j = 0; j < numberOfObjects; j++) { collisionObjects[j].Enabled = RandomHelper.Random.NextBool(); domain.Update(0); if (RandomHelper.Random.NextFloat(0, 1) > 0.5f) geometricObjects[j].Pose = new Pose(RandomHelper.Random.NextVector3F(-2, 2)); domain.Update(0); } domain.Update(0); domain.Update(0); domain.Update(0); domain.Update(0); domain.Update(0); domain.Update(0); domain.Update(0); // Compare result with brute-force check. for (int j = 0; j < numberOfObjects; j++) { for (int k = j + 1; k < numberOfObjects; k++) { var haveContact = domain.CollisionDetection.HaveContact(collisionObjects[j], collisionObjects[k]); Assert.AreEqual(haveContact, domain.ContactSets.GetContacts(collisionObjects[j], collisionObjects[k]) != null); } } } }
public CollisionFilterSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.Gray; GraphicsScreen.DrawReticle = true; SetCamera(new Vector3F(0, 0, 10), 0, 0); // ----- Initialize collision detection system. // We use one collision domain that manages all objects. var domain = new CollisionDomain(); // Let's set a filter which disables collision between object in the same collision group. // We can use a broad phase or a narrow phase filter: // Option A) Broad phase filter // The collision detection broad phase computes bounding box overlaps. // A broad phase filter is best used if the filtering rules are simple and do not change // during the runtime of your application. //domain.BroadPhase.Filter = new DelegatePairFilter<CollisionObject>( // pair => pair.First.CollisionGroup != pair.Second.CollisionGroup); // Option B) Narrow phase filter // The collision detection narrow phase computes contacts between objects where the broad // phase has detected a bounding box overlap. Use a narrow phase filter if the filtering rules // are complex or can change during the runtime of your application. var filter = new CollisionFilter(); // Disable collision between objects in the same groups. filter.Set(0, 0, false); filter.Set(1, 1, false); filter.Set(2, 2, false); filter.Set(3, 3, false); domain.CollisionDetection.CollisionFilter = filter; // Create a random list of points. var points = new List<Vector3F>(); for (int i = 0; i < 100; i++) points.Add(RandomHelper.Random.NextVector3F(-1.5f, 1.5f)); // Add lots of spheres to the collision domain. Assign spheres to different collision groups. var random = new Random(); var sphereShape = new SphereShape(0.7f); for (int i = 0; i < 20; i++) { var randomPosition = new Vector3F(random.NextFloat(-6, 6), random.NextFloat(-3, 3), 0); var geometricObject = new GeometricObject(sphereShape, new Pose(randomPosition)); var collisionObject = new CollisionObject(geometricObject) { // A collision group is simply an integer. We can assign collision objects to collision // groups to control the collision filtering. CollisionGroup = random.NextInteger(0, 3), }; domain.CollisionObjects.Add(collisionObject); } // Compute collisions. (The objects do not move in this sample. Therefore, we only have to // call Update once.) domain.Update(0); // Draw objects. The color depends on the collision group. var debugRenderer = GraphicsScreen.DebugRenderer; debugRenderer.Clear(); foreach (var collisionObject in domain.CollisionObjects) { Color color; switch (collisionObject.CollisionGroup) { case 0: color = Color.LightBlue; break; case 1: color = Color.Yellow; break; case 2: color = Color.Orange; break; default: color = Color.LightGreen; break; } debugRenderer.DrawObject(collisionObject.GeometricObject, color, false, false); } debugRenderer.DrawContacts(domain.ContactSets, 0.1f, Color.Red, true); }