//-------------------------------------------------------------- /// <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); }
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)); }
/// <summary> /// Initializes a new instance of <see cref="Ray"/> from a <see cref="RayShape"/>. /// </summary> /// <param name="rayShape"> /// The <see cref="RayShape"/> from which origin and direction are copied. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="rayShape"/> is <see langword="null"/>. /// </exception> public Ray(RayShape rayShape) { if (rayShape == null) { throw new ArgumentNullException("rayShape"); } Origin = rayShape.Origin; Direction = rayShape.Direction; Length = rayShape.Length; }
// 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. } } }
// Creates a lot of random objects. private void CreateRandomObjects() { var random = new Random(); var isFirstHeightField = true; int currentShape = 0; int numberOfObjects = 0; while (true) { numberOfObjects++; if (numberOfObjects > ObjectsPerType) { currentShape++; numberOfObjects = 0; } Shape shape; switch (currentShape) { case 0: // Box shape = new BoxShape(ObjectSize, ObjectSize * 2, ObjectSize * 3); break; case 1: // Capsule shape = new CapsuleShape(0.3f * ObjectSize, 2 * ObjectSize); break; case 2: // Cone shape = new ConeShape(1 * ObjectSize, 2 * ObjectSize); break; case 3: // Cylinder shape = new CylinderShape(0.4f * ObjectSize, 2 * ObjectSize); break; case 4: // Sphere shape = new SphereShape(ObjectSize); break; case 5: // Convex hull of several points. ConvexHullOfPoints hull = new ConvexHullOfPoints(); hull.Points.Add(new Vector3F(-1 * ObjectSize, -2 * ObjectSize, -1 * ObjectSize)); hull.Points.Add(new Vector3F(2 * ObjectSize, -1 * ObjectSize, -0.5f * ObjectSize)); hull.Points.Add(new Vector3F(1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize)); hull.Points.Add(new Vector3F(-1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize)); hull.Points.Add(new Vector3F(-1 * ObjectSize, 0.7f * ObjectSize, -0.6f * ObjectSize)); shape = hull; break; case 6: // A composite shape: two boxes that form a "T" shape. var composite = new CompositeShape(); composite.Children.Add( new GeometricObject( new BoxShape(ObjectSize, 3 * ObjectSize, ObjectSize), new Pose(new Vector3F(0, 0, 0)))); composite.Children.Add( new GeometricObject( new BoxShape(2 * ObjectSize, ObjectSize, ObjectSize), new Pose(new Vector3F(0, 2 * ObjectSize, 0)))); shape = composite; break; case 7: shape = new CircleShape(ObjectSize); break; case 8: { var compBvh = new CompositeShape(); compBvh.Children.Add(new GeometricObject(new BoxShape(0.5f, 1, 0.5f), new Pose(new Vector3F(0, 0.5f, 0), Matrix33F.Identity))); compBvh.Children.Add(new GeometricObject(new BoxShape(0.8f, 0.5f, 0.5f), new Pose(new Vector3F(0.5f, 0.7f, 0), Matrix33F.CreateRotationZ(-MathHelper.ToRadians(15))))); compBvh.Children.Add(new GeometricObject(new SphereShape(0.3f), new Pose(new Vector3F(0, 1.15f, 0), Matrix33F.Identity))); compBvh.Children.Add(new GeometricObject(new CapsuleShape(0.2f, 1), new Pose(new Vector3F(0.6f, 1.15f, 0), Matrix33F.CreateRotationX(0.3f)))); compBvh.Partition = new AabbTree<int>(); shape = compBvh; break; } case 9: CompositeShape comp = new CompositeShape(); comp.Children.Add(new GeometricObject(new BoxShape(0.5f * ObjectSize, 1 * ObjectSize, 0.5f * ObjectSize), new Pose(new Vector3F(0, 0.5f * ObjectSize, 0), QuaternionF.Identity))); comp.Children.Add(new GeometricObject(new BoxShape(0.8f * ObjectSize, 0.5f * ObjectSize, 0.5f * ObjectSize), new Pose(new Vector3F(0.3f * ObjectSize, 0.7f * ObjectSize, 0), QuaternionF.CreateRotationZ(-MathHelper.ToRadians(45))))); comp.Children.Add(new GeometricObject(new SphereShape(0.3f * ObjectSize), new Pose(new Vector3F(0, 1.15f * ObjectSize, 0), QuaternionF.Identity))); shape = comp; break; case 10: shape = new ConvexHullOfPoints(new[] { new Vector3F(-1 * ObjectSize, -2 * ObjectSize, -1 * ObjectSize), new Vector3F(2 * ObjectSize, -1 * ObjectSize, -0.5f * ObjectSize), new Vector3F(1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize), new Vector3F(-1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize), new Vector3F(-1 * ObjectSize, 0.7f * ObjectSize, -0.6f * ObjectSize) }); break; case 11: ConvexHullOfShapes shapeHull = new ConvexHullOfShapes(); shapeHull.Children.Add(new GeometricObject(new SphereShape(0.3f * ObjectSize), new Pose(new Vector3F(0, 2 * ObjectSize, 0), Matrix33F.Identity))); shapeHull.Children.Add(new GeometricObject(new BoxShape(1 * ObjectSize, 2 * ObjectSize, 3 * ObjectSize), Pose.Identity)); shape = shapeHull; break; case 12: shape = Shape.Empty; break; case 13: var numberOfSamplesX = 10; var numberOfSamplesZ = 10; var samples = new float[numberOfSamplesX * numberOfSamplesZ]; for (int z = 0; z < numberOfSamplesZ; z++) for (int x = 0; x < numberOfSamplesX; x++) samples[z * numberOfSamplesX + x] = (float)(Math.Cos(z / 3f) * Math.Sin(x / 2f) * BoxSize / 6); HeightField heightField = new HeightField(0, 0, 2 * BoxSize, 2 * BoxSize, samples, numberOfSamplesX, numberOfSamplesZ); shape = heightField; break; //case 14: //shape = new LineShape(new Vector3F(0.1f, 0.2f, 0.3f), new Vector3F(0.1f, 0.2f, -0.3f).Normalized); //break; case 15: shape = new LineSegmentShape( new Vector3F(0.1f, 0.2f, 0.3f), new Vector3F(0.1f, 0.2f, 0.3f) + 3 * ObjectSize * new Vector3F(0.1f, 0.2f, -0.3f)); break; case 16: shape = new MinkowskiDifferenceShape { ObjectA = new GeometricObject(new SphereShape(0.1f * ObjectSize)), ObjectB = new GeometricObject(new BoxShape(1 * ObjectSize, 2 * ObjectSize, 3 * ObjectSize)) }; break; case 17: shape = new MinkowskiSumShape { ObjectA = new GeometricObject(new SphereShape(0.1f * ObjectSize)), ObjectB = new GeometricObject(new BoxShape(1 * ObjectSize, 2 * ObjectSize, 3 * ObjectSize)), }; break; case 18: shape = new OrthographicViewVolume(0, ObjectSize, 0, ObjectSize, ObjectSize / 2, ObjectSize * 2); break; case 19: shape = new PerspectiveViewVolume(MathHelper.ToRadians(60f), 16f / 10, ObjectSize / 2, ObjectSize * 3); break; case 20: shape = new PointShape(0.1f, 0.3f, 0.2f); break; case 21: shape = new RayShape(new Vector3F(0.2f, 0, -0.12f), new Vector3F(1, 2, 3).Normalized, ObjectSize * 2); break; case 22: shape = new RayShape(new Vector3F(0.2f, 0, -0.12f), new Vector3F(1, 2, 3).Normalized, ObjectSize * 2) { StopsAtFirstHit = true }; break; case 23: shape = new RectangleShape(ObjectSize, ObjectSize * 2); break; case 24: shape = new TransformedShape( new GeometricObject( new BoxShape(1 * ObjectSize, 2 * ObjectSize, 3 * ObjectSize), new Pose(new Vector3F(0.1f, 1, -0.2f)))); break; case 25: shape = new TriangleShape( new Vector3F(ObjectSize, 0, 0), new Vector3F(0, ObjectSize, 0), new Vector3F(ObjectSize, ObjectSize, ObjectSize)); break; //case 26: // { // // Create a composite object from which we get the mesh. // CompositeShape compBvh = new CompositeShape(); // compBvh.Children.Add(new GeometricObject(new BoxShape(0.5f, 1, 0.5f), new Pose(new Vector3F(0, 0.5f, 0), Matrix33F.Identity))); // compBvh.Children.Add( // new GeometricObject( // new BoxShape(0.8f, 0.5f, 0.5f), // new Pose(new Vector3F(0.5f, 0.7f, 0), Matrix33F.CreateRotationZ(-(float)MathHelper.ToRadians(15))))); // compBvh.Children.Add(new GeometricObject(new SphereShape(0.3f), new Pose(new Vector3F(0, 1.15f, 0), Matrix33F.Identity))); // compBvh.Children.Add( // new GeometricObject(new CapsuleShape(0.2f, 1), new Pose(new Vector3F(0.6f, 1.15f, 0), Matrix33F.CreateRotationX(0.3f)))); // TriangleMeshShape meshBvhShape = new TriangleMeshShape { Mesh = compBvh.GetMesh(0.01f, 3) }; // meshBvhShape.Partition = new AabbTree<int>(); // shape = meshBvhShape; // break; // } //case 27: // { // // Create a composite object from which we get the mesh. // CompositeShape compBvh = new CompositeShape(); // compBvh.Children.Add(new GeometricObject(new BoxShape(0.5f, 1, 0.5f), new Pose(new Vector3F(0, 0.5f, 0), QuaternionF.Identity))); // compBvh.Children.Add( // new GeometricObject( // new BoxShape(0.8f, 0.5f, 0.5f), // new Pose(new Vector3F(0.5f, 0.7f, 0), QuaternionF.CreateRotationZ(-(float)MathHelper.ToRadians(15))))); // compBvh.Children.Add(new GeometricObject(new SphereShape(0.3f), new Pose(new Vector3F(0, 1.15f, 0), QuaternionF.Identity))); // compBvh.Children.Add( // new GeometricObject(new CapsuleShape(0.2f, 1), new Pose(new Vector3F(0.6f, 1.15f, 0), QuaternionF.CreateRotationX(0.3f)))); // TriangleMeshShape meshBvhShape = new TriangleMeshShape { Mesh = compBvh.GetMesh(0.01f, 3) }; // meshBvhShape.Partition = new AabbTree<int>(); // shape = meshBvhShape; // break; // } case 28: shape = new ConvexPolyhedron(new[] { new Vector3F(-1 * ObjectSize, -2 * ObjectSize, -1 * ObjectSize), new Vector3F(2 * ObjectSize, -1 * ObjectSize, -0.5f * ObjectSize), new Vector3F(1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize), new Vector3F(-1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize), new Vector3F(-1 * ObjectSize, 0.7f * ObjectSize, -0.6f * ObjectSize) }); break; case 29: return; default: currentShape++; continue; } // Create an object with the random shape, pose, color and velocity. Pose randomPose = new Pose( random.NextVector3F(-BoxSize + ObjectSize * 2, BoxSize - ObjectSize * 2), random.NextQuaternionF()); var newObject = new MovingGeometricObject { Pose = randomPose, Shape = shape, LinearVelocity = random.NextQuaternionF().Rotate(new Vector3F(MaxLinearVelocity, 0, 0)), AngularVelocity = random.NextQuaternionF().Rotate(Vector3F.Forward) * RandomHelper.Random.NextFloat(0, MaxAngularVelocity), }; if (RandomHelper.Random.NextBool()) newObject.LinearVelocity = Vector3F.Zero; if (RandomHelper.Random.NextBool()) newObject.AngularVelocity = Vector3F.Zero; if (shape is LineShape || shape is HeightField) { // Do not move lines or the height field. newObject.LinearVelocity = Vector3F.Zero; newObject.AngularVelocity = Vector3F.Zero; } // Create only 1 heightField! if (shape is HeightField) { if (isFirstHeightField) { isFirstHeightField = true; newObject.Pose = new Pose(new Vector3F(-BoxSize, -BoxSize, -BoxSize)); } else { currentShape++; numberOfObjects = 0; continue; } } // Add collision object to collision domain. _domain.CollisionObjects.Add(new CollisionObject(newObject)); //co.Type = CollisionObjectType.Trigger; //co.Name = "Object" + shape.GetType().Name + "_" + i; } }
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> /// 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; } } } }
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. }
//-------------------------------------------------------------- #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 override void Update(GameTime gameTime) { if (_spring != null && !_inputService.IsDown(MouseButtons.Left) && !_inputService.IsDown(Buttons.LeftTrigger, PlayerIndex.One)) { // The user has released the object. _spring.Simulation.Constraints.Remove(_spring); _spring = null; } if (_inputService.IsPressed(MouseButtons.Left, false) || _inputService.IsPressed(Buttons.LeftTrigger, false, PlayerIndex.One)) { // The user has pressed the grab button. // Remove the old joint, in case anything is grabbed. if (_spring != null && _spring.Simulation != null) _spring.Simulation.Constraints.Remove(_spring); // The spring is attached 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). Camera camera = Game.Components.OfType<Camera>().First(); Vector3F cameraPosition = camera.Pose.Position; Vector3F cameraDirection = camera.Pose.ToWorldDirection(-Vector3F.UnitZ); // 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)); // Assign the collision object to collision group 2. (In PhysicsGame.cs a collision filter // based on collision groups was set.) rayCollisionObject.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 the rigid body. // Get the first contact in the contact set. (A ray hit usually contains exactly 1 contact.) Contact contact = contactSet[0]; // The contact set contains the object pair of the collision. One object is the ray. // The other is the object we want to grab. CollisionObject hitCollisionObject = (contactSet.ObjectA == rayCollisionObject) ? contactSet.ObjectB : contactSet.ObjectA; // Check whether a dynamic rigid body was hit. RigidBody hitBody = hitCollisionObject.GeometricObject as RigidBody; if (hitBody != null && hitBody.MotionType == MotionType.Dynamic) { // Attach the rigid body at the cursor position using a ball-socket joint. // (Note: We could also use a FixedJoint, if we don't want any rotations.) // The penetration depth tells us the distance from the ray origin to the rigid body. _springAttachmentDistanceFromObserver = 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; _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 is set below. // 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); } } } if (_spring != null) { // User has grabbed something. // Update the position of the object by updating the anchor position of the ball-socket // joint. Camera camera = Game.Components.OfType<Camera>().First(); Vector3F cameraPosition = camera.Pose.Position; Vector3F cameraDirection = camera.Pose.ToWorldDirection(-Vector3F.UnitZ); _spring.AnchorPositionBLocal = cameraPosition + cameraDirection * _springAttachmentDistanceFromObserver; // 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; } base.Update(gameTime); }
/// <summary> /// Determines whether the specified world space position is underwater. /// </summary> /// <param name="position">The position in world space.</param> /// <returns> /// <see langword="true"/> if the position is underwater; otherwise, <see langword="false"/> /// </returns> /// <remarks> /// A position is underwater if it is inside the <see cref="Shape"/> of this node. /// </remarks> public bool IsUnderwater(Vector3F position) { //if (!EnableUnderwaterEffect) // return false; // Oceans are treated like a horizontal plane through the node origin. if (Volume == null) return position.Y < PoseWorld.Position.Y; // Thread-safety: We lock this operation because all tests use the same cache // and test objects. lock (_underwaterTestLock) { // Return cached result if the point and the water pose/shape are still the same. if (!IsDirty) { if (Vector3F.AreNumericallyEqual(position, _lastTestPosition)) return _lastTestResult; } else { // Clear flag. We will cache a new result. IsDirty = false; } _lastTestPosition = position; _lastTestResult = false; // Use a shared collision detection instance. var collisionDetection = SceneHelper.CollisionDetection; if (_sphereShape == null) { // First time initializations. _sphereShape = new SphereShape(0); _rayShape = new RayShape(); _testCollisionObject = new CollisionObject(TestGeometricObject.Create()); _waterCollisionObject = new CollisionObject(TestGeometricObject.Create()); } var testGeometricObject = (TestGeometricObject)_testCollisionObject.GeometricObject; var waterGeometricObject = (TestGeometricObject)_waterCollisionObject.GeometricObject; try { // Initialize water collision object. waterGeometricObject.Shape = Volume; waterGeometricObject.Scale = ScaleWorld; waterGeometricObject.Pose = PoseWorld; // Test if point touches underwater volume. (Skip this test for triangle mesh shapes.) if (!(Shape is TriangleMeshShape)) { testGeometricObject.Pose = new Pose(position); testGeometricObject.Shape = _sphereShape; if (collisionDetection.HaveContact(_testCollisionObject, _waterCollisionObject)) { _lastTestResult = true; return true; } // For convex shapes, the above test is sufficient. if (Shape is ConvexShape) return false; } // For triangle meshes - which are hollow - we have to make a more complex test. // We shoot vertical rays and check if we hit the underwater volume surface. // Make explicit point vs. AABB test first. if (!collisionDetection.HaveAabbContact(_testCollisionObject, _waterCollisionObject)) return false; // Switch to ray shape. testGeometricObject.Shape = _rayShape; // Shoot down. Start 1 unit above the surface. Vector3F origin = position; origin.Y = Math.Max(Aabb.Maximum.Y, origin.Y) + 1; _rayShape.Origin = origin; _rayShape.Length = (origin - position).Length; _rayShape.Direction = Vector3F.Down; if (!collisionDetection.HaveContact(_testCollisionObject, _waterCollisionObject)) return false; // Camera is above water. // Shoot up. Start 1 m under the water volume. origin = position; origin.Y = Math.Min(Aabb.Minimum.Y, origin.Y) - 1; _rayShape.Origin = origin; _rayShape.Length = (origin - position).Length; _rayShape.Direction = Vector3F.Up; _lastTestResult = collisionDetection.HaveContact(_testCollisionObject, _waterCollisionObject); return _lastTestResult; } finally { // Remove references to avoid "memory leaks". waterGeometricObject.Shape = Volume; } } }
private float _slopeLimit = ConstantsF.PiOver4; // = 45° #endregion Fields #region Constructors //-------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="KinematicCharacterController"/> class. /// </summary> /// <param name="simulation">The simulation.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="simulation" /> is <see langword="null"/>. /// </exception> public DynamicCharacterController(Simulation simulation) { if (simulation == null) throw new ArgumentNullException("simulation"); Simulation = simulation; CapsuleShape shape = new CapsuleShape(0.4f, 1.8f); MassFrame mass = new MassFrame { Mass = 80 }; // Push strength is proportional to the mass! UniformMaterial material = new UniformMaterial { // The body should be frictionless, so that it can be easily pushed by the simulation to // valid positions. And it does not slow down when sliding along walls. StaticFriction = 0.0f, DynamicFriction = 0.0f, // The body should not bounce when being hit or pushed. Restitution = 0 }; Body = new RigidBody(shape, mass, material) { // We set the mass explicitly and it should not automatically change when the // shape is changed; e.g. a ducked character has a smaller shape, but still the same mass. AutoUpdateMass = false, // This body is under our control and should never be deactivated by the simulation. CanSleep = false, CcdEnabled = true, // The capsule does not rotate in any direction. LockRotationX = true, LockRotationY = true, LockRotationZ = true, Name = "CharacterController", Pose = new Pose(new Vector3F(0, shape.Height / 2, 0)), }; // Create a ray that senses the space below the capsule. The ray starts in the capsule // center (to detect penetrations) and extends 0.4 units below the capsule bottom. RayShape rayShape = new RayShape(Vector3F.Zero, -Vector3F.UnitY, shape.Height / 2 + 0.4f) { StopsAtFirstHit = true, }; GeometricObject rayGeometry = new GeometricObject(rayShape, Body.Pose); _ray = new CollisionObject(rayGeometry); // Whenever the Body moves, the ray moves with it. Body.PoseChanged += (s, e) => rayGeometry.Pose = Body.Pose; // Enable the character controller. (Adds body to simulation.) Enabled = true; }