// 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); } }
private void CreateRoad() { //RandomHelper.Random = new Random(1234567); // Set isClosed to true join the start and the end of the road. bool isClosed = false; // Create a new TerrainRoadLayer which paints a road onto the terrain. // The road itself is defined by a mesh which is set later. _roadLayer = new TerrainRoadLayer(GraphicsService) { DiffuseColor = new Vector3(0.5f), SpecularColor = new Vector3(1), DiffuseTexture = ContentManager.Load <Texture2D>("Terrain/Road-Asphalt-Diffuse"), NormalTexture = ContentManager.Load <Texture2D>("Terrain/Road-Asphalt-Normal"), SpecularTexture = ContentManager.Load <Texture2D>("Terrain/Road-Asphalt-Specular"), HeightTexture = ContentManager.Load <Texture2D>("Terrain/Road-Asphalt-Height"), // The size of the tileable detail textures in world space units. TileSize = 5, // The border blend range controls how the border of the road fades out. // We fade out 5% of the texture on each side of the road. BorderBlendRange = new Vector4(0.05f, 0.05f, 0.05f, 0.05f), }; // Create 3D spline path with some semi-random control points. _roadPath = new Path3F { PreLoop = isClosed ? CurveLoopType.Cycle : CurveLoopType.Linear, PostLoop = isClosed ? CurveLoopType.Cycle : CurveLoopType.Linear, SmoothEnds = isClosed, }; // The position of the next path key. Vector3 position = new Vector3( RandomHelper.Random.NextFloat(-20, 20), 0, RandomHelper.Random.NextFloat(-20, 20)); // The direction to the next path key. Vector3 direction = Quaternion.CreateRotationY(RandomHelper.Random.NextFloat(0, 10)).Rotate(Vector3.Forward); // Add path keys. for (int j = 0; j < 10; j++) { // Instead of a normal PathKey3F, we use a TerrainRoadPathKey which allows to control // the road with and the side falloff. var key = new TerrainRoadPathKey { Interpolation = SplineInterpolation.CatmullRom, Parameter = j, Point = position, // The width of the road at the path key. Width = RandomHelper.Random.NextFloat(6, 10), // The side falloff (which is used in ClampTerrainToRoad to blend the height values with // the road). SideFalloff = RandomHelper.Random.NextFloat(20, 40), }; _roadPath.Add(key); // Get next random position and direction. position += direction * RandomHelper.Random.NextFloat(20, 40); position.Y += RandomHelper.Random.NextFloat(-2, 2); direction = Quaternion.CreateRotationY(RandomHelper.Random.NextFloat(-1, 1)) .Rotate(direction); } if (isClosed) { // To create a closed path the first and the last key should be identical. _roadPath[_roadPath.Count - 1].Point = _roadPath[0].Point; ((TerrainRoadPathKey)_roadPath[_roadPath.Count - 1]).Width = ((TerrainRoadPathKey)_roadPath[0]).Width; ((TerrainRoadPathKey)_roadPath[_roadPath.Count - 1]).SideFalloff = ((TerrainRoadPathKey)_roadPath[0]).SideFalloff; // Since the path is closed we do not have to fade out the start and the end of the road. _roadLayer.BorderBlendRange *= new Vector4(1, 0, 1, 0); } // Convert the path to a mesh. Submesh roadSubmesh; Aabb roadAabb; float roadLength; TerrainRoadLayer.CreateMesh( GraphicsService.GraphicsDevice, _roadPath, 4, 10, 0.1f, out roadSubmesh, out roadAabb, out roadLength); // Set the mesh in the road layer. _roadLayer.SetMesh(roadSubmesh, roadAabb, roadLength, true); if (isClosed) { // When the path is closed, the end texture and the start texture coordinates should // match. This is the case if (roadLength / tileSize) is an integer. var numberOfTiles = (int)(roadLength / _roadLayer.TileSize); _roadLayer.TileSize = roadLength / numberOfTiles; } // The road layer is added to the layers of a tile. We have to add the road to each terrain // tile which it overlaps. foreach (var tile in _terrainObject.TerrainNode.Terrain.Tiles) { if (GeometryHelper.HaveContact(tile.Aabb, _roadLayer.Aabb.Value)) { tile.Layers.Add(_roadLayer); } } }