Beispiel #1
0
    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);
    }
Beispiel #2
0
        public void ComputeCollision()
        {
            CollisionObject a = new CollisionObject();
              TriangleMesh mesh = new TriangleMesh();
              mesh.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(0, 0, 1), new Vector3F(1, 0, 0)), false);
              mesh.Add(new Triangle(new Vector3F(1, 0, 0), new Vector3F(0, 0, 1), new Vector3F(1, 0, 1)), false);
              TriangleMeshShape meshShape = new TriangleMeshShape();
              meshShape.Mesh = mesh;
              a.GeometricObject = new GeometricObject(meshShape, Pose.Identity);

              CollisionObject b = new CollisionObject(new GeometricObject
                  {
                    Shape = new SphereShape(1),
                    Pose = new Pose(new Vector3F(0, 0.9f, 0)),
                  });

              ContactSet set;
              TriangleMeshAlgorithm algo = new TriangleMeshAlgorithm(new CollisionDetection());

              set = algo.GetClosestPoints(a, b);
              Assert.AreEqual(true, algo.HaveContact(a, b));
              Assert.AreEqual(true, algo.HaveContact(b, a));
              Assert.AreEqual(1, set.Count);
              Assert.IsTrue(Numeric.AreEqual(0.1f, set[0].PenetrationDepth, 0.001f));
              //Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0, 0, 0), set[0].PositionALocal, 0.01f));  // MPR will not return the perfect contact point.
              Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0, 1, 0), set[0].Normal, 0.1f));

              ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(0.1f, 0.9f, 0.1f));
              algo.UpdateContacts(set, 0);
              Assert.AreEqual(true, algo.HaveContact(a, b));
              Assert.AreEqual(true, algo.HaveContact(b, a));
              Assert.AreEqual(1, set.Count);
              Assert.IsTrue(Numeric.AreEqual(0.1f, set[0].PenetrationDepth, 0.001f));
              Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0.1f, 0, 0.1f), set[0].PositionALocal, 0.01f));
              Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0, 1, 0), set[0].Normal, 0.1f));

              // The same with a swapped set.
              set = set.Swapped;
              algo.UpdateContacts(set, 0);
              Assert.AreEqual(1, set.Count);
              Assert.IsTrue(Numeric.AreEqual(0.1f, set[0].PenetrationDepth, 0.001f));
              Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0.1f, 0, 0.1f), set[0].PositionBLocal, 0.01f));
              Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0, -1, 0), set[0].Normal, 0.1f));

              // Separation.
              ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(0.2f, 1.2f, 0.3f));
              set = set.Swapped;
              Assert.AreEqual(false, algo.HaveContact(a, b));
              Assert.AreEqual(false, algo.HaveContact(b, a));
              algo.UpdateClosestPoints(set, 0);
              Assert.IsTrue(Numeric.AreEqual(-0.2f, set[0].PenetrationDepth, 0.001f));
              Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0.2f, 0, 0.3f), set[0].PositionALocal, 0.01f));
              Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0, 1, 0), set[0].Normal, 0.1f));
              algo.UpdateContacts(set, 0);
              Assert.AreEqual(0, set.Count);
        }
Beispiel #3
0
    public SpatialPartitionSample(Microsoft.Xna.Framework.Game game)
      : base(game)
    {
      SampleFramework.IsMouseVisible = false;
      GraphicsScreen.ClearBackground = true;
      GraphicsScreen.BackgroundColor = Color.Gray;
      GraphicsScreen.DrawReticle = true;
      SetCamera(new Vector3F(0, 1, 10), 0, 0);

      // Create a spatial partition. DigitalRune Geometry supports several types, see also
      // http://digitalrune.github.io/DigitalRune-Documentation/html/e32cab3b-cc7c-42ee-8ec9-23dd4467edd0.htm#WhichPartition
      // An AabbTree is useful for static objects. A DynamicAabbTree is good for moving objects.
      // The spatial partition can manage different types of items. In this case it manages
      // GeometricObjects. A delegate has to inform the spatial partition how to get the AABB
      // of an object.
      //_spatialPartition = new DynamicAabbTree<GeometricObject>
      _spatialPartition = new AabbTree<GeometricObject>
      {
        GetAabbForItem = geometricObject => geometricObject.Aabb,

        // 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: A filter can be set to disable certain kind of overlaps.
        //Filter = ...
      };

      // Create a triangle mesh.
      var triangleMesh = new SphereShape(1).GetMesh(0.01f, 4);
      var triangleMeshShape = new TriangleMeshShape(triangleMesh)
      {
        // TriangleMeshShapes can also use a spatial partition to manage triangle.
        // The items in the spatial partition are the triangle indices. The GetAabbForItem
        // delegate is set automatically.
        Partition = new AabbTree<int>(),
      };

      // Spatial partitions are built automatically when needed. However, it is still recommended
      // to call Update to initialize the spatial partition explicitly.
      triangleMeshShape.Partition.Update(false);

      // Add a lot of triangle mesh objects to _spatialPartition.
      var random = new Random();
      for (int i = 0; i < 50; i++)
      {
        var randomPosition = new Vector3F(random.NextFloat(-6, 6), random.NextFloat(-3, 3), random.NextFloat(-10, 0));
        var geometricObject = new GeometricObject(triangleMeshShape, new Pose(randomPosition));
        _spatialPartition.Add(geometricObject);
      }

      _spatialPartition.Update(false);
    }
Beispiel #4
0
        public void ComputeCollisionBvh()
        {
            CollisionObject a = new CollisionObject();
              TriangleMesh meshA = new TriangleMesh();
              meshA.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(0, 0, 1), new Vector3F(1, 0, 0)), false);
              var meshShapeA = new TriangleMeshShape();
              meshShapeA.Mesh = meshA;
              meshShapeA.Partition = new AabbTree<int>();
              ((GeometricObject)a.GeometricObject).Shape = meshShapeA;

              CollisionObject b = new CollisionObject();
              TriangleMesh meshB = new TriangleMesh();
              meshB.Add(new Triangle(new Vector3F(2, 0, 0), new Vector3F(2, 0, 1), new Vector3F(3, 0, 0)), false);
              TriangleMeshShape meshShapeB = new TriangleMeshShape();
              meshShapeB.Mesh = meshB;
              meshShapeB.Partition = new AabbTree<int>();
              ((GeometricObject)b.GeometricObject).Shape = meshShapeB;
              ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(-2, 0, 0));

              CollisionAlgorithm algo = new TriangleMeshAlgorithm(new CollisionDetection());
              Assert.AreEqual(true, algo.HaveContact(a, b));
        }
Beispiel #5
0
        public void GeneralShape()
        {
            var s = new CylinderShape(1, 3);
              float m0;
              Vector3F com0;
              Matrix33F i0;
              MassHelper.GetMass(s, new Vector3F(1, -2, -3), 0.7f, true, 0.001f, 4, out m0, out com0, out i0);

              var m = s.GetMesh(0.001f, 10);
              var s2 = new TriangleMeshShape(m);
              float m1;
              Vector3F com1;
              Matrix33F i1;
              MassHelper.GetMass(s2, new Vector3F(1, -2, -3), 0.7f, true, 0.001f, 4, out m1, out com1, out i1);

              const float e = 0.01f;
              Assert.IsTrue(Numeric.AreEqual(m0, m1, e * (1 + m0)));
              Assert.IsTrue(Vector3F.AreNumericallyEqual(com0, com1, e * (1 + com0.Length)));
              Assert.IsTrue(Matrix33F.AreNumericallyEqual(i0, i1, e * (1 + i0.Trace)));

              // Try with target mass.
              float m2;
              Vector3F com2;
              Matrix33F i2;
              MassHelper.GetMass(s2, new Vector3F(1, -2, -3), 23, false, 0.001f, 4, out m2, out com2, out i2);
              Assert.IsTrue(Numeric.AreEqual(23, m2, e * (1 + m0)));
              Assert.IsTrue(Vector3F.AreNumericallyEqual(com0, com2, e * (1 + com0.Length)));
              Assert.IsTrue(Matrix33F.AreNumericallyEqual(i0 * 23 / m0, i2, e * (1 + i0.Trace)));
        }
Beispiel #6
0
    //--------------------------------------------------------------
    #region Methods
    //--------------------------------------------------------------

    protected override void OnLoad()
    {
      // Add rigid bodies to simulation.
      var simulation = _services.GetInstance<Simulation>();

      // We use a random number generator with a custom seed.
      RandomHelper.Random = new Random(123);

      // ----- Add a ground plane.
      AddBody(simulation, "GroundPlane", Pose.Identity, new PlaneShape(Vector3F.UnitY, 0), MotionType.Static);

      // ----- Create a small flying sphere.
      AddBody(simulation, "Sphere", new Pose(new Vector3F(0, 1f, 0)), new SphereShape(0.2f), MotionType.Static);

      // ----- Create small walls at the level boundary.
      AddBody(simulation, "WallLeft", new Pose(new Vector3F(-30, 1, 0)), new BoxShape(0.3f, 2, 60), MotionType.Static);
      AddBody(simulation, "WallRight", new Pose(new Vector3F(30, 1, 0)), new BoxShape(0.3f, 2, 60), MotionType.Static);
      AddBody(simulation, "WallFront", new Pose(new Vector3F(0, 1, -30)), new BoxShape(60, 2, 0.3f), MotionType.Static);
      AddBody(simulation, "WallBack", new Pose(new Vector3F(0, 1, 30)), new BoxShape(60, 2, 0.3f), MotionType.Static);

      // ----- 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.
      AddBody(simulation, "House0", new Pose(new Vector3F(10, 1, -10)), new BoxShape(8, 2, 8f), MotionType.Static);
      AddBody(simulation, "House1", new Pose(new Vector3F(13, 1, -4)), new BoxShape(2, 2, 4), MotionType.Static);
      AddBody(simulation, "House2", new Pose(new Vector3F(10, 2, -15), Matrix33F.CreateRotationY(-0.3f)), new BoxShape(8, 4, 2), MotionType.Static);

      // ----- 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;
        Vector3F position = new Vector3F(0, startHeight + stepHeight / 2, -2 - i * stepDepth);
        AddBody(simulation, "Step" + i, new Pose(position), new BoxShape(2, stepHeight, stepDepth), MotionType.Static);

        startHeight += stepHeight;
      }

      // ----- V obstacle to test if we get stuck.
      AddBody(simulation, "V0", new Pose(new Vector3F(-5.5f, 0, 10), QuaternionF.CreateRotationZ(0.2f)), new BoxShape(1f, 2f, 2), MotionType.Static);
      AddBody(simulation, "V1", new Pose(new Vector3F(-4, 0, 10), QuaternionF.CreateRotationZ(-0.2f)), new BoxShape(1f, 2f, 2), MotionType.Static);

      // ----- 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);
      AddBody(simulation, "HeightField", new Pose(new Vector3F(10, 0, 10)), heightField, MotionType.Static);

      // ----- 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++)
      {
        Vector3F position = new Vector3F(RandomHelper.Random.NextFloat(-5, 5), 0, RandomHelper.Random.NextFloat(10, 20));
        QuaternionF orientation = RandomHelper.Random.NextQuaternionF();
        Vector3F size = RandomHelper.Random.NextVector3F(0.05f, 0.8f);
        AddBody(simulation, "Stone" + i, new Pose(position, orientation), new BoxShape(size), MotionType.Static);
      }

      // ----- 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.
      AddBody(simulation, "SlopeGround", new Pose(new Vector3F(-2, 1.8f, -12), QuaternionF.CreateRotationX(0.4f)), new BoxShape(2, 0.5f, 10), MotionType.Static);
      AddBody(simulation, "SlopeRoof", new Pose(new Vector3F(-2, 5.6f, -12), QuaternionF.CreateRotationX(-0.4f)), new BoxShape(2, 0.5f, 10), MotionType.Static);

      // Create slopes with increasing tilt angles.
      // The character controller has a slope limit. Only flat slopes should be climbable. 
      // Movement between slopes should be smooth.
      Vector3F slopePosition = new Vector3F(-17, -0.25f, 6);
      BoxShape slopeShape = new BoxShape(8, 0.5f, 5);
      for (int i = 1; i < 8; i++)
      {
        Matrix33F oldRotation = Matrix33F.CreateRotationX((i - 1) * MathHelper.ToRadians(10));
        Matrix33F rotation = Matrix33F.CreateRotationX(i * MathHelper.ToRadians(10));

        slopePosition += (oldRotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2
                         + (rotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2;

        AddBody(simulation, "Slope" + i, new Pose(slopePosition, rotation), slopeShape, MotionType.Static);
      }

      // Create slopes with decreasing tilt angles.
      slopePosition = new Vector3F(-8, -2, 5);
      slopeShape = new BoxShape(8f, 0.5f, 5);
      for (int i = 1; i < 8; i++)
      {
        Matrix33F oldRotation = Matrix33F.CreateRotationX(MathHelper.ToRadians(40) - (i - 1) * MathHelper.ToRadians(10));
        Matrix33F rotation = Matrix33F.CreateRotationX(MathHelper.ToRadians(40) - i * MathHelper.ToRadians(10));

        slopePosition += (oldRotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2
                         + (rotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2;
        Vector3F position = slopePosition - rotation * new Vector3F(0, slopeShape.WidthY / 2, 0);

        AddBody(simulation, "Slope2" + i, new Pose(position, rotation), slopeShape, MotionType.Static);
      }

      // ----- 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.)
      AddBody(simulation, "LongSlope", new Pose(new Vector3F(-24, 3, -10), Matrix33F.CreateRotationX(0.4f)), new BoxShape(4, 5f, 30), MotionType.Static);
      AddBody(simulation, "LongSlopeWall", new Pose(new Vector3F(-26, 5, -10)), new BoxShape(0.5f, 10f, 25), MotionType.Static);

      // ----- Create a trigger object that represents a ladder.
      var ladder = AddBody(simulation, "Ladder", new Pose(new Vector3F(-25.7f, 5, 0)), new BoxShape(0.5f, 10f, 1), MotionType.Static);
      ladder.CollisionObject.Type = CollisionObjectType.Trigger;

      // ----- 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(4, 4, 3), new Pose(new Vector3F(15, 1.5f, 5))));
      compositeShape.Children.Add(new GeometricObject(new BoxShape(4, 4, 3), new Pose(new Vector3F(15, 1.5f, 0))));
      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,
      };

      // Contact welding creates smoother contact normals - but it costs a bit of performance.
      meshShape.EnableContactWelding = true;

      AddBody(simulation, "Mesh", new Pose(new Vector3F(-30, 0, 10)), meshShape, MotionType.Static);

      // ----- Create a seesaw.
      var seesawBase = AddBody(simulation, "SeesawBase", new Pose(new Vector3F(5, 0.5f, 0)), new BoxShape(0.2f, 1, 1), MotionType.Static);
      var seesaw = AddBody(simulation, "Seesaw", new Pose(new Vector3F(5, 1.05f, 0)), new BoxShape(5, 0.1f, 1), MotionType.Dynamic);

      // Attach the seesaw to the base using a hinge joint.
      simulation.Constraints.Add(new HingeJoint
      {
        BodyA = seesaw,
        BodyB = seesawBase,
        AnchorPoseALocal = new Pose(new Vector3F(0, 0, 0),
                                    new Matrix33F(0, 0, -1,
                                                  0, 1, 0,
                                                  1, 0, 0)),
        AnchorPoseBLocal = new Pose(new Vector3F(0, 0.5f, 0),
                                    new Matrix33F(0, 0, -1,
                                                  0, 1, 0,
                                                  1, 0, 0)),
        CollisionEnabled = false,
      });

      // ----- A platform that is moving up/down.
      _elevator = AddBody(simulation, "Elevator", new Pose(new Vector3F(5, -1f, 5)), new BoxShape(3, 1f, 3), MotionType.Kinematic);
      _elevator.LinearVelocity = new Vector3F(2, 2, 0);

      // ----- A platform that is moving sideways.
      _pusher = AddBody(simulation, "Pusher", new Pose(new Vector3F(15, 0.5f, 0)), new BoxShape(3, 1f, 3), MotionType.Kinematic);
      _pusher.LinearVelocity = new Vector3F(0, 0, 2);

      // ----- Create conveyor belt with two static boxes on the sides.
      AddBody(simulation, "ConveyorSide0", new Pose(new Vector3F(19, 0.25f, 0)), new BoxShape(0.8f, 0.5f, 8f), MotionType.Static);
      AddBody(simulation, "ConveyorSide1", new Pose(new Vector3F(21, 0.25f, 0)), new BoxShape(0.8f, 0.5f, 8f), MotionType.Static);

      // The conveyor belt is a simple box with a special material.
      var conveyorBelt = AddBody(simulation, "ConveyorBelt", new Pose(new Vector3F(20, 0.25f, 0)), new BoxShape(1f, 0.51f, 8f), MotionType.Static);
      UniformMaterial materialWithSurfaceMotion = new UniformMaterial("ConveyorBelt", true)  // Important: The second parameter enables the surface
      {                                                                                      // motion. It has to be set to true in the constructor!
        SurfaceMotion = new Vector3F(0, 0, 1),   // The surface motion relative to the object.
      };
      conveyorBelt.Material = materialWithSurfaceMotion;

      // ----- Distribute a few dynamic spheres and boxes across the landscape.
      SphereShape sphereShape = new SphereShape(0.5f);
      for (int i = 0; i < 10; i++)
      {
        Vector3F position = RandomHelper.Random.NextVector3F(-15, 15);
        position.Y = 20;

        AddBody(simulation, "Sphere" + i, new Pose(position), sphereShape, MotionType.Dynamic);
      }

      BoxShape boxShape = new BoxShape(1, 1, 1);
      for (int i = 0; i < 10; i++)
      {
        Vector3F position = RandomHelper.Random.NextVector3F(-15, 15);
        position.Y = 20;

        AddBody(simulation, "Box" + i, new Pose(position), boxShape, MotionType.Dynamic);
      }

    }
Beispiel #7
0
    private MeshNode CreateMeshNode(IEnumerable<Submesh> submeshes, Color color)
    {
      var mesh = new Mesh();
      mesh.Submeshes.AddRange(submeshes);

      var material = new Material();
      BasicEffectBinding defaultEffectBinding = new BasicEffectBinding(GraphicsService, null)
      {
        LightingEnabled = true,
        TextureEnabled = false,
        VertexColorEnabled = false
      };
      defaultEffectBinding.Set("DiffuseColor", color.ToVector4());
      defaultEffectBinding.Set("SpecularColor", new Vector3(1, 1, 1));
      defaultEffectBinding.Set("SpecularPower", 100f);
      material.Add("Default", defaultEffectBinding);

      var triangleMesh = mesh.ToTriangleMesh();
      var shape = new TriangleMeshShape(triangleMesh);
      var aabb = shape.GetAabb();
      mesh.BoundingShape = new TransformedShape(
        new GeometricObject(
          new BoxShape(aabb.Extent),
          new Pose(aabb.Center)));

      mesh.Materials.Add(material);
      foreach (var submesh in mesh.Submeshes)
        submesh.MaterialIndex = 0;

      return new MeshNode(mesh);
    }
Beispiel #8
0
    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);
      }
    }
Beispiel #9
0
    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)); 
    }
Beispiel #10
0
    public MeshFromModelSample(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);

      // Use content pipeline to load a model.
      var bowlModelNode = ContentManager.Load<ModelNode>("Bowl");

      // Get mesh of the imported model.
      var meshNode = bowlModelNode.GetDescendants().OfType<MeshNode>().First();

      // Extract the triangle mesh from the DigitalRune Graphics Mesh instance. 
      // Note: XNA graphics use clockwise winding for triangle front sides and DigitalRune Physics uses
      // counter-clockwise winding for front sides. FromModel() automatically flips the 
      // winding order. 
      TriangleMesh mesh = MeshHelper.ToTriangleMesh(meshNode.Mesh);
      // Apply the transformation of the mesh node.
      mesh.Transform(meshNode.PoseWorld * Matrix44F.CreateScale(meshNode.ScaleWorld));

      // Note: To convert an XNA Model instance to a triangle mesh you can use:
      //TriangleMesh mesh = TriangleMesh.FromModel(bowlModel);

      // Meshes are usually "one-sided" (objects can pass through the backside of the triangles)!
      // If you need to reverse the triangle winding order, use this:
      // Reverse winding order:
      //for (int i = 0; i < mesh.NumberOfTriangles; i++)
      //{
      //  var dummy = mesh.Indices[i * 3 + 1];
      //  mesh.Indices[i * 3 + 1] = mesh.Indices[i * 3 + 2];
      //  mesh.Indices[i * 3 + 2] = dummy;
      //}

      // Create a collision shape that uses the mesh.
      TriangleMeshShape meshShape = new TriangleMeshShape(mesh);

      // Meshes are usually "one-sided" and objects moving into a backside can move through the
      // mesh. Objects are only stopped if they approach from the front. If IsTwoSided is set,
      // objects are blocked from both sides. 
      meshShape.IsTwoSided = 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!)
      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,
      };

      // Optional: The partition will be automatically built when needed. For static meshes it is
      // built only once when it is needed for the first time. Building the AABB tree can take a 
      // few seconds for very large meshes.
      // By calling Update() manually we can force the partition to be built right now:
      //triangleMeshShape.Partition.Update(false);
      // We could also call this method in a background thread while the level is loading. Or,
      // we can build the triangle mesh and the AABB tree in the XNA content pipeline and avoid the
      // building of the tree at runtime (see Sample 33).

      // Create a static rigid body with the mesh shape.
      // 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).
      var bowlBody = new RigidBody(meshShape, new MassFrame(), null)
      {
        Name = "Bowl",
        Pose = new Pose(new Vector3F()),
        MotionType = MotionType.Static
      };
      Simulation.RigidBodies.Add(bowlBody);

      // Add a dynamic sphere.
      Shape sphereShape = new SphereShape(0.4f);
      RigidBody sphere = new RigidBody(sphereShape)
      {
        Name = "Sphere",
        Pose = new Pose(new Vector3F(0, 10, 0)),
      };
      Simulation.RigidBodies.Add(sphere);
    }
Beispiel #11
0
    public TriangleMeshSample(Microsoft.Xna.Framework.Game game)
      : base(game)
    {
      // Add basic force effects.
      Simulation.ForceEffects.Add(new Gravity());
      Simulation.ForceEffects.Add(new Damping());

      // The triangle mesh could be loaded from a file, such as an XNA Model.
      // In this example will use the same height field as in Sample11 and convert
      // the shape into a triangle mesh.
      var numberOfSamplesX = 20;
      var numberOfSamplesZ = 20;
      var heights = new float[numberOfSamplesX * numberOfSamplesZ];
      for (int z = 0; z < numberOfSamplesZ; z++)
        for (int x = 0; x < numberOfSamplesX; x++)
          heights[z * numberOfSamplesX + x] = (float)(Math.Cos(z / 2f) * Math.Sin(x / 2f) * 5f + 5f);
      HeightField heightField = new HeightField(0, 0, 100, 100, heights, numberOfSamplesX, numberOfSamplesZ);

      // Convert the height field to a triangle mesh.
      ITriangleMesh mesh = heightField.GetMesh(0.01f, 3);

      // Create a shape for the triangle mesh.
      TriangleMeshShape triangleMeshShape = new TriangleMeshShape(mesh);

      // 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 CompressedAabbTree
      {
        // 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: The partition will be automatically built when needed. For static meshes it is
      // built only once when it is needed for the first time. Building the AABB tree can take a 
      // few seconds for very large meshes.
      // By calling Update() manually we can force the partition to be built right now:
      //triangleMeshShape.Partition.Update(false);
      // We could also call this method in a background thread while the level is loading. Or,
      // we can build the triangle mesh and the AABB tree in the XNA content pipeline and avoid the
      // building of the tree at runtime (see Sample 33).

      // Create a static rigid body using the shape and add it to the simulation.
      // 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).
      var ground = new RigidBody(triangleMeshShape, new MassFrame(), null)
      {
        Pose = new Pose(new Vector3F(-50, 0, -50f)),
        MotionType = MotionType.Static,
      };
      Simulation.RigidBodies.Add(ground);

      // 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);
      }
    }
Beispiel #12
0
    /// <summary>
    /// Converts mesh content to model content with a <see cref="TriangleMeshShape"/>.
    /// </summary>
    /// <param name="input">The root node content.</param>
    /// <param name="context">Context for the specified processor.</param>
    /// <returns>The <see cref="Shape"/>.</returns>
    public override DRModelNodeContent Process(NodeContent input, ContentProcessorContext context)
    {
      // ----- Process Model
      var model = base.Process(input, context);

      // ----- Extract Triangles
      var triangleMesh = new TriangleMesh();

      // The input node is usually a tree of nodes. We need to collect all MeshContent nodes
      // in the tree. The DigitalRune Helper library provides a TreeHelper that can be used 
      // to traverse trees using LINQ.
      // The following returns an IEnumerable that returns all nodes of the tree.
      var nodes = TreeHelper.GetSubtree(input, n => n.Children);

      // We only need nodes of type MeshContent.
      var meshes = nodes.OfType<MeshContent>();

      foreach (var mesh in meshes)
      {
        // Apply any transformations to vertices.
        Matrix transform = mesh.AbsoluteTransform;
        for (int i = 0; i < mesh.Positions.Count; i++)
          mesh.Positions[i] = Vector3.Transform(mesh.Positions[i], transform);

        // Extract triangles from submeshes.
        foreach (var geometry in mesh.Geometry)
        {
          int numberOfTriangles = geometry.Indices.Count / 3;
          for (int i = 0; i < numberOfTriangles; i++)
          {
            int index0 = geometry.Indices[3 * i + 0];
            int index1 = geometry.Indices[3 * i + 2]; // Note: DigitalRune Geometry uses a different winding
            int index2 = geometry.Indices[3 * i + 1]; // order. Therefore, the indices need to be swapped.

            Vector3F vertex0 = (Vector3F)geometry.Vertices.Positions[index0];
            Vector3F vertex1 = (Vector3F)geometry.Vertices.Positions[index1];
            Vector3F vertex2 = (Vector3F)geometry.Vertices.Positions[index2];

            triangleMesh.Add(new Triangle(vertex0, vertex1, vertex2), false, Numeric.EpsilonF, true);
          }
        }
      }

      // Remove duplicate vertices.
      triangleMesh.WeldVertices();

      // ----- Create TriangleMeshShape
      // Create a TriangleMeshShape that can be used for collision detection.
      // Note: 
      // - Contact-welding is enabled to improve the results of the collision detection.
      // - A CompressedAabbTree is used for spatial partitioning to speed up collision detection.
      var triangleMeshShape = new TriangleMeshShape(triangleMesh, true, new CompressedAabbTree());

      // Export the ModelNode together with the TriangleMeshShape.
      model.UserData = triangleMeshShape;
      return model;
    }
Beispiel #13
0
    private void CreateRigidBody()
    {
      var triangleMesh = new TriangleMesh();

      foreach (var meshNode in _modelNode.GetSubtree().OfType<MeshNode>())
      {
        // Extract the triangle mesh from the DigitalRune Graphics Mesh instance. 
        var subTriangleMesh = new TriangleMesh();
        foreach (var submesh in meshNode.Mesh.Submeshes)
        {
          submesh.ToTriangleMesh(subTriangleMesh);
        }

        // Apply the transformation of the mesh node.
        subTriangleMesh.Transform(meshNode.PoseWorld * Matrix44F.CreateScale(meshNode.ScaleWorld));

        // Combine into final triangle mesh.
        triangleMesh.Add(subTriangleMesh);
      }

      // Create a collision shape that uses the mesh.
      var triangleMeshShape = new TriangleMeshShape(triangleMesh);

      // 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 CompressedAabbTree
      {
        // 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,
      };

      _rigidBody = new RigidBody(triangleMeshShape, new MassFrame(), null)
      {
        Pose = _pose,
        Scale = _scale,
        MotionType = MotionType.Static
      };

      // Add rigid body to physics simulation and model to scene.
      var simulation = _services.GetInstance<Simulation>();
      simulation.RigidBodies.Add(_rigidBody);
    }
Beispiel #14
0
        public void Validate6()
        {
            GlobalSettings.ValidationLevel = 0xff;
              var scene = new Scene();
              // This is allowed.
              var n = new TestSceneNode { Shape = Shape.Empty };
              scene.Children.Add(n);

              // Invalid changes of already added node:
              var mesh = new TriangleMesh();
              mesh.Add(new Triangle(new Vector3F(1), new Vector3F(2), new Vector3F(3)));
              mesh.Add(new Triangle(new Vector3F(4), new Vector3F(float.NaN, 5, 5), new Vector3F(6)));
              var meshShape = new TriangleMeshShape(mesh);
              Assert.Throws<GraphicsException>(() => n.Shape = meshShape);
        }
Beispiel #15
0
    protected override void OnLoad()
    {
      // Add rigid bodies to simulation.
      var simulation = _services.GetInstance<Simulation>();

      // ----- Add a ground plane.
      AddBody(simulation, "GroundPlane", Pose.Identity, new PlaneShape(Vector3F.UnitY, 0), MotionType.Static);

      // ----- Create a height field.
      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++)
        {
          if (x == 0 || z == 0 || x == 19 || z == 19)
          {
            samples[z * numberOfSamplesX + x] = -1;
          }
          else
          {
            samples[z * numberOfSamplesX + x] = 1.0f + (float)(Math.Cos(z / 2f) * Math.Sin(x / 2f) * 1.0f);
          }
        }
      }
      HeightField heightField = new HeightField(0, 0, 120, 120, samples, numberOfSamplesX, numberOfSamplesZ);
      //heightField.UseFastCollisionApproximation = true;
      AddBody(simulation, "HeightField", new Pose(new Vector3F(10, 0, 20)), heightField, MotionType.Static);

      // ----- Create rubble on the floor (small random objects on the floor).
      for (int i = 0; i < 60; i++)
      {
        Vector3F position = new Vector3F(RandomHelper.Random.NextFloat(-5, 5), 0, RandomHelper.Random.NextFloat(10, 20));
        QuaternionF orientation = RandomHelper.Random.NextQuaternionF();
        BoxShape shape = new BoxShape(RandomHelper.Random.NextVector3F(0.05f, 0.5f));
        AddBody(simulation, "Stone" + i, new Pose(position, orientation), shape, MotionType.Static);
      }

      // ----- Slopes with different tilt angles.
      // Create a loop.
      Vector3F slopePosition = new Vector3F(-20, -0.25f, -5);
      BoxShape slopeShape = new BoxShape(8, 0.5f, 2);
      for (int i = 1; i < 33; i++)
      {
        Matrix33F oldRotation = Matrix33F.CreateRotationX((i - 1) * MathHelper.ToRadians(10));
        Matrix33F rotation = Matrix33F.CreateRotationX(i * MathHelper.ToRadians(10));

        slopePosition += (oldRotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2
                         + (rotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2;

        AddBody(simulation, "Loop" + i, new Pose(slopePosition, rotation), slopeShape, MotionType.Static);
      }

      // Create an arched bridge.
      slopePosition = new Vector3F(-10, -2, -15);
      slopeShape = new BoxShape(8f, 0.5f, 5);
      for (int i = 1; i < 8; i++)
      {
        Matrix33F oldRotation = Matrix33F.CreateRotationX(MathHelper.ToRadians(40) - (i - 1) * MathHelper.ToRadians(10));
        Matrix33F rotation = Matrix33F.CreateRotationX(MathHelper.ToRadians(40) - i * MathHelper.ToRadians(10));

        slopePosition += (oldRotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2
                         + (rotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2;
        Vector3F position = slopePosition - rotation * new Vector3F(0, slopeShape.WidthY / 2, 0);

        AddBody(simulation, "Bridge" + i, new Pose(position, rotation), slopeShape, MotionType.Static);
      }

      // ----- Create a mesh object.
      // We first build a composite shape out of several primitives and then convert the 
      // composite shape to a triangle mesh. (Just for testing.)
      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, true);
      meshShape.Partition = new AabbTree<int>() { BottomUpBuildThreshold = 0 };
      AddBody(simulation, "Mesh", new Pose(new Vector3F(-120, 0, 20)), meshShape, MotionType.Static);

      // ----- Create a seesaw.
      var seesawBase = AddBody(simulation, "SeesawBase", new Pose(new Vector3F(15, 0.5f, 0)), new BoxShape(0.2f, 1, 6), MotionType.Static);
      var seesaw = AddBody(simulation, "Seesaw", new Pose(new Vector3F(16, 1.05f, 0)), new BoxShape(15, 0.1f, 6), MotionType.Dynamic);
      seesaw.MassFrame.Mass = 500;
      seesaw.CanSleep = false;

      // Connect seesaw using a hinge joint.
      simulation.Constraints.Add(new HingeJoint
      {
        BodyA = seesaw,
        BodyB = seesawBase,
        AnchorPoseALocal = new Pose(new Vector3F(1.0f, 0, 0),
                                    new Matrix33F(0, 0, -1,
                                                  0, 1, 0,
                                                  1, 0, 0)),
        AnchorPoseBLocal = new Pose(new Vector3F(0, 0.5f, 0),
                                    new Matrix33F(0, 0, -1,
                                                  0, 1, 0,
                                                  1, 0, 0)),
        CollisionEnabled = false,
      });


      // ----- Distribute a few dynamic spheres and boxes across the landscape.
      SphereShape sphereShape = new SphereShape(0.5f);
      for (int i = 0; i < 40; i++)
      {
        Vector3F position = RandomHelper.Random.NextVector3F(-60, 60);
        position.Y = 10;
        AddBody(simulation, "Sphere" + i, new Pose(position), sphereShape, MotionType.Dynamic);
      }

      BoxShape boxShape = new BoxShape(1.0f, 1.0f, 1.0f);
      for (int i = 0; i < 40; i++)
      {
        Vector3F position = RandomHelper.Random.NextVector3F(-60, 60);
        position.Y = 1;
        AddBody(simulation, "Box" + i, new Pose(position), boxShape, MotionType.Dynamic);
      }
    }
Beispiel #16
0
        public void OrthographicViewVolumeTest()
        {
            var s = new OrthographicViewVolume(-1, 0, -1, 0, 0.1f, 10);
              var v0 = s.GetVolume(0.001f, 10);

              var m = s.GetMesh(0.001f, 10);
              var s1 = new TriangleMeshShape(m);
              var v1 = m.GetVolume();

              Assert.IsTrue(Numeric.AreEqual(v0, v1, 0.01f * (1 + v0)));  // 1% error is allowed.
        }
Beispiel #17
0
    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.
    }
Beispiel #18
0
        public void PerspectiveViewVolumeTest()
        {
            var s = new PerspectiveViewVolume(0.4f, 2, 0.1f, 1f);
              var v0 = s.GetVolume(0.001f, 10);

              var m = s.GetMesh(0.001f, 10);
              var s1 = new TriangleMeshShape(m);
              var v1 = s1.GetVolume(0.0001f, 10);

              Assert.IsTrue(Numeric.AreEqual(v0, v1, 0.01f * (1 + v0)));  // 1% error is allowed.
        }
Beispiel #19
0
    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);
    }
Beispiel #20
0
        public void TriangleMeshShapeTest()
        {
            var s = new CylinderShape(1, 2);
              var v0 = s.GetVolume(0.001f, 10);

              var m = s.GetMesh(0.001f, 10);
              var s1 = new TriangleMeshShape(m);
              var v1 = m.GetVolume();

              Assert.IsTrue(Numeric.AreEqual(v0, v1, 0.01f * (1 + v0)));  // 1% error is allowed.
        }
Beispiel #21
0
    public RollingSphereSample(Microsoft.Xna.Framework.Game game)
      : base(game)
    {
      // To demonstrate the problems with triangle meshes we increase the gravity and let a
      // sphere roll over a curved surface.

      // Add basic force effects.
      Simulation.ForceEffects.Add(new Gravity { Acceleration = new Vector3F(0, -30, 0) });   // Higher gravity to make the problem more visible.
      Simulation.ForceEffects.Add(new Damping());

      // Use the custom contact filter to improve sphere contacts.
      _sphereContactFilter = new SphereContactFilter();
      Simulation.CollisionDomain.CollisionDetection.ContactFilter = _sphereContactFilter;

      // The triangle mesh could be loaded from a file, such as an XNA Model.
      // In this example will create a height field and convert the height field into a triangle mesh.
      var numberOfSamplesX = 60;
      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.Sin(x / 6f) * 10f + 5f);

      var heightField = new HeightField(0, 0, 70, 30, samples, numberOfSamplesX, numberOfSamplesZ);

      // Convert the height field to a triangle mesh.
      ITriangleMesh mesh = heightField.GetMesh(0.01f, 3);

      // Create a shape for the triangle mesh.
      _triangleMeshShape = new TriangleMeshShape(mesh);

      // Enable contact welding. And set the welding limit to 1 for maximal effect.
      _triangleMeshShape.EnableContactWelding = true;
      _originalWeldingLimit = TriangleMeshAlgorithm.WeldingLimit;
      TriangleMeshAlgorithm.WeldingLimit = 1;

      // 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 CompressedAabbTree() { BottomUpBuildThreshold = 0 };

      // Create a static rigid body using the shape and add it to the simulation.
      // 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).
      var ground = new RigidBody(_triangleMeshShape, new MassFrame(), null)
      {
        Pose = new Pose(new Vector3F(-34, 0, -40f)),
        MotionType = MotionType.Static,
      };
      Simulation.RigidBodies.Add(ground);

      SphereShape sphereShape = new SphereShape(0.5f);
      _sphere = new RigidBody(sphereShape);
      Simulation.RigidBodies.Add(_sphere);

      _enableSmoothMovement = true;
      _timeUntilReset = TimeSpan.Zero;
    }
Beispiel #22
0
    // Gets neighbor normal and dot product of normals for contact welding.
    private static void GetNeighborNormal(
      int triangleIndex, Triangle triangle, Vector3F contactPositionOnTriangle, Vector3F triangleNormal, 
      TriangleMeshShape triangleMeshShape, Pose poseA, Vector3F scaleA, 
      out Vector3F neighborNormal, out float triangleDotNeighbor)
    {
      // Get barycentric coordinates of contact position.
      float u, v, w;
      // TODO: GetBaryCentricFromPoint computes the triangle normal, which we already know - optimize.
      GeometryHelper.GetBarycentricFromPoint(triangle, contactPositionOnTriangle, out u, out v, out w);

      // Find neighbor triangle normal.
      // If we do not find a neighbor, we assume the neighbor has the same normal.
      neighborNormal = triangleNormal;
      triangleDotNeighbor = float.MaxValue;

      // TODO: Optimize: We could trade memory for performance and store the precomputed triangle normals.

      // If one coordinate is near 0, the contact is near an edge.
      if (u < 0.05f || v < 0.05f || w < 0.05f)
      {
        if (u < 0.05f)
        {
          int neighborIndex = triangleMeshShape.TriangleNeighbors[triangleIndex * 3 + 0];
          if (neighborIndex >= 0)
          {
            Triangle neighbor = triangleMeshShape.Mesh.GetTriangle(neighborIndex);
            var newNeighborNormal = poseA.ToWorldDirection(neighbor.Normal / scaleA);
              // TODO: Optimize: neighbor.Normal normalizes the normal but we denormalize it with 1/scaleA.
            if (newNeighborNormal.TryNormalize())
            {
              float dot = Vector3F.Dot(triangleNormal, newNeighborNormal);
              if (dot < triangleDotNeighbor)
              {
                triangleDotNeighbor = dot;
                neighborNormal = newNeighborNormal;
              }
            }
          }
        }
        if (v < 0.05f)
        {
          int neighborIndex = triangleMeshShape.TriangleNeighbors[triangleIndex * 3 + 1];
          if (neighborIndex >= 0)
          {
            Triangle neighbor = triangleMeshShape.Mesh.GetTriangle(neighborIndex);
            var newNeighborNormal = poseA.ToWorldDirection(neighbor.Normal / scaleA);
            if (newNeighborNormal.TryNormalize())
            {
              float dot = Vector3F.Dot(triangleNormal, newNeighborNormal);
              if (dot < triangleDotNeighbor)
              {
                triangleDotNeighbor = dot;
                neighborNormal = newNeighborNormal;
              }
            }
          }
        }
        if (w < 0.05f)
        {
          int neighborIndex = triangleMeshShape.TriangleNeighbors[triangleIndex * 3 + 2];
          if (neighborIndex >= 0)
          {
            Triangle neighbor = triangleMeshShape.Mesh.GetTriangle(neighborIndex);
            var newNeighborNormal = poseA.ToWorldDirection(neighbor.Normal / scaleA);
            if (newNeighborNormal.TryNormalize())
            {
              float dot = Vector3F.Dot(triangleNormal, newNeighborNormal);
              if (dot < triangleDotNeighbor)
              {
                triangleDotNeighbor = dot;
                neighborNormal = newNeighborNormal;
              }
            }
          }
        }
      }
    }