Пример #1
0
        // This method is copied from HeightField.cs and modified to return the normal (not normalized!).
        private static void GetHeight(HeightField heightField, float x, float z, out float height, out Vector3F normal, out int featureIndex)
        {
            int arrayLengthX = heightField.NumberOfSamplesX;
              int arrayLengthZ = heightField.NumberOfSamplesZ;

              // x and z without origin.
              var xo = (x - heightField.OriginX);
              var zo = (z - heightField.OriginZ);

              // Compute cell indices.
              float cellWidthX = heightField.WidthX / (arrayLengthX - 1);
              float cellWidthZ = heightField.WidthZ / (arrayLengthZ - 1);

              int indexX = Math.Min((int)(xo / cellWidthX), arrayLengthX - 2);
              int indexZ = Math.Min((int)(zo / cellWidthZ), arrayLengthZ - 2);

              // Determine which triangle we need.
              float xRelative = xo / cellWidthX - indexX;
              float zRelative = zo / cellWidthZ - indexZ;
              Debug.Assert(Numeric.IsGreaterOrEqual(xRelative, 0) && Numeric.IsLessOrEqual(xRelative, 1));
              Debug.Assert(Numeric.IsGreaterOrEqual(zRelative, 0) && Numeric.IsLessOrEqual(zRelative, 1));
              bool useSecondTriangle = (xRelative + zRelative) > 1;  // The diagonal is where xRel + zRel == 1.

              var triangle = heightField.GetTriangle(indexX, indexZ, useSecondTriangle);

              // Store heights of the triangle vertices.
              float height0 = triangle.Vertex0.Y;
              float height1 = triangle.Vertex1.Y;
              float height2 = triangle.Vertex2.Y;

              // Get barycentric coordinates (relative to triangle in xz plane).
              float u, v, w;

              // Project triangle into xz plane.
              triangle.Vertex0.Y = 0;
              triangle.Vertex1.Y = 0;
              triangle.Vertex2.Y = 0;
              GeometryHelper.GetBarycentricFromPoint(triangle, new Vector3F(x, 0, z), out u, out v, out w);

              featureIndex = (indexZ * (arrayLengthX - 1) + indexX) * 2;
              if (useSecondTriangle)
            featureIndex++;

            #if DEBUG
              float e = Numeric.EpsilonF * 10;
              Debug.Assert((Numeric.IsGreaterOrEqual(u, 0, e) && Numeric.IsGreaterOrEqual(v, 0, e)) && Numeric.IsLessOrEqual(u + v, 1, e));
            #endif

              // Return height (computed with barycentric coordinates).
              height = u * height0 + v * height1 + w * height2;

              // Correct triangle vertex heights because triangle is an out parameter.
              triangle.Vertex0.Y = height0;
              triangle.Vertex1.Y = height1;
              triangle.Vertex2.Y = height2;

              normal = Vector3F.Cross(triangle.Vertex1 - triangle.Vertex0, triangle.Vertex2 - triangle.Vertex0);
        }
        /// <summary>
        /// Clamps a road path to the terrain height.
        /// </summary>
        /// <param name="road">The path that represents the road.</param>
        /// <param name="terrain">The terrain represented by a <see cref="HeightField"/>.</param>
        /// <remarks>
        /// The y position of each path key is set to the terrain height at the xz position.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="terrain"/> is <see langword="null"/>.
        /// </exception>
        public static void ClampRoadToTerrain(Path3F road, HeightField terrain)
        {
            if (road == null)
            return;
              if (terrain == null)
            throw new ArgumentNullException("terrain");

              foreach (var key in road)
              {
            Vector3F position = key.Point;
            float height = terrain.GetHeight(position.X, position.Z);
            if (!Numeric.IsNaN(height))
            {
              position.Y = height;
              key.Point = position;
            }
              }
        }
Пример #3
0
    public DebugRendererSample(Microsoft.Xna.Framework.Game game)
      : base(game)
    {
      SampleFramework.IsMouseVisible = false;
      var delegateGraphicsScreen = new DelegateGraphicsScreen(GraphicsService)
      {
        RenderCallback = Render,
      };
      GraphicsService.Screens.Insert(0, delegateGraphicsScreen);

      // Add a custom game object which controls the camera.
      _cameraObject = new CameraObject(Services);
      GameObjectService.Objects.Add(_cameraObject);

      // Load a sprite font.
      var spriteFont = UIContentManager.Load<SpriteFont>("UI Themes/BlendBlue/Default");

      // Create a new debug renderer.
      _debugRenderer = new DebugRenderer(GraphicsService, spriteFont)
      {
        DefaultColor = Color.White,
      };

      // A normal XNA model.
      _xnaModel = ContentManager.Load<Model>("Saucer3/saucer");

      // A DigitalRune model.
      _modelNode = ContentManager.Load<ModelNode>("Dude/Dude").Clone();
      _modelNode.PoseLocal = new Pose(new Vector3F(6, 0, -7));

      // Create a geometric object with a height field shape.
      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++)
          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);
      _geometricObject = new GeometricObject(heightField, new Pose(new Vector3F(5, 0, -5)))
      {
        Scale = new Vector3F(0.01f, 0.05f, 0.02f),
      };
    }
Пример #4
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);
    }
Пример #5
0
    // Creates a test scene with a lot of randomly placed objects.
    internal static void CreateScene(ServiceContainer services, ContentManager content, DeferredGraphicsScreen graphicsScreen)
    {
      var gameObjectService = services.GetInstance<IGameObjectService>();
      var graphicsService = services.GetInstance<IGraphicsService>();

      gameObjectService.Objects.Add(new DynamicSkyObject(services, true, false, true)
      {
        EnableAmbientLight = false, // Disable ambient light of sky to make shadows more visible.
        EnableCloudShadows = false
      });

      gameObjectService.Objects.Add(new GroundObject(services));
      gameObjectService.Objects.Add(new DynamicObject(services, 1));
      gameObjectService.Objects.Add(new DynamicObject(services, 2));
      gameObjectService.Objects.Add(new DynamicObject(services, 3));
      gameObjectService.Objects.Add(new DynamicObject(services, 5));
      gameObjectService.Objects.Add(new DynamicObject(services, 6));
      gameObjectService.Objects.Add(new DynamicObject(services, 7));
      gameObjectService.Objects.Add(new ObjectCreatorObject(services));
      gameObjectService.Objects.Add(new LavaBallsObject(services));

      var random = new Random();

      // Spheres
      var sphereMesh = SampleHelper.CreateMesh(content, graphicsService, new SphereShape(1));
      for (int i = 0; i < 100; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-100, 100), random.NextFloat(0, 3), random.NextFloat(-100, 100));
        float scale = random.NextFloat(0.5f, 3f);
        var meshNode = new MeshNode(sphereMesh)
        {
          PoseLocal = new Pose(position),
          ScaleLocal = new Vector3F(scale),
          IsStatic = true,
        };
        graphicsScreen.Scene.Children.Add(meshNode);
      }

      // Boxes
      var boxMesh = SampleHelper.CreateMesh(content, graphicsService, new BoxShape(1, 1, 1));
      for (int i = 0; i < 100; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-100, 100), random.NextFloat(0, 3), random.NextFloat(-100, 100));
        QuaternionF orientation = random.NextQuaternionF();
        Vector3F scale = random.NextVector3F(0.1f, 4f);
        var meshNode = new MeshNode(boxMesh)
        {
          PoseLocal = new Pose(position, orientation),
          ScaleLocal = scale,
          IsStatic = true,
        };
        graphicsScreen.Scene.Children.Add(meshNode);
      }

      // Height field with smooth hills.
      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)
          {
            // 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 + (float)(Math.Cos(z / 2f) * Math.Sin(x / 2f) * 1);
          }
        }
      }
      var heightField = new HeightField(0, 0, 20, 20, samples, numberOfSamplesX, numberOfSamplesZ);
      var heightFieldMesh = SampleHelper.CreateMesh(content, graphicsService, heightField);
      var heightFieldMeshNode = new MeshNode(heightFieldMesh)
      {
        PoseLocal = new Pose(new Vector3F(20, 0, -20)),
        ScaleLocal = new Vector3F(1, 2, 1),
        IsStatic = true,
      };
      graphicsScreen.Scene.Children.Add(heightFieldMeshNode);

      // Dudes.
      for (int i = 0; i < 10; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-20, 20), 0, random.NextFloat(-20, 20));
        Matrix33F orientation = Matrix33F.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi));
        gameObjectService.Objects.Add(new DudeObject(services) { Pose = new Pose(position, orientation) });
      }

      // Palm trees.
      for (int i = 0; i < 100; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-80, 80), 0, random.NextFloat(-100, 100));
        Matrix33F orientation = Matrix33F.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi));
        float scale = random.NextFloat(0.5f, 1.2f);
        gameObjectService.Objects.Add(new StaticObject(services, "PalmTree/palm_tree", scale, new Pose(position, orientation)));
      }

      // Rocks
      for (int i = 0; i < 100; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-80, 80), 1, random.NextFloat(-100, 100));
        QuaternionF orientation = RandomHelper.Random.NextQuaternionF();
        float scale = random.NextFloat(0.5f, 1.2f);
        gameObjectService.Objects.Add(new StaticObject(services, "Rock/rock_05", scale, new Pose(position, orientation)));
      }

      // Grass
      for (int i = 0; i < 100; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-20, 20), 0, random.NextFloat(-20, 20));
        Matrix33F orientation = Matrix33F.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi));
        float scale = random.NextFloat(0.5f, 1.2f);
        gameObjectService.Objects.Add(new StaticObject(services, "Grass/Grass", scale, new Pose(position, orientation)));
      }

      // More plants
      for (int i = 0; i < 100; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-20, 20), 0, random.NextFloat(-20, 20));
        Matrix33F orientation = Matrix33F.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi));
        float scale = random.NextFloat(0.5f, 1.2f);
        gameObjectService.Objects.Add(new StaticObject(services, "Parviflora/Parviflora", scale, new Pose(position, orientation)));
      }

      // "Skyscrapers"
      for (int i = 0; i < 20; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(90, 100), 0, random.NextFloat(-100, 100));
        Matrix33F orientation = Matrix33F.CreateRotationY(random.NextFloat(0, ConstantsF.TwoPi));
        Vector3F scale = new Vector3F(random.NextFloat(6, 20), random.NextFloat(10, 100), random.NextFloat(6, 20));
        var meshNode = new MeshNode(boxMesh)
        {
          PoseLocal = new Pose(position, orientation),
          ScaleLocal = scale,
          IsStatic = true,
          UserFlags = 1, // Mark the distant huge objects. Used in render callbacks in the CompositeShadowSample.
        };
        graphicsScreen.Scene.Children.Add(meshNode);
      }

      // "Hills"
      for (int i = 0; i < 20; i++)
      {
        Vector3F position = new Vector3F(random.NextFloat(-90, -100), 0, random.NextFloat(-100, 100));
        Vector3F scale = new Vector3F(random.NextFloat(10, 20), random.NextFloat(10, 30), random.NextFloat(10, 20));
        var meshNode = new MeshNode(sphereMesh)
        {
          PoseLocal = new Pose(position),
          ScaleLocal = scale,
          IsStatic = true,
          UserFlags = 1, // Mark the distant huge objects. Used in render callbacks in the CompositeShadowSample.
        };
        graphicsScreen.Scene.Children.Add(meshNode);
      }
    }
Пример #6
0
    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);
      }
    }
Пример #7
0
    //--------------------------------------------------------------
    #region Methods
    //--------------------------------------------------------------

    protected override void OnLoad()
    {
      // Get common services and game objects.
      _graphicsService = _services.GetInstance<IGraphicsService>();
      _graphicsScreen = _graphicsService.Screens.OfType<DeferredGraphicsScreen>().First();
      var content = _services.GetInstance<ContentManager>();
      var scene = _services.GetInstance<IScene>();
      _simulation = _services.GetInstance<Simulation>();
      var gameObjectService = _services.GetInstance<IGameObjectService>();
      _cameraObject = gameObjectService.Objects.OfType<CameraObject>().First();
      _previousCameraFar = _cameraObject.CameraNode.Camera.Projection.Far;

      // Create a new terrain.
      var terrain = new Terrain();

      // The terrain is made up of terrain tiles which can be loaded independently. 
      // Each terrain tile consists of height and normal textures which define the terrain
      // geometry and terrain layers which define the material (detail textures).
      // In this sample we create 2x2 tiles.
      _tiles = new Tile[2, 2];
      for (int row = 0; row < 2; row++)
      {
        for (int column = 0; column < 2; column++)
        {
          // Create a tile and add it to the terrain.
          // (The tile content is loaded later.)
          var terrainTile = new TerrainTile(_graphicsService)
          {
            CellSize = 1,   // The terrain has a resolution of 1 height sample per world space unit.
          };
          terrain.Tiles.Add(terrainTile);

          // Create a rigid body with a height field for collision detection and add
          // it to the simulation. (The height data is loaded later.)
          var heightField = new HeightField
          {
            Depth = 1,
            UseFastCollisionApproximation = false,
          };
          var rigidBody = new RigidBody(heightField, new MassFrame(), null)
          {
            MotionType = MotionType.Static,
            UserData = terrainTile,
          };
          _simulation.RigidBodies.Add(rigidBody);

          // Store the tile for use later in this sample.
          _tiles[row, column] = new Tile
          {
            TerrainTile = terrainTile,
            RigidBody = rigidBody,
          };
        }
      }

      // Create a terrain node which represents the terrain in the scene graph.
      // The terrain node is rendered by the TerrainRenderer (see DeferredGraphicsScreen).
      // The material used to render the terrain is customizable.  The material must specify 
      // the effects for the different render passes which we use in the DeferredGraphicsScreen 
      // ("ShadowMap", "GBuffer", "Material").
      // The prebuilt DigitalRune content contains standard terrain effects. However, you could
      // change the effects to change how the material is rendered.
      // We can create the material by loading a .drmat file. Or we can create the material in
      // code like this:
      var shadowMapEffect = content.Load<Effect>("DigitalRune/Terrain/TerrainShadowMap");
      var gBufferEffect = content.Load<Effect>("DigitalRune/Terrain/TerrainGBuffer");
      var materialEffect = content.Load<Effect>("DigitalRune/Terrain/TerrainMaterial");
      var material = new Material
      {
        { "ShadowMap", new EffectBinding(_graphicsService, shadowMapEffect, null, EffectParameterHint.Material) },
        { "GBuffer", new EffectBinding(_graphicsService, gBufferEffect, null, EffectParameterHint.Material) },
        { "Material", new EffectBinding(_graphicsService, materialEffect, null, EffectParameterHint.Material) }
      };
      TerrainNode = new TerrainNode(terrain, material)
      {
        // The terrain rendering uses clipmaps.
        // The clipmaps are updated by the TerrainClipmapRenderer (see DeferredGraphicsScreen)
        // when the camera moves.
        // The base clipmap contains the basic geometry info (height, normals, hole info).
        // It also determines the terrain mesh resolution.
        BaseClipmap =
        {
          CellsPerLevel = 128,
          NumberOfLevels = 6
        },
        // The detail clipmap contains the splatted detail textures (e.g. grass, rock, ...).
        // (The max texture size in XNA is 4096x4096. That means we can fit 9 clipmap levels
        // into a single texture.)
        DetailClipmap =
        {
          CellsPerLevel = 1365,
          NumberOfLevels = 9,
        },
      };
      scene.Children.Add(TerrainNode);

      // Load the height and normal maps which define the terrain geometry.
      InitializeHeightsAndNormals();

      // Set the clipmap cell sizes.
      InitializeClipmapCellSizes();

      // Create the terrain layers which define the detail textures (e.g. grass, rock, ...)
      InitializeTerrainLayers(content);

      // Special note for AMD GPUs:
      // If we want anisotropic filtering for the terrain, then we need to enable mipmaps for
      // AMD GPUs. NVIDIA and Intel can do anisotropic filtering without mipmaps.
      //TerrainNode.DetailClipmap.EnableMipMap = true;

      CreateGuiControls();
    }
Пример #8
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);
      }

    }
Пример #9
0
    //--------------------------------------------------------------
    #region Methods
    //--------------------------------------------------------------

    protected override void OnLoad()
    {
      // Get common services and game objects.
      _graphicsService = _services.GetInstance<IGraphicsService>();
      var content = _services.GetInstance<ContentManager>();
      var scene = _services.GetInstance<IScene>();
      _simulation = _services.GetInstance<Simulation>();
      var gameObjectService = _services.GetInstance<IGameObjectService>();
      _cameraObject = gameObjectService.Objects.OfType<CameraObject>().First();
      _previousCameraFar = _cameraObject.CameraNode.Camera.Projection.Far;

      // Create a new terrain.
      var terrain = new Terrain();
      _terrainTile = new TerrainTile(_graphicsService)
      {
        CellSize = 2,
      };
      terrain.Tiles.Add(_terrainTile);

      var shadowMapEffect = content.Load<Effect>("DigitalRune/Terrain/TerrainShadowMap");
      var gBufferEffect = content.Load<Effect>("DigitalRune/Terrain/TerrainGBuffer");
      var materialEffect = content.Load<Effect>("DigitalRune/Terrain/TerrainMaterial");
      var material = new Material
      {
        { "ShadowMap", new EffectBinding(_graphicsService, shadowMapEffect, null, EffectParameterHint.Material) },
        { "GBuffer", new EffectBinding(_graphicsService, gBufferEffect, null, EffectParameterHint.Material) },
        { "Material", new EffectBinding(_graphicsService, materialEffect, null, EffectParameterHint.Material) }
      };
      TerrainNode = new TerrainNode(terrain, material)
      {
        BaseClipmap =
        {
          CellsPerLevel = 128,
          NumberOfLevels = 6,
        },
        DetailClipmap =
        {
          CellsPerLevel = 1364,
          NumberOfLevels = 9,
        },
      };
      scene.Children.Add(TerrainNode);

      // Create a rigid body with a height field for collision detection.
      var heightField = new HeightField
      {
        Depth = 1,
        UseFastCollisionApproximation = false,
      };
      _rigidBody = new RigidBody(heightField, new MassFrame(), null)
      {
        MotionType = MotionType.Static,
        UserData = _terrainTile,
      };
      _simulation.RigidBodies.Add(_rigidBody);

      InitializeHeightsAndNormals();

      InitializeClipmapCellSizes();

      InitializeTerrainLayers(content);

      // Enable mipmaps when using anisotropic filtering on AMD graphics cards:
      //TerrainNode.DetailClipmap.EnableMipMap = true;

      CreateGuiControls();
    }
Пример #10
0
        private static void ClampHeightsToTriangle(HeightField terrain, Vector3F vertexA, Vector3F vertexB, Vector3F vertexC)
        {
            // This code is a like an unoptimized software rasterizer.
              // TODO: Optimize this (see software rasterizers).

              float originX = terrain.OriginX;
              float originZ = terrain.OriginZ;
              int numberOfSamplesX = terrain.NumberOfSamplesX;
              int numberOfSamplesZ = terrain.NumberOfSamplesZ;
              int numberOfCellsX = numberOfSamplesX - 1;
              int numberOfCellsZ = numberOfSamplesZ - 1;
              float widthX = terrain.WidthX;
              float cellSizeX = widthX / numberOfCellsX;
              float widthZ = terrain.WidthZ;
              float cellSizeZ = widthZ / numberOfCellsZ;
              float[] heights = terrain.Samples;

              // Get min and max indices (inclusive).
              float minX = Min(vertexA.X, vertexB.X, vertexC.X);
              float maxX = Max(vertexA.X, vertexB.X, vertexC.X);
              float minZ = Min(vertexA.Z, vertexB.Z, vertexC.Z);
              float maxZ = Max(vertexA.Z, vertexB.Z, vertexC.Z);

              Vector2F a = new Vector2F(vertexA.X, vertexA.Z);
              Vector2F b = new Vector2F(vertexB.X, vertexB.Z);
              Vector2F c = new Vector2F(vertexC.X, vertexC.Z);

              // Get min and max indices (inclusive).
              int indexXMin = Math.Max(0, (int)Math.Floor((minX - originX) / cellSizeX));
              int indexZMin = Math.Max(0, (int)Math.Floor((minZ - originZ) / cellSizeZ));
              int indexXMax = Math.Min(numberOfSamplesX - 1, (int)Math.Ceiling((maxX - originX) / cellSizeX));
              int indexZMax = Math.Min(numberOfSamplesZ - 1, (int)Math.Ceiling((maxZ - originZ) / cellSizeZ));

              // Values for the barycentric computation:
              Vector2F v0 = b - a;
              Vector2F v1 = c - a;
              float den = v0.X * v1.Y - v1.X * v0.Y;

              Parallel.For(indexZMin, indexZMax + 1, indexZ =>
              //for (int indexZ = indexZMin; indexZ <= indexZMax; indexZ++)
              {
            for (int indexX = indexXMin; indexX <= indexXMax; indexX++)
            {
              Vector2F p = new Vector2F(originX + cellSizeX * indexX, originZ + cellSizeZ * indexZ);

              // Get barycentric coordinates.
              Vector2F v2 = p - a;
              float v = (v2.X * v1.Y - v1.X * v2.Y) / den;
              float w = (v0.X * v2.Y - v2.X * v0.Y) / den;
              float u = 1.0f - v - w;

              var epsilonF = 0.0;
              if (u < -epsilonF || u > 1 + epsilonF)
            continue;
              if (v < -epsilonF || v > 1 + epsilonF)
            continue;
              if (w < -epsilonF || w > 1 + epsilonF)
            continue;

              float height = u * vertexA.Y + v * vertexB.Y + w * vertexC.Y;
              heights[indexZ * numberOfSamplesX + indexX] = height;
            }
              });
        }
Пример #11
0
    public PerformanceSample(Microsoft.Xna.Framework.Game game)
      : base(game)
    {
      // ----- Multithreading
      // If the scene contains many active (= moving objects), using multithreading is 
      // recommended. Multithreading does not improve very static scenes where most bodies
      // are not moving or scenes with very little bodies. 
      // If multithreading is enabled in the collision domain, the narrow phase algorithms
      // (which determines contacts) are executed in parallel.
      Simulation.CollisionDomain.EnableMultithreading = true;
      // If multithreading is enabled in the simulation, the simulation islands are solved
      // in parallel and a few other things run in parallel.
      Simulation.Settings.EnableMultithreading = true;

      // The processor affinity and the number of threads can be controlled with these properties:
      //Parallel.ProcessorAffinity = new[] { 4, 3, 5 };    // Threads use the cores 3, 4, 5.
      //Parallel.Scheduler = new WorkStealingScheduler(3); // Use 3 worker threads.
      // The default settings should be ok for most scenarios.

      // ----- Collision Detection Settings
      // We disable the flag FullContactSetPerFrame. If the flag is disabled, the collision
      // detection is faster because in the narrow phase some algorithms will compute only
      // one new contact between two touching bodies.
      // If the flag is enabled, the simulation is more stable because the narrow phase computes
      // more contacts per pair of touching bodies.
      Simulation.CollisionDomain.CollisionDetection.FullContactSetPerFrame = false;

      // ----- Physics Settings
      // If SynchronizeCollisionDomain is false, the collision detection is run only at the 
      // beginning of Simulation.Update(). If SynchronizeCollisionDomain is set, the collision 
      // detection is also performed at the end. This is necessary in case you need to make manual 
      // collision detection queries and need up-to-date collision detection info.
      // Disable this flag if you do not need it.
      Simulation.Settings.SynchronizeCollisionDomain = false;
      // The MinConstraintImpulse defines when the constraint solver will stop its iterative
      // process. A higher limit will make the solver stop earlier (=> faster, but less stable).
      Simulation.Settings.Constraints.MinConstraintImpulse = 0.0001f;
      // NumberOfConstraintIterations defines how many iterations the solver performs at max.
      // Values from 4 to 20 are normal. Use higher values if stable stacking is required.
      Simulation.Settings.Constraints.NumberOfConstraintIterations = 4;
      // Randomization of constraints takes a tiny bit of time and helps to make stacks and
      // complex scenes more stable. For simple scenes we can disable it.
      Simulation.Settings.Constraints.RandomizeConstraints = false;
      // Continuous collision detection cost a bit performance. We are faster if we disable it
      // but with disabled CCD balls (right mouse button) will fly through objects because of 
      // their high speed.
      Simulation.Settings.Motion.CcdEnabled = false;
      // If RemoveBodiesOutsideWorld is set, the simulation automatically removes bodies that 
      // leave the simulation (defined with Simulation.World). Disable it if not needed.
      Simulation.Settings.Motion.RemoveBodiesOutsideWorld = false;
      // TimeThreshold defines how fast bodies are deactivated. Normal values are 1 or 2 seconds.
      // We can set it to a low value, e.g. 0.5 s, for a very aggressive sleeping. The negative
      // effects of this are that bodies that are slowly falling over, can freeze in a tilted
      // position. 
      // You can also try to disable sleeping by setting TimeThreshold to float.MaxValue. But the
      // simulation will run significantly slower. You can run the PhysicsSample and compare the 
      // simulation times with enabled and disabled sleeping.
      Simulation.Settings.Sleeping.TimeThreshold = 0.5f;
      // FixedTimeStep defines the size of a single simulation step. Per default, the smallest
      // step is 1 / 60 s (60 fps). In some cases it is ok to use an even larger time step
      // like 1 / 30. But with large time steps stacks and walls will not be stable.
      Simulation.Settings.Timing.FixedTimeStep = 1.0f / 60.0f;
      // If the simulation gets complex the game will need more time to compute each frame.
      // If the game becomes very slow, Simulation.Update(elapsedTime) will be called with 
      // a large elapsedTime. If our frame rate drops to 30 fps, Simulation.Update(1/30) will
      // internally make 2 sub-time steps (if FixedTimeStep = 1/60). This could make the problem
      // worse and if we expect such a situation we should limit the number of sub steps to 1.
      // Then, if the game is running slowly, the physics simulation will run in slow motion -
      // but at least it will not freeze the game.
      Simulation.Settings.Timing.MaxNumberOfSteps = 1;

      // ----- Force Effects.
      // Using a low gravity is common trick to make the simulation more stable:
      Simulation.ForceEffects.Add(new Gravity { Acceleration = new Vector3F(0, -5, 0) });
      // Using high damping coefficients helps to make your simulation faster and more stable
      // because objects will come the rest much quicker. - But too high values can create a
      // very unrealistic damped body movement.
      Simulation.ForceEffects.Add(new Damping { LinearDamping = 0.3f, AngularDamping = 0.3f });

      // ----- Rigid Body Prototypes
      // Here we create 3 rigid bodies that will serve as templates for the new random bodies
      // that are created in Update().
      // We use the same material instance for all rigid bodies to avoid the creation of several 
      // material instances.
      var material = new UniformMaterial();
      _prototypes = new RigidBody[3];
      _prototypes[0] = new RigidBody(new SphereShape(0.5f), null, material);
      _prototypes[1] = new RigidBody(new CylinderShape(0.4f, 0.9f), null, material);
      _prototypes[2] = new RigidBody(new BoxShape(0.9f, 0.9f, 0.9f), null, material);

      // ----- Height Field
      // Create a height field.
      var numberOfSamplesX = 30;
      var numberOfSamplesZ = 30;
      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 / 2f) * Math.Sin(x / 2f) * 3f + 5f);
      var heightField = new HeightField(0, 0, 100, 100, 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.
      // The mass of static rigid bodies is not relevant, therefore we use a default 
      // mass frame instance as the second constructor parameter. If we do not specify
      // the mass frame, the physics library will try to compute a suitable mass frame
      // which can take some time for large meshes.
      RigidBody landscape = new RigidBody(heightField, new MassFrame(), material)
      {
        Pose = new Pose(new Vector3F(-50, 0, -50f)),
        MotionType = MotionType.Static,
      };
      Simulation.RigidBodies.Add(landscape);
    }
Пример #12
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);
      }
    }
Пример #13
0
    // Modify the terrain height values to add the road to the terrain.
    private void ClampTerrainToRoad()
    {
      // We have to manipulate the height and normal texture of each tile which contains the road.
      foreach (var tile in _terrainObject.TerrainNode.Terrain.Tiles)
      {
        // Get the current height texture of the tile and extract the heights into a float array.
        var heightTexture = tile.HeightTexture;
        float[] heights = TerrainHelper.GetTextureLevelSingle(heightTexture, 0);

        // Create a temporary height field.
        var heightField = new HeightField(
          tile.OriginX,
          tile.OriginZ,
          tile.WidthX,
          tile.WidthZ,
          heights,
          heightTexture.Width,
          heightTexture.Height);

        // Change the height values of the height field.
        TerrainRoadLayer.ClampTerrainToRoad(heightField, _roadPath, 8, 30, 10, 0.1f);

        // Rebuild the height texture.
        TerrainHelper.CreateHeightTexture(
          GraphicsService.GraphicsDevice,
          heights,
          heightTexture.Width,
          heightTexture.Height,
          false,
          ref heightTexture);

        // Rebuild the normal texture.
        var normalTexture = tile.NormalTexture;
        TerrainHelper.CreateNormalTexture(
          GraphicsService.GraphicsDevice,
          heights,
          heightTexture.Width,
          heightTexture.Height,
          tile.CellSize,
          false,
          ref normalTexture);

        // Get rigid body that represents this tile.
        var rigidBody = Simulation.RigidBodies.First(body => body.UserData == tile);

        // Update the height values of the collision detection height field.
        ((HeightField)rigidBody.Shape).SetSamples(heights, heightTexture.Width, heightTexture.Height);
      }
    }
Пример #14
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);
      }
    }
Пример #15
0
        public static void ClampTerrainToRoad(HeightField terrain, Path3F road,
            float defaultWidth, float defaultSideFalloff,
            int maxNumberOfIterations, float tolerance)
        {
            if (terrain == null)
            throw new ArgumentNullException("terrain");
              if (road == null)
            throw new ArgumentNullException("road");

              // Compute list of line segments. (2 points per line segment!)
              var flattenedPoints = new List<Vector3F>();
              road.Flatten(flattenedPoints, maxNumberOfIterations, tolerance);

              // Abort if path is empty.
              int numberOfLineSegments = flattenedPoints.Count / 2;
              if (numberOfLineSegments <= 0)
            return;

              // Compute accumulated lengths. (One entry for each entry in flattenedPoints.)
              float[] accumulatedLengths = new float[flattenedPoints.Count];
              accumulatedLengths[0] = 0;
              for (int i = 1; i < flattenedPoints.Count; i += 2)
              {
            Vector3F previous = flattenedPoints[i - 1];
            Vector3F current = flattenedPoints[i];
            float length = (current - previous).Length;

            accumulatedLengths[i] = accumulatedLengths[i - 1] + length;
            if (i + 1 < flattenedPoints.Count)
              accumulatedLengths[i + 1] = accumulatedLengths[i];
              }

              // Create a mapping between accumulatedLength and the path keys.
              // (accumulatedLength --> key)
              var pathLengthsAndKeys = new List<Pair<float, TerrainRoadPathKey>>();
              {
            int index = 0;
            foreach (var key in road)
            {
              Vector3F position = key.Point;
              var roadKey = key as TerrainRoadPathKey;
              if (roadKey == null)
              {
            roadKey = new TerrainRoadPathKey
            {
              Point = key.Point,
              Width = defaultWidth,
              SideFalloff = defaultSideFalloff,
            };
              }

              for (; index < flattenedPoints.Count; index++)
              {
            if (Vector3F.AreNumericallyEqual(position, flattenedPoints[index]))
            {
              pathLengthsAndKeys.Add(new Pair<float, TerrainRoadPathKey>(accumulatedLengths[index], roadKey));
              break;
            }

            bool isLastLineSegment = (index + 2 == flattenedPoints.Count);
            if (!isLastLineSegment)
              index++;
              }

              index++;
            }
              }

              // Create a list of interpolated road widths and side falloffs. (One entry for each entry in flattenedPoints.)
              var halfWidths = new float[flattenedPoints.Count];
              var sideFalloffs = new float[flattenedPoints.Count];
              int previousKeyIndex = 0;
              var previousKey = pathLengthsAndKeys[0];
              var nextKey = pathLengthsAndKeys[1];
              halfWidths[0] = 0.5f * pathLengthsAndKeys[0].Second.Width;
              sideFalloffs[0] = pathLengthsAndKeys[0].Second.SideFalloff;
              for (int i = 1; i < flattenedPoints.Count; i += 2)
              {
            if (accumulatedLengths[i] > nextKey.First)
            {
              previousKey = nextKey;
              previousKeyIndex++;
              nextKey = pathLengthsAndKeys[previousKeyIndex + 1];
            }

            float p = (accumulatedLengths[i] - previousKey.First) / (nextKey.First - previousKey.First);
            halfWidths[i] = 0.5f * InterpolationHelper.Lerp(previousKey.Second.Width, nextKey.Second.Width, p);
            sideFalloffs[i] = InterpolationHelper.Lerp(previousKey.Second.SideFalloff, nextKey.Second.SideFalloff, p);

            if (i + 1 < flattenedPoints.Count)
            {
              halfWidths[i + 1] = halfWidths[i];
              sideFalloffs[i + 1] = sideFalloffs[i];
            }
              }

              // Get AABB of road with the side falloff.
              Aabb aabbWithSideFalloffs;
              {
            Vector3F p = flattenedPoints[0];
            float r = halfWidths[0] + sideFalloffs[0];
            aabbWithSideFalloffs = new Aabb(new Vector3F(p.X - r, 0, p.Z - r),
                                        new Vector3F(p.X + r, 0, p.Z + r));
            for (int i = 1; i < flattenedPoints.Count; i += 2)
            {
              p = flattenedPoints[i];
              r = halfWidths[i] + sideFalloffs[i];
              aabbWithSideFalloffs.Grow(new Vector3F(p.X - r, 0, p.Z - r));
              aabbWithSideFalloffs.Grow(new Vector3F(p.X + r, 0, p.Z + r));
            }
              }

              // Terrain properties.
              int numberOfSamplesX = terrain.NumberOfSamplesX;
              int numberOfSamplesZ = terrain.NumberOfSamplesZ;
              int numberOfCellsX = numberOfSamplesX - 1;
              int numberOfCellsZ = numberOfSamplesZ - 1;
              float widthX = terrain.WidthX;
              float cellSizeX = widthX / numberOfCellsX;
              float widthZ = terrain.WidthZ;
              float cellSizeZ = widthZ / numberOfCellsZ;
              float cellSizeDiagonal = (float)Math.Sqrt(cellSizeX * cellSizeX + cellSizeZ * cellSizeZ);

              bool isClosed = Vector3F.AreNumericallyEqual(flattenedPoints[0], flattenedPoints[flattenedPoints.Count - 1]);

              {
            // Get the line segments which of the road border.
            List<Vector4F> segments = new List<Vector4F>();  // 2 points per segment.
            Vector3F lastOrthonormal = Vector3F.Right;
            Vector4F previousV1 = Vector4F.Zero;
            Vector4F previousV2 = Vector4F.Zero;
            for (int i = 0; i < flattenedPoints.Count; i++)
            {
              Vector3F start = flattenedPoints[i];

              Vector3F previous;
              bool isFirstPoint = (i == 0);
              if (!isFirstPoint)
            previous = flattenedPoints[i - 1];
              else if (isClosed && road.SmoothEnds)
            previous = flattenedPoints[flattenedPoints.Count - 2];
              else
            previous = start;

              Vector3F next;
              bool isLastPoint = (i + 1 == flattenedPoints.Count);
              if (!isLastPoint)
            next = flattenedPoints[i + 1];
              else if (isClosed && road.SmoothEnds)
            next = flattenedPoints[1];
              else
            next = start;

              Vector3F tangent = next - previous;

              Vector3F orthonormal = new Vector3F(tangent.Z, 0, -tangent.X);
              if (!orthonormal.TryNormalize())
            orthonormal = lastOrthonormal;

              // Add 2 vertices two segments for the road side border.
              //
              //  pV1        pV2 (previous vertices)
              //  x           x
              //  |           |
              //  x           x
              //  v1          v2 (current vertices)
              //
              // We store the side falloff with the vertex:
              // Vectors are 4D. Height is y. Side falloff is w.
              Vector4F v1 = new Vector4F(start - orthonormal * (halfWidths[i] + 0), sideFalloffs[i]);
              Vector4F v2 = new Vector4F(start + orthonormal * (halfWidths[i] + 0), sideFalloffs[i]);

              if (i > 0)
              {
            segments.Add(previousV1);
            segments.Add(v1);
            segments.Add(previousV2);
            segments.Add(v2);

            if (isLastPoint && !isClosed)
            {
              // A segment for the end of the road.
              segments.Add(v1);
              segments.Add(v2);
            }
              }
              else
              {
            if (!isClosed)
            {
              // A segment for the start of the road.
              segments.Add(v1);
              segments.Add(v2);
            }
              }

              previousV1 = v1;
              previousV2 = v2;

              lastOrthonormal = orthonormal;

              // The flattened points list contains 2 points per line segment, which means that there
              // are duplicate intermediate points, which we skip.
              bool isLastLineSegment = (i + 2 == flattenedPoints.Count);
              if (!isLastLineSegment)
            i++;
            }

            // Apply the side falloff to the terrain heights.
            // We use a padding where the road influence is 100% because we want road width to be flat
            // but that means that we also have to set some triangle vertices outside the road width to
            // full 100% road height.
            float padding = cellSizeDiagonal;
            ClampHeightsToLineSegments(terrain, aabbWithSideFalloffs, segments, padding);
              }

              // Clamp the terrain heights to the inner part of the road.
              // We create quads for the road mesh and clamp the heights to the quad triangles.
              {
            Vector3F previousV1 = Vector3F.Zero;
            Vector3F previousV2 = Vector3F.Zero;
            Vector3F lastOrthonormal = Vector3F.Right;
            for (int i = 0; i < flattenedPoints.Count; i++)
            {
              Vector3F start = flattenedPoints[i];

              Vector3F previous;
              bool isFirstPoint = (i == 0);
              if (!isFirstPoint)
            previous = flattenedPoints[i - 1];
              else if (isClosed && road.SmoothEnds)
            previous = flattenedPoints[flattenedPoints.Count - 2];
              else
            previous = start;

              Vector3F next;
              bool isLastPoint = (i + 1 == flattenedPoints.Count);
              if (!isLastPoint)
            next = flattenedPoints[i + 1];
              else if (isClosed && road.SmoothEnds)
            next = flattenedPoints[1];
              else
            next = start;

              Vector3F tangent = next - previous;

              Vector3F orthonormal = new Vector3F(tangent.Z, 0, -tangent.X);
              if (!orthonormal.TryNormalize())
            orthonormal = lastOrthonormal;

              // Add 2 vertices to create a mesh like this:
              //
              //  pV1             pV2 (previous vertices)
              //  x---------------x
              //  |               |
              //  x---------------x
              //  v1              v2 (current vertices)
              //
              // Then we check all height samples against these triangles.

              // Vectors are 4D. Height is y. Influence is w.
              Vector3F v1 = start - orthonormal * halfWidths[i];
              Vector3F v2 = start + orthonormal * halfWidths[i];

              if (i > 0)
            ClampHeightsToQuad(terrain, previousV1, previousV2, v1, v2);

              previousV1 = v1;
              previousV2 = v2;

              lastOrthonormal = orthonormal;

              // The flattened points list contains 2 points per line segment, which means that there
              // are duplicate intermediate points, which we skip.
              bool isLastLineSegment = (i + 2 == flattenedPoints.Count);
              if (!isLastLineSegment)
            i++;
            }
              }

              terrain.Invalidate();
        }
Пример #16
0
    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);
      }
    }
Пример #17
0
        private static void ClampHeightsToLineSegments(HeightField terrain, Aabb aabb, List<Vector4F> segments, float padding)
        {
            // TODO: Optimize this (see software rasterizers).

              float originX = terrain.OriginX;
              float originZ = terrain.OriginZ;
              int numberOfSamplesX = terrain.NumberOfSamplesX;
              int numberOfSamplesZ = terrain.NumberOfSamplesZ;
              int numberOfCellsX = numberOfSamplesX - 1;
              int numberOfCellsZ = numberOfSamplesZ - 1;
              float widthX = terrain.WidthX;
              float cellSizeX = widthX / numberOfCellsX;
              float widthZ = terrain.WidthZ;
              float cellSizeZ = widthZ / numberOfCellsZ;
              float[] heights = terrain.Samples;

              // Get min and max indices (inclusive).
              float minX = aabb.Minimum.X;
              float maxX = aabb.Maximum.X;
              float minZ = aabb.Minimum.Z;
              float maxZ = aabb.Maximum.Z;

              // Get min and max indices (inclusive).
              int indexXMin = Math.Max(0, (int)Math.Floor((minX - originX) / cellSizeX));
              int indexZMin = Math.Max(0, (int)Math.Floor((minZ - originZ) / cellSizeZ));
              int indexXMax = Math.Min(numberOfSamplesX - 1, (int)Math.Ceiling((maxX - originX) / cellSizeX));
              int indexZMax = Math.Min(numberOfSamplesZ - 1, (int)Math.Ceiling((maxZ - originZ) / cellSizeZ));
              Parallel.For(indexZMin, indexZMax + 1, indexZ =>
              //for (int indexZ = indexZMin; indexZ <= indexZMax; indexZ++)
              {
            for (int indexX = indexXMin; indexX <= indexXMax; indexX++)
            {
              Vector3F terrainPointFlat = new Vector3F(originX + cellSizeX * indexX, 0, originZ + cellSizeZ * indexZ);

              float bestSegmentInfluence = 0;
              float bestSegmentHeight = 0;
              for (int segmentIndex = 0; segmentIndex < segments.Count / 2; segmentIndex++)
              {
            var segmentStartFlat = new Vector3F(segments[segmentIndex * 2].X, 0, segments[segmentIndex * 2].Z);
            var segmentEndFlat = new Vector3F(segments[segmentIndex * 2 + 1].X, 0, segments[segmentIndex * 2 + 1].Z);
            var segment = new LineSegment(segmentStartFlat, segmentEndFlat);
            float parameter;
            GetLineParameter(ref segment, ref terrainPointFlat, out parameter);
            Vector4F closestPoint = segments[segmentIndex * 2] + parameter * (segments[segmentIndex * 2 + 1] - segments[segmentIndex * 2]);
            Vector3F closestPointFlat = new Vector3F(closestPoint.X, 0, closestPoint.Z);
            float distance = (closestPointFlat - terrainPointFlat).Length - padding;
            float influence = MathHelper.Clamp(1 - distance / (closestPoint.W - padding), 0, 1);
            if (influence > bestSegmentInfluence)
            {
              bestSegmentInfluence = influence;
              bestSegmentHeight = closestPoint.Y;
            }
              }

              if (bestSegmentInfluence > 0)
              {
            heights[indexZ * numberOfSamplesX + indexX] = InterpolationHelper.Lerp(
              heights[indexZ * numberOfSamplesX + indexX],
              bestSegmentHeight,
              InterpolationHelper.HermiteSmoothStep(bestSegmentInfluence));
              }
            }
              });
        }
Пример #18
0
    // 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;
      }
    }
Пример #19
0
        private static void ClampHeightsToQuad(HeightField terrain, Vector3F pV1, Vector3F pV2,
            Vector3F v1, Vector3F v2)
        {
            // Handle 2 triangles:
              //
              //  pV1  pV2 (previous vertices)
              //  x----x
              //  |   /|
              //  |  / |
              //  | /  |
              //  |/   |
              //  x----x
              //  v1   v2 (current vertices)

              ClampHeightsToTriangle(terrain, pV1, pV2, v1);
              ClampHeightsToTriangle(terrain, pV2, v2, v1);
        }
Пример #20
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;
    }
Пример #21
0
        private void ComputeCollisionFast(ContactSet contactSet, CollisionQueryType type,
            IGeometricObject heightFieldGeometricObject, IGeometricObject convexGeometricObject,
            HeightField heightField, ConvexShape convex, bool swapped)
        {
            Debug.Assert(type != CollisionQueryType.ClosestPoints);

              // Assume no contact.
              contactSet.HaveContact = false;

              // Get scales and poses
              Pose heightFieldPose = heightFieldGeometricObject.Pose;
              Vector3F heightFieldScale = heightFieldGeometricObject.Scale;
              if (heightFieldScale.X < 0 || heightFieldScale.Y < 0 || heightFieldScale.Z < 0)
            throw new NotSupportedException("Computing collisions for height fields with a negative scaling is not supported.");

              Pose convexPose = convexGeometricObject.Pose;
              Vector3F convexScale = convexGeometricObject.Scale;

              // Get a point in the convex. (Could also use center of AABB.)
              var convexPoint = convexPose.ToWorldPosition(convex.InnerPoint * convexScale);

              // Get height field coordinates.
              convexPoint = heightFieldPose.ToLocalPosition(convexPoint);
              float xUnscaled = convexPoint.X / heightFieldScale.X;
              float zUnscaled = convexPoint.Z / heightFieldScale.Z;

              // If convex point is outside height field, abort.
              var originX = heightField.OriginX;
              var originZ = heightField.OriginZ;
              if (xUnscaled < originX || xUnscaled > originX + heightField.WidthX
              || zUnscaled < originZ || zUnscaled > originZ + heightField.WidthZ)
              {
            return;
              }

              // Get height and normal.
              float height;
              Vector3F normal;
              int featureIndex;
              GetHeight(heightField, xUnscaled, zUnscaled, out height, out normal, out featureIndex);

              // Check for holes.
              if (Numeric.IsNaN(height))
            return;

              // Apply scaling.
              height *= heightFieldScale.Y;
              // Normals are transformed with the inverse transposed matrix --> 1 / scale.
              normal = normal / heightFieldScale;
              normal.Normalize();

              // ----- Now we test convex vs. plane.
              // Convert normal to convex space.
              normal = heightFieldPose.ToWorldDirection(normal);
              var normalInConvex = convexPose.ToLocalDirection(normal);

              // Convert plane point to convex space.
              Vector3F planePoint = new Vector3F(convexPoint.X, height, convexPoint.Z);
              planePoint = heightFieldPose.ToWorldPosition(planePoint);
              planePoint = convexPose.ToLocalPosition(planePoint);

              // Get convex support point in plane normal direction.
              Vector3F supportPoint = convex.GetSupportPoint(-normalInConvex, convexScale);

              // Get penetration depth.
              float penetrationDepth = Vector3F.Dot((planePoint - supportPoint), normalInConvex);

              // Abort if there is no contact.
              if (penetrationDepth < 0)
            return;

              // Abort if object is too deep under the height field.
              // This is important for height fields with holes/caves. Without this check
              // no objects could enter the cave.
              if (penetrationDepth > heightField.Depth)
            return;

              // We have contact.
              contactSet.HaveContact = true;

              // Return for boolean queries.
              if (type == CollisionQueryType.Boolean)
            return;

              // Contact position is in the "middle of the penetration".
              Vector3F position = convexPose.ToWorldPosition(supportPoint + normalInConvex * (penetrationDepth / 2));

              if (swapped)
            normal = -normal;

              // Add contact
              var contact = ContactHelper.CreateContact(contactSet, position, normal, penetrationDepth, false);

              if (swapped)
            contact.FeatureB = featureIndex;
              else
            contact.FeatureA = featureIndex;

              ContactHelper.Merge(contactSet, contact, type, CollisionDetection.ContactPositionTolerance);

              if (CollisionDetection.FullContactSetPerFrame
              && contactSet.Count < 3)
              {
            // Trying to create a full contact set.

            // We use arbitrary orthonormal values to perturb the normal direction.
            var ortho1 = normalInConvex.Orthonormal1;
            var ortho2 = normalInConvex.Orthonormal2;

            // Test 4 perturbed support directions.
            for (int i = 0; i < 4; i++)
            {
              Vector3F direction;
              switch (i)
              {
            case 0:
              direction = -normalInConvex + ortho1;
              break;
            case 1:
              direction = -normalInConvex - ortho1;
              break;
            case 2:
              direction = -normalInConvex + ortho2;
              break;
            default:
              direction = -normalInConvex - ortho2;
              break;
              }

              // Support point vs. plane test as above:
              supportPoint = convex.GetSupportPoint(direction, convexScale);
              penetrationDepth = Vector3F.Dot((planePoint - supportPoint), normalInConvex);
              if (penetrationDepth >= 0)
              {
            position = convexPose.ToWorldPosition(supportPoint + normalInConvex * (penetrationDepth / 2));
            contact = ContactHelper.CreateContact(contactSet, position, normal, penetrationDepth, false);
            ContactHelper.Merge(contactSet, contact, type, CollisionDetection.ContactPositionTolerance);
              }
            }
              }
        }