//-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="Wheel"/> class. /// </summary> public Wheel() { Radius = 0.4f; SuspensionRestLength = 0.6f; MinSuspensionLength = float.NegativeInfinity; SuspensionLength = SuspensionRestLength; PreviousSuspensionLength = SuspensionRestLength; SuspensionStiffness = 20; SuspensionCompressionDamping = 4f; SuspensionRelaxationDamping = 3f; MaxSuspensionForce = 6000; RollingFrictionForce = 500; Friction = 0.9f; RollReduction = 0.3f; Vector3F rayOrigin = Vector3F.Zero; Vector3F rayDirection = -Vector3F.UnitY; float rayLength = Radius + SuspensionRestLength; Ray = new RayShape(rayOrigin, rayDirection, rayLength) { StopsAtFirstHit = true, }; GeometricObject = new GeometricObject(Ray); CollisionObject = new CollisionObject(GeometricObject); }
protected override void OnLoad() { var contentManager = _services.GetInstance<ContentManager>(); // ----- Graphics // Load a graphics model and add it to the scene for rendering. _modelNode = contentManager.Load<ModelNode>("Ship/Ship").Clone(); _modelNode.PoseWorld = new Pose(Vector3F.Zero, Matrix33F.CreateRotationY(-ConstantsF.PiOver2)); var scene = _services.GetInstance<IScene>(); scene.Children.Add(_modelNode); // ----- Collision Detection // Create a collision object and add it to the collision domain. // Load collision shape from a separate model (created using the CollisionShapeProcessor). var shape = contentManager.Load<Shape>("Ship/Ship_CollisionModel"); // Create a GeometricObject (= Shape + Pose + Scale). _geometricObject = new GeometricObject(shape, _modelNode.PoseWorld); // Create a collision object and add it to the collision domain. _collisionObject = new CollisionObject(_geometricObject); // Important: We do not need detailed contact information when a collision // is detected. The information of whether we have contact or not is sufficient. // Therefore, we can set the type to "Trigger". This increases the performance // dramatically. _collisionObject.Type = CollisionObjectType.Trigger; // Add the collision object to the collision domain of the game. var collisionDomain = _services.GetInstance<CollisionDomain>(); collisionDomain.CollisionObjects.Add(_collisionObject); }
protected override void OnLoad() { var contentManager = _services.GetInstance<ContentManager>(); // ----- Graphics // Load graphics model (created using the ModelWithCollisionMeshProcessor). var sharedModelNode = contentManager.Load<ModelNode>("Saucer/saucer"); // Let's create a clone because we do not want to change the shared Saucer // instance stored in the content manager. _modelNode = sharedModelNode.Clone(); _modelNode.PoseWorld = new Pose(Vector3F.Zero, Matrix33F.CreateRotationY(-ConstantsF.PiOver2)); // The collision shape is stored in the UserData. var shape = (Shape)_modelNode.UserData; // Add model to the scene for rendering. var scene = _services.GetInstance<IScene>(); scene.Children.Add(_modelNode); // ----- Collision Detection // Create a collision object and add it to the collision domain. _geometricObject = new GeometricObject(shape, _modelNode.PoseWorld); _collisionObject = new CollisionObject(_geometricObject); // Important: We do not need detailed contact information when a collision // is detected. The information of whether we have contact or not is sufficient. // Therefore, we can set the type to "Trigger". This increases the performance // dramatically. _collisionObject.Type = CollisionObjectType.Trigger; var collisionDomain = _services.GetInstance<CollisionDomain>(); collisionDomain.CollisionObjects.Add(_collisionObject); }
public DumpContactSetSample(Microsoft.Xna.Framework.Game game) : base(game) { GraphicsScreen.ClearBackground = true; // Create two collision objects with triangle mesh shapes. var meshA = new SphereShape(1).GetMesh(0.01f, 4); var shapeA = new TriangleMeshShape(meshA, true) { Partition = new CompressedAabbTree() }; var poseA = new Pose(new Vector3F(-1, 0, 0), RandomHelper.Random.NextQuaternionF()); var collisionObjectA = new CollisionObject(new GeometricObject(shapeA, poseA)); var meshB = new BoxShape(0.2f, 2, 1f).GetMesh(0.01f, 4); var shapeB = new TriangleMeshShape(meshB, true) { Partition = new CompressedAabbTree() }; var poseB = new Pose(new Vector3F(0.1f, 0, 0), RandomHelper.Random.NextQuaternionF()); var collisionObjectB = new CollisionObject(new GeometricObject(shapeB, poseB)); // Explicitly create a contact set. (Normally you would get the contact set // from the collision domain...) var contactSet = ContactSet.Create(collisionObjectA, collisionObjectB); // Create a C# sample which visualizes the contact set. const string Filename = "DumpedContactSet001.cs"; DumpContactSet(contactSet, Filename); GraphicsScreen.DebugRenderer2D.DrawText( "Contact set dumped into the file: " + Filename, new Vector2F(300, 300), Color.Black); }
public static Contact CreateContact(CollisionObject objectA, CollisionObject objectB, Vector3F position, Vector3F normal, float penetrationDepth, bool isRayHit) { Debug.Assert(objectA != null); Debug.Assert(objectB != null); Contact contact = Contact.Create(); contact.Position = position; contact.Normal = normal; contact.PenetrationDepth = penetrationDepth; contact.IsRayHit = isRayHit; if (isRayHit) { contact.PositionALocal = objectA.GeometricObject.Pose.ToLocalPosition(position); contact.PositionBLocal = objectB.GeometricObject.Pose.ToLocalPosition(position); } else { //Vector3F halfPenetration = normal * (penetrationDepth / 2); //contact.PositionALocal = objectA.GeometricObject.Pose.ToLocalPosition(position + halfPenetration); //contact.PositionBLocal = objectB.GeometricObject.Pose.ToLocalPosition(position - halfPenetration); // ----- Optimized version: float halfPenetration = penetrationDepth / 2; Vector3F halfPenetrationVector; halfPenetrationVector.X = normal.X * halfPenetration; halfPenetrationVector.Y = normal.Y * halfPenetration; halfPenetrationVector.Z = normal.Z * halfPenetration; var poseA = objectA.GeometricObject.Pose; Vector3F diffA; diffA.X = position.X + halfPenetrationVector.X - poseA.Position.X; diffA.Y = position.Y + halfPenetrationVector.Y - poseA.Position.Y; diffA.Z = position.Z + halfPenetrationVector.Z - poseA.Position.Z; Vector3F positionALocal; positionALocal.X = poseA.Orientation.M00 * diffA.X + poseA.Orientation.M10 * diffA.Y + poseA.Orientation.M20 * diffA.Z; positionALocal.Y = poseA.Orientation.M01 * diffA.X + poseA.Orientation.M11 * diffA.Y + poseA.Orientation.M21 * diffA.Z; positionALocal.Z = poseA.Orientation.M02 * diffA.X + poseA.Orientation.M12 * diffA.Y + poseA.Orientation.M22 * diffA.Z; contact.PositionALocal = positionALocal; var poseB = objectB.GeometricObject.Pose; Vector3F diffB; diffB.X = position.X - halfPenetrationVector.X - poseB.Position.X; diffB.Y = position.Y - halfPenetrationVector.Y - poseB.Position.Y; diffB.Z = position.Z - halfPenetrationVector.Z - poseB.Position.Z; Vector3F positionBLocal; positionBLocal.X = poseB.Orientation.M00 * diffB.X + poseB.Orientation.M10 * diffB.Y + poseB.Orientation.M20 * diffB.Z; positionBLocal.Y = poseB.Orientation.M01 * diffB.X + poseB.Orientation.M11 * diffB.Y + poseB.Orientation.M21 * diffB.Z; positionBLocal.Z = poseB.Orientation.M02 * diffB.X + poseB.Orientation.M12 * diffB.Y + poseB.Orientation.M22 * diffB.Z; contact.PositionBLocal = positionBLocal; } return contact; }
public override void Update(GameTime gameTime) { if (InputService.IsPressed(MouseButtons.Left, true) || InputService.IsPressed(Buttons.RightTrigger, true, LogicalPlayerIndex.One)) { var cameraPose = GraphicsScreen.CameraNode.PoseWorld; Vector3F cameraPosition = cameraPose.Position; Vector3F cameraDirection = cameraPose.ToWorldDirection(Vector3F.Forward); // Create a ray for picking. RayShape ray = new RayShape(cameraPosition, cameraDirection, 1000); // The ray should stop at the first hit. We only want the first object. ray.StopsAtFirstHit = true; // The collision detection requires a CollisionObject. CollisionObject rayCollisionObject = new CollisionObject(new GeometricObject(ray, Pose.Identity)); // Get the first object that has contact with the ray. ContactSet contactSet = Simulation.CollisionDomain.GetContacts(rayCollisionObject).FirstOrDefault(); if (contactSet != null && contactSet.Count > 0) { // The ray has hit something. // The contact set contains all detected contacts between the ray and the rigid body. // Get the first contact in the contact set. (A ray hit usually contains exactly 1 contact.) Contact contact = contactSet[0]; var hitPosition = contact.Position; var normal = contact.Normal; if (contactSet.ObjectA == rayCollisionObject) normal = -normal; // The particle parameter arrays are circular buffers. Get the particle array index // where the next particle is created: int particleIndex = (_decals.ParticleStartIndex + _decals.NumberOfActiveParticles) % _decals.MaxNumberOfParticles; // Add 1 particle. int numberOfCreatedParticles = _decals.AddParticles(1, null); if (numberOfCreatedParticles > 0) { // We initialize the particle parameters Position, Normal and Axis manually using // the results of the collision detection: var positionParameter = _decals.Parameters.Get<Vector3F>(ParticleParameterNames.Position); positionParameter.Values[particleIndex] = hitPosition + normal * 0.01f; // We add a slight 1 cm offset to avoid z-fighting. var normalParameter = _decals.Parameters.Get<Vector3F>("Normal"); normalParameter.Values[particleIndex] = normal; var axisParameter = _decals.Parameters.Get<Vector3F>("Axis"); axisParameter.Values[particleIndex] = (normal == Vector3F.Up) ? Vector3F.Backward : Vector3F.Up; } } } // Synchronize particles <-> graphics. _particleSystemNode.Synchronize(GraphicsService); Profiler.AddValue("ParticleCount", ParticleHelper.CountNumberOfParticles(ParticleSystemService.ParticleSystems)); }
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 GjkProblemTest(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; SetCamera(new Vector3F(0, 1, 10), 0, 0); var points1a = new List<Vector3F> { new Vector3F(0.0f, 0.0f, -0.1875f), new Vector3F(0.0f, 0.0f, 0.1875f), new Vector3F(10.0f, 0.0f, -0.1875f), new Vector3F(10.0f, 0.0f, 0.1875f), new Vector3F(10.0f, 5.0f, -0.1875f), new Vector3F(10.0f, 5.0f, 0.1875f), new Vector3F(0.0f, 5.0f, -0.1875f), new Vector3F(0.0f, 5.0f, 0.1875f) }; var points1b = new List<Vector3F> { new Vector3F(0.0f, 0.0f, -0.1875f), new Vector3F(10.0f, 0.0f, -0.1875f), new Vector3F(10.0f, 5.0f, -0.1875f), new Vector3F(0.0f, 5.0f, -0.1875f), new Vector3F(0.0f, 0.0f, 0.1875f), new Vector3F(10.0f, 0.0f, 0.1875f), new Vector3F(10.0f, 5.0f, 0.1875f), new Vector3F(0.0f, 5.0f, 0.1875f) }; var matrix1 = new Matrix44F(0.0f, 1.0f, 0.0f, 208.5f, -1.0f, 0.0f, 0.0f, 10.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); _part1A = new CollisionObject(new GeometricObject(new ConvexPolyhedron(points1a), Pose.FromMatrix(matrix1))); _part1B = new CollisionObject(new GeometricObject(new ConvexPolyhedron(points1b), Pose.FromMatrix(matrix1))); var points2 = new List<Vector3F> { new Vector3F(0.0f, 0.0f, -0.375f), new Vector3F(0.0f, 0.0f, 0.375f), new Vector3F(23.0f, 0.0f, -0.375f), new Vector3F(23.0f, 0.0f, 0.375f), new Vector3F(23.0f, 10.0f, -0.375f), new Vector3F(23.0f, 10.0f, 0.375f), new Vector3F(0.0f, 10.0f, -0.375f), new Vector3F(0.0f, 10.0f, 0.375f) }; var matrix2 = new Matrix44F(0.0f, 0.0f, -1.0f, 208.125f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 5.0f, 0.0f, 0.0f, 0.0f, 1.0f); _part2 = new CollisionObject(new GeometricObject(new ConvexPolyhedron(points2), Pose.FromMatrix(matrix2))); }
// OnUpdate() is called once per frame. protected override void OnUpdate(TimeSpan deltaTime) { if (_inputService.IsPressed(MouseButtons.Middle, true) || _inputService.IsPressed(Buttons.RightShoulder, true, LogicalPlayerIndex.One)) { // The user has triggered an explosion. // The explosion is created at the position that is targeted with the cross-hair. // We can perform a ray hit-test to find the position. The ray starts at the camera // position and shoots forward (-z direction). var cameraGameObject = (CameraObject)_gameObjectService.Objects["Camera"]; var cameraNode = cameraGameObject.CameraNode; Vector3F cameraPosition = cameraNode.PoseWorld.Position; Vector3F cameraDirection = cameraNode.PoseWorld.ToWorldDirection(Vector3F.Forward); // Create a ray for hit-testing. var ray = new RayShape(cameraPosition, cameraDirection, 1000); // The ray should stop at the first hit. We only want the first object. ray.StopsAtFirstHit = true; // The collision detection requires a CollisionObject. var rayCollisionObject = new CollisionObject(new GeometricObject(ray, Pose.Identity)) { // In SampleGame.ResetPhysicsSimulation() a collision filter was set: // CollisionGroup = 0 ... objects that support hit-testing // CollisionGroup = 1 ... objects that are ignored during hit-testing // CollisionGroup = 2 ... objects (rays) for hit-testing CollisionGroup = 2, }; // Get the first object that has contact with the ray. ContactSet contactSet = _simulation.CollisionDomain.GetContacts(rayCollisionObject).FirstOrDefault(); if (contactSet != null && contactSet.Count > 0) { // The ray has hit something. // The contact set contains all detected contacts between the ray and another object. // Get the first contact in the contact set. (A ray hit usually contains exactly 1 contact.) Contact contact = contactSet[0]; // Create an explosion at the hit position. var explosion = new Explosion { Position = contact.Position }; _simulation.ForceEffects.Add(explosion); // Note: The Explosion force effect removes itself automatically from the simulation once // it has finished. } } }
public ContinuousCollisionDetectionSample(Microsoft.Xna.Framework.Game game) : base(game) { GraphicsScreen.ClearBackground = true; SetCamera(new Vector3F(0, 1, 10), 0, 0); // ----- Initialize collision detection and create objects. // Create a geometric object with a capsule shape. // Position it on the top with an arbitrary rotation. _startPoseA = new Pose(new Vector3F(0, 2, 0), Matrix33F.CreateRotationZ(0.1f)); var geometricObjectA = new GeometricObject(new CapsuleShape(0.2f, 1), _startPoseA); _collisionObjectA = new CollisionObject(geometricObjectA); // Object A moves to the bottom of the screen. _targetPoseA = new Pose(new Vector3F(0, -2, 0), Matrix33F.CreateRotationZ(0.63f)); // Create a geometric object with a composite shape. // Position it on the left with an arbitrary rotation. _startPoseB = new Pose(new Vector3F(-3, -1, 0), Matrix33F.CreateRotationZ(0.2f)); var composite = new CompositeShape(); composite.Children.Add(new GeometricObject(new BoxShape(0.1f, 1, 0.1f), new Pose(new Vector3F(-0.75f, 0.5f, -0.5f)))); composite.Children.Add(new GeometricObject(new BoxShape(0.1f, 1, 0.1f), new Pose(new Vector3F(0.75f, 0.5f, -0.5f)))); composite.Children.Add(new GeometricObject(new BoxShape(0.1f, 1, 0.1f), new Pose(new Vector3F(-0.75f, 0.5f, 0.5f)))); composite.Children.Add(new GeometricObject(new BoxShape(0.1f, 1, 0.1f), new Pose(new Vector3F(0.75f, 0.5f, 0.5f)))); composite.Children.Add(new GeometricObject(new BoxShape(1.8f, 0.1f, 1.1f), new Pose(new Vector3F(0, 1f, 0)))); var geometricObjectB = new GeometricObject(composite, _startPoseB); // Object B moves to the left of the screen. _targetPoseB = new Pose(new Vector3F(3, -1, 0), Matrix33F.CreateRotationZ(0.3f)); // 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); // Create a collision detection. // (The CollisionDetection stores general parameters and it can be used to perform // closest-point and contact queries.) _collisionDetection = new CollisionDetection(); }
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. }
/// <summary> /// Allows the user to drag a rigid body by using touch. /// </summary> private void DragBodies() { // Here is how it works: // We first make a hit-test using a ray to check whether the user touches a rigid body. // If there is a hit we create a spring (using a ball-socket joint) and connect the rigid // body to the touch location. Every time the user moves her finger we update the position // of the spring and the spring pulls the rigid body towards the finger. // We use raw touch points to select and drag a rigid body. TouchCollection touches = InputService.TouchCollection; if (touches.Count == 0) { // No touches detected. if (_spring != null) { // There is an active spring, so the user is currently dragging a rigid body. // Release the body by removing the spring. _spring.Simulation.Constraints.Remove(_spring); _spring = null; } } else { // Touch detected. TouchLocation touchLocation = touches[0]; // Convert the touch location from screen coordinates to world coordinates. var cameraNode = GraphicsScreen.CameraNode; Vector3 pScreen = new Vector3(touchLocation.Position.X, touchLocation.Position.Y, 0); Vector3 pWorld = GraphicsService.GraphicsDevice.Viewport.Unproject( pScreen, cameraNode.Camera.Projection, (Matrix)cameraNode.View, Matrix.Identity); // pWorld is point on the near clip plane of the camera. // Set the origin and direction of the ray for hit-testing. Vector3F rayOrigin = _cameraPosition; Vector3F rayDirection = ((Vector3F)pWorld - _cameraPosition).Normalized; if (touchLocation.State == TouchLocationState.Pressed) { // Let's create a ray and see if we hit a rigid body. // (Create the ray shape and the required collision object only once.) if (_rayShape == null) { _rayShape = new RayShape { StopsAtFirstHit = true }; _rayCollisionObject = new CollisionObject(new GeometricObject(_rayShape)); } // Set the origin and direction of the ray. _rayShape.Origin = rayOrigin; _rayShape.Direction = rayDirection.Normalized; // Make a hit test using the collision detection and get the first contact found. var contactSet = Simulation.CollisionDomain .GetContacts(_rayCollisionObject) .FirstOrDefault(); if (contactSet != null && contactSet.Count > 0) { // Get the point where the ray hits the rigid body. Contact contact = contactSet[0]; // The contact sets contains two objects ("ObjectA" and "ObjectB"). // One is the ray the other is the object that was hit by the ray. var hitCollisionObject = (contactSet.ObjectA == _rayCollisionObject) ? contactSet.ObjectB : contactSet.ObjectA; // Check whether the object is a dynamic rigid body. var hitBody = hitCollisionObject.GeometricObject as RigidBody; if (hitBody != null && hitBody.MotionType == MotionType.Dynamic) { // Remove the old joint, in case a rigid body is already grabbed. if (_spring != null && _spring.Simulation != null) _spring.Simulation.Constraints.Remove(_spring); // The penetration depth tells us the distance from the ray origin to the rigid body // in view direction. _springAnchorDistanceFromCamera = contact.PenetrationDepth; // Get the position where the ray hits the other object. // (The position is defined in the local space of the object.) Vector3F hitPositionLocal = (contactSet.ObjectA == _rayCollisionObject) ? contact.PositionBLocal : contact.PositionALocal; // Attach the rigid body at the touch location using a ball-socket joint. // (Note: We could also use a FixedJoint, if we don't want any rotations.) _spring = new BallJoint { BodyA = hitBody, AnchorPositionALocal = hitPositionLocal, // We need to attach the grabbed object to a second body. In this case we just want to // anchor the object at a specific point in the world. To achieve this we can use the // special rigid body "World", which is defined in the simulation. BodyB = Simulation.World, AnchorPositionBLocal = rayOrigin + rayDirection * _springAnchorDistanceFromCamera, // Some constraint adjustments. ErrorReduction = 0.3f, // We set a softness > 0. This makes the joint "soft" and it will act like // damped spring. Softness = 0.00001f, // We limit the maximal force. This reduces the ability of this joint to violate // other constraints. MaxForce = 1e6f }; // Add the spring to the simulation. Simulation.Constraints.Add(_spring); } } } else if (touchLocation.State == TouchLocationState.Moved) { if (_spring != null) { // User has grabbed something. // Update the position of the object by updating the anchor position of the ball-socket // joint. _spring.AnchorPositionBLocal = rayOrigin + rayDirection * _springAnchorDistanceFromCamera; // Reduce the angular velocity by a certain factor. (This acts like a damping because we // do not want the object to rotate like crazy.) _spring.BodyA.AngularVelocity *= 0.9f; } } } }
//-------------------------------------------------------------- #region Creation & Cleanup //-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="ConstraintWheel"/> class. /// </summary> public ConstraintWheel() { _radius = 0.4f; _suspensionRestLength = 0.6f; MinSuspensionLength = float.NegativeInfinity; SuspensionLength = SuspensionRestLength; SuspensionStiffness = 100; SuspensionDamping = 10; MaxSuspensionForce = float.PositiveInfinity; RollingFrictionForce = 500; Friction = 1.1f; RollReduction = 0.3f; Vector3F rayOrigin = Vector3F.Zero; Vector3F rayDirection = -Vector3F.UnitY; float rayLength = Radius + SuspensionRestLength; _ray = new RayShape(rayOrigin, rayDirection, rayLength) { StopsAtFirstHit = true, }; CollisionObject = new CollisionObject(this); Constraint = new WheelConstraint(this); }
public void ComputeCapsuleWithRandomPoints() { float height; float radius; Pose pose; const int numberOfTests = 100; RandomHelper.Random = new Random(377); for (int test = 0; test < numberOfTests; test++) { // Fill list with a random number of random points. int numberOfPoints = RandomHelper.Random.NextInteger(2, 100); List<Vector3F> points = new List<Vector3F>(); for (int i = 0; i < numberOfPoints; i++) points.Add(RandomHelper.Random.NextVector3F(-10, 100)); GeometryHelper.ComputeBoundingCapsule(points, out radius, out height, out pose); // Check if sphere can be valid. Assert.IsTrue(radius >= 0); Assert.IsTrue(height >= 2 * radius); Assert.IsTrue(!float.IsNaN(pose.Position.Length)); Assert.IsTrue(pose.Orientation.IsRotation); // Test if all points are in the shape. var cd = new CollisionDetection(); GeometricObject geometry = new GeometricObject(new CapsuleShape(radius, height), pose); CollisionObject boundingObject = new CollisionObject(geometry); // Test if all points are in the bounding shape. for (int i = 0; i < numberOfPoints; i++) { var point = points[i]; // Test against a sphere around the point. Some points are exactly on the surface // and are very sensitive to tiny numerical errors. var pointGeometry = new GeometricObject(new SphereShape(Numeric.EpsilonF * (height + 1)), new Pose(point)); Assert.IsTrue(cd.HaveContact(new CollisionObject(pointGeometry), boundingObject)); } } }
private void CreateObjects() { // Create two collision objects with triangle mesh shapes. var meshA = new TriangleMesh(); meshA.Add(new Triangle(new Vector3F(0, 1, 0), new Vector3F(0, 1, 0), new Vector3F(0, 1, 0))); meshA.Add(new Triangle(new Vector3F(0, 1, 0), new Vector3F(0, 1, 0), new Vector3F(0, 1, 0))); var shapeA = new TriangleMeshShape() { Partition = new CompressedAabbTree() }; var poseA = new Pose(new Vector3F(-1, 0, 0)); _objectA = new CollisionObject(new GeometricObject(shapeA, poseA)); var meshB = new BoxShape(0.2f, 2, 1f).GetMesh(0.05f, 4); var shapeB = new TriangleMeshShape(meshB, true) { Partition = new CompressedAabbTree() }; var poseB = new Pose(new Vector3F(0.1f, 0, 0)); _objectB = new CollisionObject(new GeometricObject(shapeB, poseB)); }
// 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; }
/// <summary> /// Initializes a new instance of the <see cref="RigidBody"/> class. /// </summary> /// <param name="shape"> /// The shape. Can be <see langword="null"/> to use the default <see cref="Shape"/>. /// </param> /// <param name="massFrame"> /// The mass frame. Can be <see langword="null"/> in which case the mass properties for a /// density of 1000 are used. /// </param> /// <param name="material"> /// The material. Can be <see langword="null"/> to use the default <see cref="Material"/>. /// </param> public RigidBody(Shape shape, MassFrame massFrame, IMaterial material) { AutoUpdateMass = true; IslandId = -1; Name = "Unnamed"; _pose = Pose.Identity; _shape = shape ?? new BoxShape(1, 1, 1); _shape.Changed += OnShapeChanged; _scale = Vector3F.One; Material = material ?? new UniformMaterial(); if (massFrame != null) MassFrame = massFrame; else UpdateMassFrame(); CollisionResponseEnabled = true; MotionType = MotionType.Dynamic; CollisionObject = new CollisionObject(this); CanSleep = true; IsSleeping = false; TimeOfImpact = 1; }
private static void Append(StringBuilder text, CollisionObject co, bool isFirstCollisionObject) { text.Append(@" { var mesh = new TriangleMesh();"); var shape = co.GeometricObject.Shape as TriangleMeshShape; if (shape == null) throw new NotSupportedException("The shapes must be TriangleMeshShapes"); var mesh = shape.Mesh; for (int i = 0; i < mesh.NumberOfTriangles; i++) { text.Append(@" mesh.Add("); Append(text, mesh.GetTriangle(i)); text.Append(");"); } text.Append(@" var pose = new Pose("); Append(text, co.GeometricObject.Pose.Position); text.Append(", "); Append(text, QuaternionF.CreateRotation(co.GeometricObject.Pose.Orientation)); text.Append(@"); var scale = "); Append(text, co.GeometricObject.Scale); text.Append(@"; var shape = new TriangleMeshShape(mesh) { Partition = new CompressedAabbTree() }; shape.IsTwoSided = "); Append(text, shape.IsTwoSided); text.Append(@"; shape.EnableContactWelding = "); Append(text, shape.EnableContactWelding); text.Append(@"; "); if (isFirstCollisionObject) text.Append("_objectA"); else text.Append("_objectB"); text.Append(@" = new CollisionObject(new GeometricObject(shape, scale, pose)); }"); }
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 BuoyancySample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // ----- Buoyancy Force Effect // Buoyancy is a force effect that lets bodies swim in water. The water area is // defined by two properties: // - Buoyancy.AreaOfEffect defines which objects are affected. // - Buoyancy.Surface defines the water level within this area. // The area of effect can be defined in different ways. In this sample we will use // a geometric object ("trigger volume"). // First, define the shape of the water area. We will create simple pool. Shape poolShape = new BoxShape(16, 10, 16); Vector3F poolCenter = new Vector3F(0, -5, 0); // Then create a geometric object for the water area. (A GeometricObject is required // to position the shape in the world. A GeometricObject stores shape, scale, position, // orientation, ...) GeometricObject waterGeometry = new GeometricObject(poolShape, new Pose(poolCenter)); // Then create a collision object for the geometric object. (A CollisionObject required // because the geometry should be used for collision detection with other objects.) _waterCollisionObject = new CollisionObject(waterGeometry) { // Assign the object to a different collision group: // The Grab component (see Grab.cs) uses a ray to perform hit tests. We don't want the ray // to collide with the water. Therefore, we need to assign the water collision object to a // different collision group. The general geometry is in collision group 0. The rays are in // collision group 2. Add the water to collision group 1. Collision between 0 and 2 are // enabled. Collision between 1 and 2 need to be disabled - this collision filter was set // in PhysicsGame.cs. CollisionGroup = 1, // Set the type to "Trigger". This improves the performance because the collision // detection does not need to compute detailed contact information. The collision // detection only returns whether an objects has contact with the water. Type = CollisionObjectType.Trigger, }; // The collision object needs to be added into the collision domain of the simulation. Simulation.CollisionDomain.CollisionObjects.Add(_waterCollisionObject); // Now we can add the buoyancy effect. Buoyancy buoyancy = new Buoyancy { AreaOfEffect = new GeometricAreaOfEffect(_waterCollisionObject), Surface = new Plane(Vector3F.Up, 0), Density = 1000f, // The density of water (1000 kg/m³). AngularDrag = 0.4f, LinearDrag = 4f, // Optional: Let the objects drift in the water by setting a flow velocity. //Velocity = new Vector3F(-0.5f, 0, 0.5f), }; Simulation.ForceEffects.Add(buoyancy); // Add static area around the pool. RigidBody bottom = new RigidBody(new BoxShape(36, 2, 36)) { MotionType = MotionType.Static, Pose = new Pose(new Vector3F(0, -11, 0)), }; Simulation.RigidBodies.Add(bottom); RigidBody left = new RigidBody(new BoxShape(10, 10, 36)) { MotionType = MotionType.Static, Pose = new Pose(new Vector3F(-13, -5, 0)), }; Simulation.RigidBodies.Add(left); RigidBody right = new RigidBody(new BoxShape(10, 10, 36)) { MotionType = MotionType.Static, Pose = new Pose(new Vector3F(13, -5, 0)), }; Simulation.RigidBodies.Add(right); RigidBody front = new RigidBody(new BoxShape(16, 10, 10)) { MotionType = MotionType.Static, Pose = new Pose(new Vector3F(0, -5, 13)), }; Simulation.RigidBodies.Add(front); RigidBody back = new RigidBody(new BoxShape(16, 10, 10)) { MotionType = MotionType.Static, Pose = new Pose(new Vector3F(0, -5, -13)), }; Simulation.RigidBodies.Add(back); // ----- Add some random objects to test the effect. // Note: Objects swim if their density is less than the density of water. They sink // if the density is greater than the density of water. // We can define the density of objects by explicitly setting the mass. // Add a swimming board. BoxShape raftShape = new BoxShape(4, 0.3f, 4); MassFrame raftMass = MassFrame.FromShapeAndDensity(raftShape, Vector3F.One, 700, 0.01f, 3); RigidBody raft = new RigidBody(raftShape, raftMass, null) { Pose = new Pose(new Vector3F(0, 4, 0)), }; Simulation.RigidBodies.Add(raft); // Add some boxes on top of the swimming board. BoxShape boxShape = new BoxShape(1, 1, 1); MassFrame boxMass = MassFrame.FromShapeAndDensity(boxShape, Vector3F.One, 700, 0.01f, 3); for (int i = 0; i < 5; i++) { RigidBody box = new RigidBody(boxShape, boxMass, null) { Pose = new Pose(new Vector3F(0, 5 + i * 1.1f, 0)), }; Simulation.RigidBodies.Add(box); } // Add some "heavy stones" represented as spheres. SphereShape stoneShape = new SphereShape(0.5f); MassFrame stoneMass = MassFrame.FromShapeAndDensity(stoneShape, Vector3F.One, 2500, 0.01f, 3); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-9, 9); position.Y = 5; RigidBody stone = new RigidBody(stoneShape, stoneMass, null) { Pose = new Pose(position), }; Simulation.RigidBodies.Add(stone); } // Add some very light objects. CylinderShape cylinderShape = new CylinderShape(0.3f, 1); MassFrame cylinderMass = MassFrame.FromShapeAndDensity(cylinderShape, Vector3F.One, 500, 0.01f, 3); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-9, 9); position.Y = 5; QuaternionF orientation = RandomHelper.Random.NextQuaternionF(); RigidBody cylinder = new RigidBody(cylinderShape, cylinderMass, null) { Pose = new Pose(position, orientation), }; Simulation.RigidBodies.Add(cylinder); } }
protected override void OnHandleInput(InputContext context) { if (_cameraObject.CameraNode == null) return; // The input context contains the mouse position that is used by the UI controls of this // screen. The mouse position is stored in the following properties: // - context.ScreenMousePosition // - context.ScreenMousePositionDelta // - context.MousePosition // - context.MousePositionDelta // // Currently, these properties contain the mouse position relative to the game window. // But the mouse position of the in-game screen is determined by the reticle of the // game camera. We need to make a ray-cast to see which part of the screen is hit and // override the properties. bool screenHit = false; // Get the camera position and the view direction in world space. Vector3F cameraPosition = _cameraObject.CameraNode.PoseWorld.Position; Vector3F cameraDirection = _cameraObject.CameraNode.PoseWorld.ToWorldDirection(Vector3F.Forward); // Create a ray (ideally this shape should be cached and reused). var ray = new RayShape(cameraPosition, cameraDirection, 1000); // We are only interested in the first object that is hit by the ray. ray.StopsAtFirstHit = true; // Create a collision object for this shape. var rayCollisionObject = new CollisionObject(new GeometricObject(ray, Pose.Identity)); // Use the CollisionDomain of the physics simulation to perform a ray cast. ContactSet contactSet = _simulation.CollisionDomain.GetContacts(rayCollisionObject).FirstOrDefault(); if (contactSet != null && contactSet.Count > 0) { // We have hit something :-) // Get the contact information of the ray hit. Contact contact = contactSet[0]; // Get the hit object (one object in the contact set is the ray and the other object is the hit object). CollisionObject hitCollisionObject = (contactSet.ObjectA == rayCollisionObject) ? contactSet.ObjectB : contactSet.ObjectA; RigidBody hitBody = hitCollisionObject.GeometricObject as RigidBody; if (hitBody != null && hitBody.UserData is string && (string)hitBody.UserData == "TV") { // We have hit a dynamic rigid body of a TV object. // Get the normal vector of the contact. var normal = (contactSet.ObjectA == rayCollisionObject) ? -contact.Normal : contact.Normal; // Convert the normal vector to the local space of the TV box. normal = hitBody.Pose.ToLocalDirection(normal); // The InGameUIScreen texture is only mapped onto the -Y sides of the boxes. If the user // looks onto another side, he cannot interact with the game screen. if (normal.Y < 0.5f) { // The user looks onto the TV's front side. Now, we have to map the ray hit position // to the texture coordinate of the InGameUIScreen render target/texture. var localHitPosition = (contactSet.ObjectA == rayCollisionObject) ? contact.PositionBLocal : contact.PositionALocal; var normalizedPosition = GetTextureCoordinate(localHitPosition); // The texture coordinate is in the range [0, 0] to [1, 1]. If we multiply it with the // screen extent to the position in pixels. var inGameScreenMousePosition = normalizedPosition * new Vector2F(ActualWidth, ActualHeight); var inGameScreenMousePositionDelta = inGameScreenMousePosition - _lastMousePosition; // Finally, we can set the mouse positions that are relative to the InGame screen. Hurray! context.ScreenMousePosition = inGameScreenMousePosition; context.ScreenMousePositionDelta = inGameScreenMousePositionDelta; context.MousePosition = inGameScreenMousePosition; context.MousePositionDelta = inGameScreenMousePositionDelta; // Store the mouse position so that we can compute MousePositionDelta in the next frame. _lastMousePosition = context.MousePosition; screenHit = true; } } } if (screenHit) { // Call base class to call HandleInput for all child controls. The child controls will // use the overridden mouse positions. base.OnHandleInput(context); } }
/// <summary> /// Computes the closest points between two <see cref="CollisionObject"/>s. /// </summary> /// <param name="objectA">The first collision object.</param> /// <param name="objectB">The second collision object.</param> /// <returns> /// The <see cref="ContactSet"/> with the closest-point information. The /// <see cref="ContactSet"/> will have exactly 1 <see cref="Contact"/> (describing the /// closest-point pair). /// </returns> /// <remarks> /// <para> /// Collision filtering (see <see cref="CollisionFilter"/>) is NOT applied. /// </para> /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="objectA"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="objectB"/> is <see langword="null"/>. /// </exception> public ContactSet GetClosestPoints(CollisionObject objectA, CollisionObject objectB) { if (objectA == null) throw new ArgumentNullException("objectA"); if (objectB == null) throw new ArgumentNullException("objectB"); return AlgorithmMatrix[objectA, objectB].GetClosestPoints(objectA, objectB); }
// 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); }
/// <summary> /// Computes the contacts between two <see cref="CollisionObject"/>s. /// </summary> /// <param name="objectA">The first collision object.</param> /// <param name="objectB">The second collision object.</param> /// <returns> /// A <see cref="ContactSet"/> describing the contact information if <paramref name="objectA"/> /// and <paramref name="objectB"/> are intersecting; otherwise, <see langword="null"/> if the /// objects are separated. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="objectA"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="objectB"/> is <see langword="null"/>. /// </exception> public ContactSet GetContacts(CollisionObject objectA, CollisionObject objectB) { // Broad phase AABB check and collision filtering if (HaveAabbContact(objectA, objectB) == false) return null; // Narrow phase ContactSet contactSet = AlgorithmMatrix[objectA, objectB].GetContacts(objectA, objectB); Debug.Assert(contactSet != null, "CollisionAlgorithm.GetContacts should always return a ContactSet."); if (contactSet.HaveContact) { return contactSet; } else { contactSet.Recycle(); return null; } }
public void ComputeBoundingShapeCenteredSphere() { RandomHelper.Random = new Random(123); var radius = new Vector3F(3, 0, 0); var points = new List<Vector3F> { new Vector3F(3, 0, 0), new Vector3F(-3, 0, 0), new Vector3F(0, 3, 0), new Vector3F(0, -3, 0), new Vector3F(0, 0, 3), new Vector3F(0, 0, -3), }; for (int i = 0; i < 40; i++) points.Add(RandomHelper.Random.NextQuaternionF().Rotate(radius)); var shape = GeometryHelper.CreateBoundingShape(points); SphereShape s = (SphereShape)shape; Assert.IsTrue(Numeric.AreEqual(3, s.Radius)); var cd = new CollisionDetection(); GeometricObject geometry = new GeometricObject(shape); CollisionObject boundingObject = new CollisionObject(geometry); // Test if all points are in the bounding shape. for (int i = 0; i < points.Count; i++) { var point = points[i]; // Test against a sphere around the point. Some points are exactly on the surface // and are very sensitive to tiny numerical errors. var pointGeometry = new GeometricObject(new SphereShape(Numeric.EpsilonF * 10), new Pose(point)); Assert.IsTrue(cd.HaveContact(new CollisionObject(pointGeometry), boundingObject)); } }
/// <summary> /// Gets the time of impact between two moving objects. /// </summary> /// <param name="objectA">The object A.</param> /// <param name="targetPoseA">The target pose of A.</param> /// <param name="objectB">The object B.</param> /// <param name="targetPoseB">The target pose of B.</param> /// <param name="allowedPenetration"> /// The allowed penetration. A positive allowed penetration value makes sure that the objects /// have a measurable contact at the time of impact. /// </param> /// <returns>The time of impact in the range [0, 1].</returns> /// <remarks> /// <para> /// Both objects are moved from their current pose (time = 0) to the given target pose (time = /// 1). If they collide during this movement the first time of impact is returned. A time of /// impact of 1 can mean that the objects do not collide or they collide at their target /// positions. /// </para> /// <para> /// The result is undefined if the objects are already in contact at their start poses. /// </para> /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="objectA"/> or <paramref name="objectB"/> is <see langword="null"/>. /// </exception> public float GetTimeOfImpact(CollisionObject objectA, Pose targetPoseA, CollisionObject objectB, Pose targetPoseB, float allowedPenetration) { if (objectA == null) throw new ArgumentNullException("objectA"); if (objectB == null) throw new ArgumentNullException("objectB"); return AlgorithmMatrix[objectA, objectB].GetTimeOfImpact(objectA, targetPoseA, objectB, targetPoseB, allowedPenetration); }
public void CreateBoundingShape() { int numberOfBoxes = 0; int numberOfSpheres = 0; const int numberOfTests = 100; RandomHelper.Random = new Random(727); for (int test = 0; test < numberOfTests; test++) { // Fill list with a random number of random points. int numberOfPoints = RandomHelper.Random.NextInteger(2, 100); List<Vector3F> points = new List<Vector3F>(); for (int i = 0; i < numberOfPoints; i++) points.Add(RandomHelper.Random.NextVector3F(-10, 100)); Shape shape = GeometryHelper.CreateBoundingShape(points); GeometricObject geometry = new GeometricObject(shape); CollisionObject boundingObject = new CollisionObject(geometry); if (shape is BoxShape) numberOfBoxes++; if (shape is SphereShape) numberOfSpheres++; if (((TransformedShape)shape).Child.Shape is BoxShape) numberOfBoxes++; else numberOfSpheres++; Assert.IsNotNull(shape); var cd = new CollisionDetection(); // Test if all points are in the bounding shape. for (int i = 0; i < numberOfPoints; i++) { var point = points[i]; // Test against a sphere around the point. Some points are exactly on the surface // and are very sensitive to tiny numerical errors. var pointGeometry = new GeometricObject(new SphereShape(Numeric.EpsilonF * 10), new Pose(point)); Assert.IsTrue(cd.HaveContact(new CollisionObject(pointGeometry), boundingObject)); } } Console.WriteLine("ShapeHelper.CreateBoundingShape: Number of Boxes : Number of Spheres = " + numberOfBoxes + " : " + numberOfSpheres); }
public bool HaveAabbContact(CollisionObject objectA, CollisionObject objectB) { if (objectA == null) throw new ArgumentNullException("objectA"); if (objectB == null) throw new ArgumentNullException("objectB"); // Collision filtering if (objectA.Enabled == false || objectB.Enabled == false || (CollisionFilter != null && !CollisionFilter.Filter(new Pair<CollisionObject>(objectA, objectB)))) { return false; } // AABB test return GeometryHelper.HaveContact(objectA.GeometricObject.Aabb, objectB.GeometricObject.Aabb); }
public CollisionAlgorithm this[CollisionObject objectA, CollisionObject objectB] { get { if (objectA == null) throw new ArgumentNullException("objectA"); if (objectB == null) throw new ArgumentNullException("objectB"); Debug.Assert(objectA.GeometricObject != null, "CollisionObject needs to ensure that GeometricObject is not null."); Debug.Assert(objectB.GeometricObject != null, "CollisionObject needs to ensure that GeometricObject is not null."); Debug.Assert(objectA.GeometricObject.Shape != null, "IGeometricObject needs to ensure that Shape is not null."); Debug.Assert(objectB.GeometricObject.Shape != null, "IGeometricObject needs to ensure that Shape is not null."); return this[objectA.GeometricObject.Shape.GetType(), objectB.GeometricObject.Shape.GetType()]; } set { if (objectA == null) throw new ArgumentNullException("objectA"); if (objectB == null) throw new ArgumentNullException("objectB"); Debug.Assert(objectA.GeometricObject != null, "CollisionObject needs to ensure that GeometricObject is not null."); Debug.Assert(objectB.GeometricObject != null, "CollisionObject needs to ensure that GeometricObject is not null."); Debug.Assert(objectA.GeometricObject.Shape != null, "IGeometricObject needs to ensure that Shape is not null."); Debug.Assert(objectB.GeometricObject.Shape != null, "IGeometricObject needs to ensure that Shape is not null."); this[objectA.GeometricObject.Shape.GetType(), objectB.GeometricObject.Shape.GetType()] = value; } }
/// <summary> /// Returns <see langword="true"/> if two <see cref="CollisionObject"/>s are in contact. /// </summary> /// <param name="objectA">The first collision object.</param> /// <param name="objectB">The second collision object.</param> /// <returns> /// <see langword="true"/> if the object are touching or intersecting; otherwise /// <see langword="false"/>. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="objectA"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="objectB"/> is <see langword="null"/>. /// </exception> public bool HaveContact(CollisionObject objectA, CollisionObject objectB) { // Broad phase AABB check and collision filtering if (HaveAabbContact(objectA, objectB) == false) return false; // Narrow phase return AlgorithmMatrix[objectA, objectB].HaveContact(objectA, objectB); }