public void CutConvex() { var mesh = new BoxShape(1, 2, 3).GetMesh(0.01f, 1); var dcel = DcelMesh.FromTriangleMesh((ITriangleMesh)mesh); Assert.IsTrue(dcel.IsValid()); Assert.IsTrue(dcel.IsConvex()); Assert.IsTrue(dcel.IsClosed()); bool result = dcel.CutConvex(new Plane(new Vector3F(0, 0, 1), 1.5f)); Assert.IsFalse(result); result = dcel.CutConvex(new Plane(new Vector3F(0, 0, 1), -1.5f)); Assert.IsTrue(result); Assert.IsNull(dcel.Vertex); Assert.AreEqual(0, dcel.Vertices.Count); dcel = DcelMesh.FromTriangleMesh((ITriangleMesh)mesh); result = dcel.CutConvex(new Plane(new Vector3F(0, 0, 1), 1f)); Assert.IsTrue(result); Assert.IsTrue(dcel.IsValid()); Assert.IsTrue(dcel.IsConvex()); Assert.IsTrue(dcel.IsClosed()); Assert.AreEqual(12, dcel.Vertices.Count); Assert.AreEqual(6 + 8 + 7 * 4, dcel.Edges.Count); // Bottom = 6 edges, new cap = 8 edges, cut sides = each 7 edges. Assert.AreEqual(12 - 1, dcel.Faces.Count); }
private void InitializeSimulation(Simulation simulation, Vector3F offset) { // Add default force effects. simulation.ForceEffects.Add(new Gravity()); simulation.ForceEffects.Add(new Damping()); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", MotionType = MotionType.Static, }; simulation.RigidBodies.Add(groundPlane); // Add a stack of boxes. const float boxSize = 0.8f; float overlap = simulation.Settings.Constraints.AllowedPenetration * 0.5f; float yPosition = boxSize / 2 - overlap; BoxShape boxShape = new BoxShape(boxSize, boxSize, boxSize); for (int i = 0; i < 15; i++) { RigidBody stackBox = new RigidBody(boxShape) { Name = "StackBox" + i, Pose = new Pose(new Vector3F(0, yPosition, 0) + offset), }; simulation.RigidBodies.Add(stackBox); yPosition += boxSize - overlap; } }
public BoxStackSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", // Names are not required but helpful for debugging. MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(groundPlane); // ----- Add a stack of boxes. const int numberOfBoxes = 5; const float boxSize = 0.8f; BoxShape boxShape = new BoxShape(boxSize, boxSize, boxSize); // Optional: Use a small overlap between boxes to improve the stability. float overlap = Simulation.Settings.Constraints.AllowedPenetration * 0.5f; Vector3F position = new Vector3F(0, boxSize / 2 - overlap, 0); for (int i = 0; i < numberOfBoxes; i++) { RigidBody box = new RigidBody(boxShape) { Name = "Box" + i, Pose = new Pose(position), }; Simulation.RigidBodies.Add(box); position.Y += boxSize - overlap; } }
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 void BoxVolume() { var s = new BoxShape(1, 2, 3); Assert.AreEqual(1 * 2 * 3, s.GetVolume(0.0001f, 10)); var m = s.GetMesh(0.001f, 4); Assert.AreEqual(1 * 2 * 3, m.GetVolume()); }
public void GetOutcode() { BoxShape box = new BoxShape(1, 2, 3); Assert.AreEqual(0, GeometryHelper.GetOutcode(box.Extent, new Vector3F(0.1f, 0.1f, 0.1f))); Assert.AreEqual(1 | 8, GeometryHelper.GetOutcode(box.Extent, new Vector3F(-1.1f, 2.1f, 0.1f))); Assert.AreEqual(2 | 4 | 32, GeometryHelper.GetOutcode(box.Extent, new Vector3F(1.1f, -2.1f, 3.1f))); Assert.AreEqual(16, GeometryHelper.GetOutcode(box.Extent, new Vector3F(0.1f, 0.9f, -3.1f))); }
public LockRotationSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", // Names are not required but helpful for debugging. MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(groundPlane); // Next, we add boxes and capsules in random positions and orientations. // For all bodies the flags LockRotationX/Y/Z are set. This will prevent all // rotation that would be caused by forces. (It is still allowed to manually // change the rotation or to set an angular velocity.) BoxShape boxShape = new BoxShape(0.5f, 0.8f, 1.2f); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-10, 10); position.Y = 5; QuaternionF orientation = RandomHelper.Random.NextQuaternionF(); RigidBody body = new RigidBody(boxShape) { Pose = new Pose(position, orientation), LockRotationX = true, LockRotationY = true, LockRotationZ = true, }; Simulation.RigidBodies.Add(body); } CapsuleShape capsuleShape = new CapsuleShape(0.3f, 1.2f); for (int i = 0; i < 10; i++) { Vector3F randomPosition = RandomHelper.Random.NextVector3F(-10, 10); randomPosition.Y = 5; QuaternionF randomOrientation = RandomHelper.Random.NextQuaternionF(); RigidBody body = new RigidBody(capsuleShape) { Pose = new Pose(randomPosition, randomOrientation), LockRotationX = true, LockRotationY = true, LockRotationZ = true, }; Simulation.RigidBodies.Add(body); } }
public ResponseFilterSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Only disable collision response if you need collision detection info but no collision // response. If you can disable collision detection too, use // Simulation.CollisionDomain.CollisionDetection.CollisionFilter // instead - this is more efficient! // (In this sample, a custom filter implementation is used. DigitalRune.Physics provides // a standard filter implementation: DigitalRune.Physics.CollisionResponseFilter.) Simulation.ResponseFilter = new MyCollisionResponseFilter(); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", // Names are not required but helpful for debugging. MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(groundPlane); // ----- Add boxes at random poses. BoxShape boxShape = new BoxShape(0.5f, 0.8f, 1.2f); for (int i = 0; i < 20; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-3, 3); position.Y = 5; QuaternionF orientation = RandomHelper.Random.NextQuaternionF(); RigidBody body = new RigidBody(boxShape) { Pose = new Pose(position, orientation), }; Simulation.RigidBodies.Add(body); } // ----- Add capsules at random poses. CapsuleShape capsuleShape = new CapsuleShape(0.3f, 1.2f); for (int i = 0; i < 20; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-3, 3); position.Y = 5; QuaternionF orientation = RandomHelper.Random.NextQuaternionF(); RigidBody body = new RigidBody(capsuleShape) { Pose = new Pose(position, orientation), }; Simulation.RigidBodies.Add(body); } }
/// <summary> /// Computes a minimum bounding shape that contains all given points. /// </summary> /// <param name="points">The points.</param> /// <returns>A minimum bounding shape that contains all given points.</returns> /// <remarks> /// The returned shape will be a <see cref="SphereShape"/>, a <see cref="CapsuleShape"/>, /// a <see cref="BoxShape"/>, or a <see cref="TransformedShape"/> (containing a sphere, capsule, /// or a box). The bounding shape is not guaranteed to be optimal, it is only guaranteed that /// the bounding shape includes all given points. /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="points"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="points"/> is empty. /// </exception> public static Shape CreateBoundingShape(IList<Vector3F> points) { if (points == null) throw new ArgumentNullException("points"); if (points.Count == 0) throw new ArgumentException("The list of 'points' is empty."); // Compute minimal sphere. Vector3F center; float radius; ComputeBoundingSphere(points, out radius, out center); SphereShape sphere = new SphereShape(radius); float sphereVolume = sphere.GetVolume(); // Compute minimal capsule. float height; Pose capsulePose; ComputeBoundingCapsule(points, out radius, out height, out capsulePose); CapsuleShape capsule = new CapsuleShape(radius, height); float capsuleVolume = capsule.GetVolume(); // Compute minimal box. Vector3F boxExtent; Pose boxPose; ComputeBoundingBox(points, out boxExtent, out boxPose); var box = new BoxShape(boxExtent); float boxVolume = box.GetVolume(); // Return the object with the smallest volume. // A TransformedShape is used if the shape needs to be translated or rotated. if (sphereVolume < boxVolume && sphereVolume < capsuleVolume) { if (center.IsNumericallyZero) return sphere; return new TransformedShape(new GeometricObject(sphere, new Pose(center))); } else if (capsuleVolume < boxVolume) { if (!capsulePose.HasTranslation && !capsulePose.HasRotation) return capsule; return new TransformedShape(new GeometricObject(capsule, capsulePose)); } else { if (!boxPose.HasTranslation && !boxPose.HasRotation) return box; return new TransformedShape(new GeometricObject(box, boxPose)); } }
public ContentPipelineHeightFieldSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Load height field model and add it to the graphics scene. _heightFieldModelNode = ContentManager.Load<ModelNode>("HeightField/TerrainHeights").Clone(); GraphicsScreen.Scene.Children.Add(_heightFieldModelNode); // The UserData contains the collision shape of type HeightField. HeightField heightField = (HeightField)_heightFieldModelNode.UserData; _heightFieldModelNode.PoseWorld = new Pose(new Vector3F(-heightField.WidthX / 2, 0, -heightField.WidthZ / 2)); // Create rigid body. _heightFieldBody = new RigidBody(heightField, null, null) { MotionType = MotionType.Static, Pose = _heightFieldModelNode.PoseWorld, // The PhysicsSample class should not draw the height field. UserData = "NoDraw", }; Simulation.RigidBodies.Add(_heightFieldBody); // Distribute a few spheres and boxes across the landscape. SphereShape sphereShape = new SphereShape(0.5f); for (int i = 0; i < 30; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-30, 30); position.Y = 20; RigidBody body = new RigidBody(sphereShape) { Pose = new Pose(position) }; Simulation.RigidBodies.Add(body); } BoxShape boxShape = new BoxShape(1, 1, 1); for (int i = 0; i < 30; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-30, 30); position.Y = 20; RigidBody body = new RigidBody(boxShape) { Pose = new Pose(position) }; Simulation.RigidBodies.Add(body); } }
public WallSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", // Names are not required but helpful for debugging. MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(groundPlane); // ----- Create a wall of boxes. const int wallHeight = 10; const int wallWidth = 10; const float boxWidth = 1.0f; const float boxDepth = 0.5f; const float boxHeight = 0.5f; BoxShape boxShape = new BoxShape(boxWidth, boxHeight, boxDepth); // Optional: Use a small overlap between boxes to improve the stability. float overlap = Simulation.Settings.Constraints.AllowedPenetration * 0.5f; float x; float y = boxHeight / 2 - overlap; float z = -5; for (int i = 0; i < wallHeight; i++) { for (int j = 0; j < wallWidth; j++) { // Tip: Leave a small gap between neighbor bricks. If the neighbors on the same // row do not touch, the simulation has less work to do! x = -boxWidth * wallWidth / 2 + j * (boxWidth + 0.02f) + (i % 2) * boxWidth / 2; RigidBody brick = new RigidBody(boxShape) { Name = "Brick" + i, Pose = new Pose(new Vector3F(x, y, z)), }; Simulation.RigidBodies.Add(brick); } y += boxHeight - overlap; } }
public KinematicSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", // Names are not required but helpful for debugging. MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(groundPlane); // Create the kinematic body. (Kinematic means that the velocity of the body is not // the result of simulated forces and constraints. Instead the velocity of the body // is directly controlled by the application.) _kinematicBody = new RigidBody(new ConeShape(0.3f, 1)) { MotionType = MotionType.Kinematic }; Simulation.RigidBodies.Add(_kinematicBody); // Create a cyclic path. CreatePath(); // Add a number of random boxes. const int numberOfBoxes = 20; BoxShape boxShape = new BoxShape(1, 1, 1); for (int i = 0; i < numberOfBoxes; i++) { Vector3F randomPosition = RandomHelper.Random.NextVector3F(-5, 5); randomPosition.Y = 5; QuaternionF randomOrientation = RandomHelper.Random.NextQuaternionF(); RigidBody body = new RigidBody(boxShape) { Pose = new Pose(randomPosition, randomOrientation), }; Simulation.RigidBodies.Add(body); } }
public RagdollSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", // Names are not required but helpful for debugging. MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(groundPlane); // Add a number of ragdolls. for (int i = 0; i < 5; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-3, 3); position.Y = 1 + i; AddRagdoll(Simulation, 1, position, 0.0005f, true); } // Add some random, static boxes. BoxShape boxShape = new BoxShape(1, 1, 1); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-5, 5); position.Y = 0.5f; QuaternionF orientation = RandomHelper.Random.NextQuaternionF(); RigidBody body = new RigidBody(boxShape) { Pose = new Pose(position, orientation), MotionType = MotionType.Static }; Simulation.RigidBodies.Add(body); } }
public void BoxMass() { var b = new BoxShape(1, 2, 3); float m0; Vector3F com0; Matrix33F i0; MassHelper.GetMass(b, new Vector3F(1, -2, -3), 1, true, 0.001f, 10, out m0, out com0, out i0); var m = b.GetMesh(0.1f, 1); m.Transform(Matrix44F.CreateScale(1, -2, -3)); float m1; Vector3F com1; Matrix33F i1; MassHelper.GetMass(m, out m1, out com1, out i1); Assert.AreEqual(m0, m1); Assert.AreEqual(com0, com1); Assert.AreEqual(i0, i1); // Try other density. float m2; Vector3F com2; Matrix33F i2; MassHelper.GetMass(b, new Vector3F(1, -2, -3), 0.7f, true, 0.001f, 10, out m2, out com2, out i2); Assert.AreEqual(m0 * 0.7f, m2); Assert.AreEqual(com0, com2); Assert.IsTrue(Matrix33F.AreNumericallyEqual(i0 * 0.7f, i2)); const float e = 0.01f; // Try with target mass. float m3; Vector3F com3; Matrix33F i3; MassHelper.GetMass(b, new Vector3F(1, -2, -3), 23, false, 0.001f, 10, out m3, out com3, out i3); Assert.IsTrue(Numeric.AreEqual(23, m3, e * (1 + m0))); Assert.IsTrue(Vector3F.AreNumericallyEqual(com0, com3, e * (1 + com0.Length))); Assert.IsTrue(Matrix33F.AreNumericallyEqual(i0 * 23 / m0, i3, e * (1 + i0.Trace))); }
public SmallerScaleSample(Microsoft.Xna.Framework.Game game) : base(game) { // The default simulation settings are optimized for game objects that are 1 unit in size. // These default settings are useful for standard game objects: crates, barrels, furniture, // humans, rocks, etc. // The simulation settings should be adapted if the average game object is more than 10 times // bigger or smaller. (Note: Future versions of DigitalRune.Physics will automatically // adapt the simulation settings.) // If you are unsure which settings are relevant for your game scenario, just ask in our // support forums: http://www.digitalrune.com/Support/Forum.aspx // Nevertheless, it is strongly recommended to scale game objects so that the average game // object is about 1 unit in size. // In this sample, the average game object is 0.05 units. // Simulating small objects is a lot more difficult. Therefore, we decrease the time // step size to improve the simulation accuracy. Simulation.Settings.Timing.FixedTimeStep /= 5; Simulation.Settings.Timing.MaxNumberOfSteps *= 5; // Rigid bodies have an allowed penetration. Errors in this range are acceptable and not // corrected to improve stability and remove jittering. For small objects this limit // must be reduced. Simulation.Settings.Constraints.AllowedPenetration /= 5; // The collision detection settings are defined in the CollisionDetection class. // The collision detection uses a tolerance to define when two contacts near each other // can be considered the same contact. For small objects this limit must be reduced. Simulation.CollisionDomain.CollisionDetection.ContactPositionTolerance /= 5; // To improve stacking of small objects: Simulation.Settings.Constraints.StackingFactor = 10; // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping { AngularDamping = 0.9f }); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(groundPlane); // ----- Add a stack of small boxes. const float boxSize = 0.05f; float overlap = Simulation.Settings.Constraints.AllowedPenetration * 0.5f; float yPosition = boxSize / 2 - overlap; BoxShape boxShape = new BoxShape(boxSize, boxSize, boxSize); for (int i = 0; i < 10; i++) { RigidBody stackBox = new RigidBody(boxShape) { Pose = new Pose(new Vector3F(0, yPosition, 0)), }; Simulation.RigidBodies.Add(stackBox); yPosition += boxSize - overlap; } // ------ Add spheres at random positions. SphereShape sphereShape = new SphereShape(boxSize * 0.5f); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-0.5f, 0.5f); position.Y = 1; RigidBody body = new RigidBody(sphereShape) { Pose = new Pose(position), }; Simulation.RigidBodies.Add(body); } }
// 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; } }
/// <summary> /// Gets a bounding shape that matches the specified AABB. /// </summary> /// <param name="aabb">The AABB.</param> /// <returns>A box or transformed box that matches the specified AABB.</returns> private Shape GetBoundingShape(Aabb aabb) { // The AABB of the LOD is real world size including scaling. We have to undo // the scale because this LodGroupNode also applies the same scale. var unscaledCenter = aabb.Center / ScaleWorld; var unscaledExtent = aabb.Extent / ScaleWorld; // Get existing shape objects to avoid unnecessary memory allocation. BoxShape boxShape; GeometricObject geometricObject = null; TransformedShape transformedShape = null; if (Shape is BoxShape) { boxShape = (BoxShape)Shape; } else if (Shape is TransformedShape) { transformedShape = (TransformedShape)Shape; geometricObject = (GeometricObject)transformedShape.Child; boxShape = (BoxShape)geometricObject.Shape; } else { boxShape = new BoxShape(); } // Make bounding box the size of the unscaled AABB. boxShape.Extent = unscaledExtent; if (unscaledCenter.IsNumericallyZero) { // Bounding box is centered at origin. return boxShape; } // Apply offset to bounding box. if (transformedShape == null) { geometricObject = new GeometricObject(boxShape, new Pose(unscaledCenter)); transformedShape = new TransformedShape(geometricObject); } else { geometricObject.Shape = boxShape; geometricObject.Pose = new Pose(unscaledCenter); } return transformedShape; }
private void InitializePhysics() { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(groundPlane); // Add walls. RigidBody wall0 = new RigidBody(new BoxShape(10, 2, 0.5f)) { Name = "Wall0", MotionType = MotionType.Static, Pose = new Pose(new Vector3F(0, 1, -5)) }; Simulation.RigidBodies.Add(wall0); RigidBody wall1 = new RigidBody(new BoxShape(10, 2, 0.5f)) { Name = "Wall1", MotionType = MotionType.Static, Pose = new Pose(new Vector3F(0, 1, 5)) }; Simulation.RigidBodies.Add(wall1); RigidBody wall2 = new RigidBody(new BoxShape(0.5f, 2, 10)) { Name = "Wall2", MotionType = MotionType.Static, Pose = new Pose(new Vector3F(-5, 1, 0)) }; Simulation.RigidBodies.Add(wall2); RigidBody wall3 = new RigidBody(new BoxShape(0.5f, 2, 10)) { Name = "Wall3", MotionType = MotionType.Static, Pose = new Pose(new Vector3F(5, 1, 0)) }; Simulation.RigidBodies.Add(wall3); // Add sphere. RigidBody sphere = new RigidBody(new SphereShape(0.4f)) { Name = "Sphere", Pose = new Pose(new Vector3F(1, 1, 1)), }; Simulation.RigidBodies.Add(sphere); // Add a stack of boxes. const int numberOfBoxes = 3; const float boxSize = 0.8f; BoxShape boxShape = new BoxShape(boxSize, boxSize, boxSize); // Optional: Use a small overlap between boxes to improve the stability. float overlap = Simulation.Settings.Constraints.AllowedPenetration * 0.5f; Vector3F position = new Vector3F(0, boxSize / 2 - overlap, 0); for (int i = 0; i < numberOfBoxes; i++) { RigidBody box = new RigidBody(boxShape) { Name = "Box" + i, Pose = new Pose(position), }; Simulation.RigidBodies.Add(box); position.Y += boxSize - overlap; } }
public HeightFieldSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Create a height field. // The height data consists of height samples with a resolution of 20 entries per dimension. // (Height samples are stored in a 1-dimensional array.) var numberOfSamplesX = 20; var numberOfSamplesZ = 20; var samples = new float[numberOfSamplesX * numberOfSamplesZ]; for (int z = 0; z < numberOfSamplesZ; z++) { for (int x = 0; x < numberOfSamplesX; x++) { // Set the y values (height). samples[z * numberOfSamplesX + x] = (float)(Math.Cos(z / 2f) * Math.Sin(x / 2f) * 5f + 5f); } } // The height field has a size of 100 m x 100 m. float widthX = 100; float widthZ = 100; // The origin is at (-50, -50) to center the height field at the world space origin. float originX = -50; float originZ = -50; // Create the height field shape. HeightField heightField = new HeightField( originX, originZ, widthX, widthZ, samples, numberOfSamplesX, numberOfSamplesZ); // We can set following flag to get a significant performance gain - but the collision // detection will be less accurate. For smooth height fields this flag can be set. heightField.UseFastCollisionApproximation = true; // Create a static rigid body using the height field and add it to the simulation. RigidBody landscape = new RigidBody(heightField) { Pose = Pose.Identity, MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(landscape); // Distribute a few spheres and boxes across the landscape. SphereShape sphereShape = new SphereShape(0.5f); for (int i = 0; i < 30; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-30, 30); position.Y = 20; RigidBody body = new RigidBody(sphereShape) { Pose = new Pose(position), }; Simulation.RigidBodies.Add(body); } BoxShape boxShape = new BoxShape(1, 1, 1); for (int i = 0; i < 30; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-30, 30); position.Y = 20; RigidBody body = new RigidBody(boxShape) { Pose = new Pose(position), }; Simulation.RigidBodies.Add(body); } }
protected override void OnLoad() { _scene = _services.GetInstance<IScene>(); // Get a bounding shape for the cells. We use a box with the cell size and // make it bigger by some arbitrary values. The boxes must be bigger because // mesh instances can be placed on the cell boundary and when they are animated // tree branches can sway outside the cell bounds. var meshAabb = _mesh.BoundingShape.GetAabb(); float meshWidth = new Vector2F(meshAabb.Extent.X, meshAabb.Extent.Z).Length * 1.5f; float meshHeight = meshAabb.Extent.Y * 1.7f; var boxShape = new BoxShape(_cellSize + meshWidth, meshHeight, _cellSize + meshWidth); // Create one MeshInstancingNode per cell and add random instances. _nodes = new MeshInstancingNode<InstanceData>[_numberOfCellsX, _numberOfCellsZ]; float xOrigin = -(_numberOfCellsX * _cellSize) / 2; float zOrigin = -(_numberOfCellsZ * _cellSize) / 2; var random = new Random(_randomSeed); for (int x = 0; x < _numberOfCellsX; x++) { for (int z = 0; z < _numberOfCellsZ; z++) { var instances = new InstanceData[_numberOfInstancesPerCell]; for (int i = 0; i < instances.Length; i++) { Vector3F scale = new Vector3F(random.NextFloat(0.5f, 1.5f)); Pose pose = new Pose(new Vector3F(xOrigin + x * _cellSize + random.NextFloat(0, _cellSize), 0, zOrigin + z * _cellSize + random.NextFloat(0, _cellSize)), Matrix33F.CreateRotationY(random.NextFloat(0, 10))); Vector4F color = new Vector4F(1); instances[i] = new InstanceData(scale, pose, color); } _nodes[x, z] = new MeshInstancingNode<InstanceData>(_mesh, instances) { PoseLocal = new Pose(new Vector3F(xOrigin + (0.5f + x) * _cellSize, boxShape.WidthY / 2, zOrigin + (0.5f + z) * _cellSize)), Shape = boxShape, CastsShadows = _castsShadows, }; _scene.Children.Add(_nodes[x, z]); } } UpdateLodDistances(); // ----- Add GUI controls to the Options window. var sampleFramework = _services.GetInstance<SampleFramework>(); var optionsPanel = sampleFramework.AddOptions("Game Objects"); var panel = SampleHelper.AddGroupBox(optionsPanel, "VegetationObject " + Name); SampleHelper.AddSlider( panel, "Min distance", "F2", 0, 100, MinDistance, value => MinDistance = value); SampleHelper.AddSlider( panel, "Max distance", "F2", 0, 100, MaxDistance, value => MaxDistance = value); }
public static void Load(CollisionDomain collisionDomain) { // Create a box for the ground. AddObject("Ground", new Pose(new Vector3F(0, -5, 0)), new BoxShape(60, 10, 60), collisionDomain); // Create a small flying sphere to visualize the approx. head height. - This is just // for debugging so that we have a feeling for heights. AddObject("Sphere", new Pose(new Vector3F(0, 1.5f, 0)), new SphereShape(0.2f), collisionDomain); // Create small walls at the level boundary. AddObject("WallLeft", new Pose(new Vector3F(-30, 1, 0)), new BoxShape(0.3f, 2, 60), collisionDomain); AddObject("WallRight", new Pose(new Vector3F(30, 1, 0)), new BoxShape(0.3f, 2, 60), collisionDomain); AddObject("WallFront", new Pose(new Vector3F(0, 1, -30)), new BoxShape(60, 2, 0.3f), collisionDomain); AddObject("WallBack", new Pose(new Vector3F(0, 1, 30)), new BoxShape(60, 2, 0.3f), collisionDomain); // Create a few bigger objects. // We position the boxes so that we have a few corners we can run into. Character controllers // should be stable when the user runs into corners. AddObject("House0", new Pose(new Vector3F(10, 1, -10)), new BoxShape(8, 2, 8f), collisionDomain); AddObject("House1", new Pose(new Vector3F(13, 1, -4)), new BoxShape(2, 2, 4), collisionDomain); AddObject("House2", new Pose(new Vector3F(10, 2, -15), Matrix33F.CreateRotationY(-0.3f)), new BoxShape(8, 4, 2), collisionDomain); // // Create stairs with increasing step height. // // Each step is a box. With this object we can test if our character can climb up // stairs. The character controller has a step height limit. Increasing step heights // let us test if the step height limit works. float startHeight = 0; const float stepDepth = 1f; for (int i = 0; i < 10; i++) { float stepHeight = 0.1f + i * 0.05f; Pose pose = new Pose(new Vector3F(0, startHeight + stepHeight / 2, -2 - i * stepDepth)); BoxShape shape = new BoxShape(2, stepHeight, stepDepth); AddObject("Step" + i, pose, shape, collisionDomain); startHeight += stepHeight; } // // Create a height field. // // Terrain that is uneven is best modeled with a height field. Height fields are faster // than general triangle meshes. // The height direction is the y direction. // The height field lies in the x/z plane. var numberOfSamplesX = 20; var numberOfSamplesZ = 20; var samples = new float[numberOfSamplesX * numberOfSamplesZ]; // Create arbitrary height values. for (int z = 0; z < numberOfSamplesZ; z++) { for (int x = 0; x < numberOfSamplesX; x++) { if (x == 0 || z == 0 || x == 19 || z == 19) { // Set this boundary elements to a low height, so that the height field is connected // to the ground. samples[z * numberOfSamplesX + x] = -1; } else { // A sine/cosine function that creates some interesting waves. samples[z * numberOfSamplesX + x] = 1.0f + (float)(Math.Cos(z / 2f) * Math.Sin(x / 2f) * 1.0f); } } } var heightField = new HeightField(0, 0, 20, 20, samples, numberOfSamplesX, numberOfSamplesZ); AddObject("Heightfield", new Pose(new Vector3F(10, 0, 10)), heightField, collisionDomain); // Create rubble on the floor (small random objects on the floor). // Our character should be able to move over small bumps on the ground. for (int i = 0; i < 50; i++) { Pose pose = new Pose( new Vector3F(RandomHelper.Random.NextFloat(-5, 5), 0, RandomHelper.Random.NextFloat(10, 20)), RandomHelper.Random.NextQuaternionF()); BoxShape shape = new BoxShape(RandomHelper.Random.NextVector3F(0.05f, 0.8f)); AddObject("Stone" + i, pose, shape, collisionDomain); } // Create some slopes to see how our character performs on/under sloped surfaces. // Here we can test how the character controller behaves if the head touches an inclined // ceiling. AddObject("SlopeGround", new Pose(new Vector3F(-2, 1.8f, -12), QuaternionF.CreateRotationX(0.4f)), new BoxShape(2, 0.5f, 10), collisionDomain); AddObject("SlopeRoof", new Pose(new Vector3F(-2, 5.6f, -12), QuaternionF.CreateRotationX(-0.4f)), new BoxShape(2, 0.5f, 10), collisionDomain); // Slopes with different tilt angles. // The character controller has a slope limit. Only flat slopes should be climbable. for (int i = 0; i < 10; i++) { float stepHeight = 0.1f + i * 0.1f; Pose pose = new Pose( new Vector3F(-10, i * 0.5f, -i * 2), Matrix33F.CreateRotationX(MathHelper.ToRadians(10) + i * MathHelper.ToRadians(10))); BoxShape shape = new BoxShape(8 * (1 - i * 0.1f), 0.5f, 30); AddObject("Slope" + i, pose, shape, collisionDomain); startHeight += stepHeight; } // Create a slope with a wall on one side. // This objects let's us test how the character controller behaves while falling and // sliding along a vertical wall. (Run up the slope and then jump down while moving into // the wall.) AddObject("LongSlope", new Pose(new Vector3F(-20, 3, -10), Matrix33F.CreateRotationX(0.4f)), new BoxShape(4, 5f, 30), collisionDomain); AddObject("LongSlopeWall", new Pose(new Vector3F(-22, 5, -10)), new BoxShape(0.5f, 10f, 25), collisionDomain); // Create a mesh object to test walking on triangle meshes. // Normally, the mesh would be loaded from a file. Here, we make a composite shape and // let DigitalRune Geometry compute a mesh for it. Then we throw away the composite // shape and use only the mesh. - We do this to test triangle meshes. Using the composite // shape instead of the triangle mesh would be a lot faster. CompositeShape compositeShape = new CompositeShape(); compositeShape.Children.Add(new GeometricObject(heightField, Pose.Identity)); compositeShape.Children.Add(new GeometricObject(new CylinderShape(1, 2), new Pose(new Vector3F(10, 1, 10)))); compositeShape.Children.Add(new GeometricObject(new SphereShape(3), new Pose(new Vector3F(15, 0, 15)))); compositeShape.Children.Add(new GeometricObject(new BoxShape(1, 2, 3), new Pose(new Vector3F(15, 0, 5)))); ITriangleMesh mesh = compositeShape.GetMesh(0.01f, 3); TriangleMeshShape meshShape = new TriangleMeshShape(mesh); // Collision detection speed for triangle meshes can be improved by using a spatial // partition. Here, we assign an AabbTree to the triangle mesh shape. The tree is // built automatically when needed and it stores triangle indices (therefore the generic // parameter of the AabbTree is int). meshShape.Partition = new AabbTree<int>() { // The tree is automatically built using a mixed top-down/bottom-up approach. Bottom-up // building is slower but produces better trees. If the tree building takes too long, // we can lower the BottomUpBuildThreshold (default is 128). BottomUpBuildThreshold = 0, }; AddObject("Mesh", new Pose(new Vector3F(-30, 0, 10)), meshShape, collisionDomain); }
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)); }
public MaterialSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", // Names are not required but helpful for debugging. MotionType = MotionType.Static, }; // Adjust the coefficient of restitution of the ground plane. // (By default, the material of a rigid body is of type UniformMaterial.) ((UniformMaterial)groundPlane.Material).Restitution = 1; // Max. bounciness. (The simulation actually // accepts higher values, but these usually // lead to unnatural bounciness.) Simulation.RigidBodies.Add(groundPlane); // Add a static inclined ground plane. RigidBody inclinedPlane = new RigidBody(new PlaneShape(new Vector3F(-0.3f, 1f, 0).Normalized, 0)) { Name = "InclinedPlane", MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(inclinedPlane); // Create a few boxes with different coefficient of friction. BoxShape boxShape = new BoxShape(1, 1, 1); for (int i = 0; i < 5; i++) { // Each box gets a different friction value. UniformMaterial material = new UniformMaterial { DynamicFriction = i * 0.2f, StaticFriction = i * 0.2f, }; RigidBody box = new RigidBody(boxShape, null, material) // The second argument (the mass frame) is null. The { // simulation will automatically compute a default mass. Name = "Box" + i, Pose = new Pose(new Vector3F(5, 6, -5 + i * 2)), }; Simulation.RigidBodies.Add(box); } // Create a few balls with different coefficient of restitution (= bounciness). Shape sphereShape = new SphereShape(0.5f); for (int i = 0; i < 6; i++) { // Vary restitution between 0 and 1. UniformMaterial material = new UniformMaterial { Restitution = i * 0.2f }; RigidBody body = new RigidBody(sphereShape, null, material) { Name = "Ball" + i, Pose = new Pose(new Vector3F(-1 - i * 2, 5, 0)), }; Simulation.RigidBodies.Add(body); } }
/// <summary> /// Gets a bounding shape that matches the specified AABB. /// </summary> /// <param name="aabb">The AABB.</param> /// <returns>A box or transformed box that matches the specified AABB.</returns> private Shape GetBoundingShape(Aabb aabb) { // Get existing shape objects to avoid unnecessary memory allocation. BoxShape boxShape; GeometricObject geometricObject = null; TransformedShape transformedShape = null; if (Shape is BoxShape) { boxShape = (BoxShape)Shape; } else if (Shape is TransformedShape) { transformedShape = (TransformedShape)Shape; geometricObject = (GeometricObject)transformedShape.Child; boxShape = (BoxShape)geometricObject.Shape; } else { boxShape = new BoxShape(); } // Make box the size of the AABB. boxShape.Extent = aabb.Extent; if (aabb.Center.IsNumericallyZero) { // Bounding box is centered at origin. return boxShape; } // Apply offset to bounding box. if (transformedShape == null) { geometricObject = new GeometricObject(boxShape, new Pose(aabb.Center)); transformedShape = new TransformedShape(geometricObject); } else { geometricObject.Shape = boxShape; geometricObject.Pose = new Pose(aabb.Center); } return transformedShape; }
public AdvancedAvatarRagdollSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; // This sample uses for a DebugRenderer to draw text and rigid bodies. _debugRenderer = new DebugRenderer(GraphicsService, SpriteFont) { DefaultColor = Color.Black, DefaultTextPosition = new Vector2F(10), }; // Add a custom game object which controls the camera. _cameraObject = new CameraObject(Services); _cameraObject.ResetPose(new Vector3F(0, 1, -3), ConstantsF.Pi, 0); GameObjectService.Objects.Add(_cameraObject); // Add some objects which allow the user to interact with the rigid bodies. _grabObject = new GrabObject(Services); _ballShooterObject = new BallShooterObject(Services) { Speed = 20 }; GameObjectService.Objects.Add(_grabObject); GameObjectService.Objects.Add(_ballShooterObject); // Add some default force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Create a random avatar. _avatarDescription = AvatarDescription.CreateRandom(); _avatarRenderer = new AvatarRenderer(_avatarDescription); // Use the "Wave" animation preset. var avatarAnimation = new AvatarAnimation(AvatarAnimationPreset.Wave); _expressionAnimation = new AnimationClip<AvatarExpression>(new WrappedAvatarExpressionAnimation(avatarAnimation)) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; _skeletonAnimation = new AnimationClip<SkeletonPose>(new WrappedAvatarSkeletonAnimation(avatarAnimation)) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; // Add a ground plane in the simulation. Simulation.RigidBodies.Add(new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { MotionType = MotionType.Static }); // Distribute a few dynamic spheres and boxes across the landscape. SphereShape sphereShape = new SphereShape(0.3f); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-10, 10); position.Y = 1; Simulation.RigidBodies.Add(new RigidBody(sphereShape) { Pose = new Pose(position) }); } BoxShape boxShape = new BoxShape(0.6f, 0.6f, 0.6f); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-10, 10); position.Y = 1; Simulation.RigidBodies.Add(new RigidBody(boxShape) { Pose = new Pose(position) }); } }
private static void GetMass(BoxShape box, Vector3F scale, float densityOrMass, bool isDensity, out float mass, out Matrix33F inertia) { scale = Vector3F.Absolute(scale); Vector3F extent = box.Extent * scale; GetMass(extent, densityOrMass, isDensity, out mass, out inertia); }
public ContentPipelineMeshSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", // Names are not required but helpful for debugging. MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(groundPlane); // ----- Load triangle mesh model. 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(new Vector3F(0, 2, -5), Matrix33F.CreateRotationY(MathHelper.ToRadians(-30))); _modelNode.ScaleLocal = new Vector3F(8); // The UserData contains the collision shape of type TriangleMeshShape. TriangleMeshShape triangleMesh = (TriangleMeshShape)_modelNode.UserData; // Add model node to graphics scene. GraphicsScreen.Scene.Children.Add(_modelNode); // Create rigid body. // We explicitly specify a mass frame. We can use any mass frame for static bodies (because // static bodies are effectively treated as if they have infinite mass). If we do not specify // a mass frame in the rigid body constructor, the constructor will automatically compute an // approximate mass frame (which can take some time for large meshes). _rigidBody = new RigidBody(triangleMesh, new MassFrame(), null) { MotionType = MotionType.Static, Pose = _modelNode.PoseWorld, Scale = _modelNode.ScaleWorld, // The PhysicsSample class should not draw the height field. UserData = "NoDraw", }; Simulation.RigidBodies.Add(_rigidBody); // Distribute a few spheres and boxes across the triangle mesh. SphereShape sphereShape = new SphereShape(0.5f); for (int i = 0; i < 40; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-15, 10); position.Y = RandomHelper.Random.NextFloat(20, 40); RigidBody body = new RigidBody(sphereShape) { Pose = new Pose(position) }; Simulation.RigidBodies.Add(body); } BoxShape boxShape = new BoxShape(1, 1, 1); for (int i = 0; i < 40; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-15, 10); position.Y = RandomHelper.Random.NextFloat(20, 40); RigidBody body = new RigidBody(boxShape) { Pose = new Pose(position) }; Simulation.RigidBodies.Add(body); } }
/// <summary> /// Initializes the physics simulation. /// </summary> private void InitializePhysics() { // Define the timing of the physics simulation: // The Windows Phone runs with 30 FPS, so one frame takes 1/30 seconds. // We can run the simulation using the same timing. // However, 1/30 seconds is a very large time step for a physics simulation. A large // time step improves performance, but reduces the quality of the simulation. // Instead we can run the simulation with a time step of 1/60 seconds and compute // 2 time steps per frame. // Additionally, we can reduce the number of constraint solver iterations to gain additional // performance. But if you want to simulate complex scene with stable stacks or walls, you // might want to leave the default value. // Below are two variants - you can comment/uncomment the variants to compare them. // ----- Variant #1: "Fast, less stable" Simulation.Settings.Timing.FixedTimeStep = 1.0f / 30.0f; Simulation.Settings.Timing.MaxNumberOfSteps = 1; Simulation.Settings.Constraints.NumberOfConstraintIterations = 6; // ----- Variant #2: "Slower, more stable" //_simulation.Settings.Timing.FixedTimeStep = 1.0f / 60.0f; //_simulation.Settings.Timing.MaxNumberOfSteps = 2; //_simulation.Settings.Constraints.NumberOfConstraintIterations = 10; // Add the typical force effect for gravity and damping. Simulation.ForceEffects.Add(new Gravity { Acceleration = new Vector3F(0, -10, 0) }); Simulation.ForceEffects.Add(new Damping()); // Add a ground plane (static object). var groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(groundPlane); // Create a set of predefined shapes. // The shapes are chosen randomly when a new rigid body is added. _shapes = new List<Shape>(); _shapes.Add(new BoxShape(1.0f, 1.0f, 1.0f)); _shapes.Add(new BoxShape(1.5f, 0.5f, 1.0f)); _shapes.Add(new BoxShape(1.0f, 0.75f, 1.0f)); _shapes.Add(new CapsuleShape(0.4f, 2.0f)); _shapes.Add(new SphereShape(0.6f)); // Add convex shape. var randomPoints = new List<Vector3F>(); for (int i = 0; i < 20; i++) randomPoints.Add(RandomHelper.Random.NextVector3F(-1, 1)); _shapes.Add(new ConvexPolyhedron(randomPoints)); // Add a composite shape looking like table. var board = new BoxShape(2.0f, 0.3f, 1.2f); var leg = new BoxShape(0.2f, 1f, 0.2f); var table = new CompositeShape(); table.Children.Add(new GeometricObject(board, new Pose(new Vector3F(0.0f, 1.0f, 0.0f)))); table.Children.Add(new GeometricObject(leg, new Pose(new Vector3F(-0.7f, 0.5f, -0.4f)))); table.Children.Add(new GeometricObject(leg, new Pose(new Vector3F(-0.7f, 0.5f, 0.4f)))); table.Children.Add(new GeometricObject(leg, new Pose(new Vector3F(0.7f, 0.5f, 0.4f)))); table.Children.Add(new GeometricObject(leg, new Pose(new Vector3F(0.7f, 0.5f, -0.4f)))); _shapes.Add(table); }
public ShapesSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a ground plane. RigidBody groundPlane = new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { Name = "GroundPlane", // Names are not required but helpful for debugging. MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(groundPlane); // ----- Add a sphere. Shape sphere = new SphereShape(0.5f); Simulation.RigidBodies.Add(new RigidBody(sphere)); // ----- Add a box. BoxShape box = new BoxShape(0.5f, 0.9f, 0.7f); Simulation.RigidBodies.Add(new RigidBody(box)); // ----- Add a capsule. CapsuleShape capsule = new CapsuleShape(0.4f, 1.2f); Simulation.RigidBodies.Add(new RigidBody(capsule)); // ----- Add a cone. ConeShape cone = new ConeShape(0.5f, 1f); Simulation.RigidBodies.Add(new RigidBody(cone)); // ----- Add a cylinder. CylinderShape cylinder = new CylinderShape(0.3f, 1f); Simulation.RigidBodies.Add(new RigidBody(cylinder)); // ----- Add a convex hull of random points. ConvexHullOfPoints convexHullOfPoints = new ConvexHullOfPoints(); for (int i = 0; i < 20; i++) convexHullOfPoints.Points.Add(RandomHelper.Random.NextVector3F(-0.5f, 0.5f)); Simulation.RigidBodies.Add(new RigidBody(convexHullOfPoints)); // ----- Add a convex polyhedron. // (A ConvexPolyhedron is similar to the ConvexHullOfPoints. The difference is that // the points in a ConvexHullOfPoints can be changed at runtime. A ConvexPolyhedron // cannot be changed at runtime, but it is faster.) List<Vector3F> points = new List<Vector3F>(); for (int i = 0; i < 20; i++) points.Add(RandomHelper.Random.NextVector3F(-0.7f, 0.7f)); ConvexPolyhedron convexPolyhedron = new ConvexPolyhedron(points); Simulation.RigidBodies.Add(new RigidBody(convexPolyhedron)); // ----- Add a composite shape (a table that consists of 5 boxes). CompositeShape composite = new CompositeShape(); composite.Children.Add(new GeometricObject(new BoxShape(0.1f, 0.8f, 0.1f), new Pose(new Vector3F(-0.75f, 0.4f, -0.5f)))); composite.Children.Add(new GeometricObject(new BoxShape(0.1f, 0.8f, 0.1f), new Pose(new Vector3F(0.75f, 0.4f, -0.5f)))); composite.Children.Add(new GeometricObject(new BoxShape(0.1f, 0.8f, 0.1f), new Pose(new Vector3F(-0.75f, 0.4f, 0.5f)))); composite.Children.Add(new GeometricObject(new BoxShape(0.1f, 0.8f, 0.1f), new Pose(new Vector3F(0.75f, 0.4f, 0.5f)))); composite.Children.Add(new GeometricObject(new BoxShape(1.8f, 0.1f, 1.1f), new Pose(new Vector3F(0, 0.8f, 0)))); Simulation.RigidBodies.Add(new RigidBody(composite)); // ----- Add a convex hull of multiple shapes. ConvexHullOfShapes convexHullOfShapes = new ConvexHullOfShapes(); convexHullOfShapes.Children.Add(new GeometricObject(new CylinderShape(0.2f, 0.8f), new Pose(new Vector3F(-0.4f, 0, 0)))); convexHullOfShapes.Children.Add(new GeometricObject(new CylinderShape(0.2f, 0.8f), new Pose(new Vector3F(+0.4f, 0, 0)))); Simulation.RigidBodies.Add(new RigidBody(convexHullOfShapes)); // ----- Add the Minkowski sum of two shapes. // (The Minkowski sum is a mathematical operation that combines two shapes. // Here a circle is combined with a sphere. The result is a wheel.) MinkowskiSumShape minkowskiSum = new MinkowskiSumShape(); minkowskiSum.ObjectA = new GeometricObject(new SphereShape(0.2f), Pose.Identity); minkowskiSum.ObjectB = new GeometricObject(new CircleShape(0.5f), Pose.Identity); Simulation.RigidBodies.Add(new RigidBody(minkowskiSum)); // Create another Minkowski sum. (Here a small sphere is added to a box to create a // box with rounded corners.) minkowskiSum = new MinkowskiSumShape(); minkowskiSum.ObjectA = new GeometricObject(new SphereShape(0.1f), Pose.Identity); minkowskiSum.ObjectB = new GeometricObject(new BoxShape(0.2f, 0.5f, 0.8f), Pose.Identity); Simulation.RigidBodies.Add(new RigidBody(minkowskiSum)); // ----- Add a triangle mesh. // A triangle mesh could be loaded from a file or built from an XNA model. // Here we first create a composite shape and convert the shape into a triangle // mesh. (Any Shape in DigitalRune.Geometry can be converted to a triangle mesh.) CompositeShape dumbbell = new CompositeShape(); dumbbell.Children.Add(new GeometricObject(new SphereShape(0.4f), new Pose(new Vector3F(0.6f, 0.0f, 0.0f)))); dumbbell.Children.Add(new GeometricObject(new SphereShape(0.4f), new Pose(new Vector3F(-0.6f, 0.0f, 0.0f)))); dumbbell.Children.Add(new GeometricObject(new CylinderShape(0.1f, 0.6f), new Pose(Matrix33F.CreateRotationZ(ConstantsF.PiOver2)))); TriangleMeshShape triangleMeshShape = new TriangleMeshShape(dumbbell.GetMesh(0.01f, 2)); // Optional: We can enable "contact welding". When this flag is enabled, the triangle shape // precomputes additional internal information for the mesh. The collision detection will // be able to compute better contacts (e.g. better normal vectors at triangle edges). // Pro: Collision detection can compute better contact information. // Con: Contact welding information needs a bit of memory. And the collision detection is // a bit slower. triangleMeshShape.EnableContactWelding = true; // Optional: Assign a spatial partitioning scheme to the triangle mesh. (A spatial partition // adds an additional memory overhead, but it improves collision detection speed tremendously!) triangleMeshShape.Partition = new AabbTree<int>() { // The tree is automatically built using a mixed top-down/bottom-up approach. Bottom-up // building is slower but produces better trees. If the tree building takes too long, // we can lower the BottomUpBuildThreshold (default is 128). BottomUpBuildThreshold = 0, }; Simulation.RigidBodies.Add(new RigidBody(triangleMeshShape)); // ----- Set random positions/orientations. // (Start with the second object. The first object is the ground plane which should // not be changed.) for (int i = 1; i < Simulation.RigidBodies.Count; i++) { RigidBody body = Simulation.RigidBodies[i]; Vector3F position = RandomHelper.Random.NextVector3F(-3, 3); position.Y = 3; // Position the objects 3m above ground. QuaternionF orientation = RandomHelper.Random.NextQuaternionF(); body.Pose = new Pose(position, orientation); } }
public CompositeMaterialSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // ----- Create a simple height field. // The height data consists of height samples with a resolution of 20 entries per dimension. // (Height samples are stored in a 1-dimensional array.) var numberOfSamplesX = 20; var numberOfSamplesZ = 20; var samples = new float[numberOfSamplesX * numberOfSamplesZ]; for (int z = 0; z < numberOfSamplesZ; z++) { for (int x = 0; x < numberOfSamplesX; x++) { // Set the y values (height). samples[z * numberOfSamplesX + x] = 20 - z; } } // Set the size of the height field in world space. (WidthX/Z determine the extent // of the height field but not the resolution of the height samples.) float widthX = 40; float widthZ = 40; // The origin is at (-20, -20) to center the height field at the world space origin. float originX = -20; float originZ = -20; // Create the height field shape. HeightField heightField = new HeightField( originX, originZ, widthX, widthZ, samples, numberOfSamplesX, numberOfSamplesZ); RigidBody ground = new RigidBody(heightField) { Pose = new Pose(new Vector3F(0, -10, 0)), MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(ground); // Assign two different materials to the height field. // A rough material (high friction) should be used for the left cells of the height field. UniformMaterial roughMaterial = new UniformMaterial { DynamicFriction = 1, StaticFriction = 1, }; // A slippery material (low friction) should be used for the right cells of the height field. UniformMaterial slipperyMaterial = new UniformMaterial { DynamicFriction = 0, StaticFriction = 0, }; // Use a CompositeMaterial two assign the materials to the features of the height field. CompositeMaterial compositeMaterial = new CompositeMaterial(); // A "feature" of a height field is a triangle: // The height field is triangulated. Each cell consists of two triangles. The triangles are // numbered from left-to-right and top-to-bottom. // (For more information: See the description of HeightField.) // Loop over the cells. // (If the resolution is 20, we have 20 height values in one row. Between these height // values are 19 cells.) for (int z = 0; z < numberOfSamplesZ - 1; z++) { for (int x = 0; x < numberOfSamplesX - 1; x++) { // Assign the rough material to the left cells and the slippery material to the // right cells. if (x < numberOfSamplesX / 2) { // Each cell contains 2 triangles, therefore we have to add 2 entries to the // CompositeMaterial. compositeMaterial.Materials.Add(roughMaterial); compositeMaterial.Materials.Add(roughMaterial); } else { compositeMaterial.Materials.Add(slipperyMaterial); compositeMaterial.Materials.Add(slipperyMaterial); } } } ground.Material = compositeMaterial; // Create a few boxes on the height field. // The left boxes will roll or stop on the height field because of the high friction. // The right boxes will slide down because of the low friction. BoxShape boxShape = new BoxShape(1, 1, 1); for (int i = 0; i < 10; i++) { RigidBody body = new RigidBody(boxShape, null, roughMaterial) { Pose = new Pose(new Vector3F(-19 + i * 4, 10, -10)), }; Simulation.RigidBodies.Add(body); } }