public void InitializeHeightsAndNormals() { // Create a procedural height field. int numberOfSamples = 1025; float[] heights = new float[numberOfSamples * numberOfSamples]; var creator = new ProceduralTerrainCreator(RandomHelper.Random.Next(), 256, _noiseMu); creator.CreateTerrain( 7777, 9999, _noiseWidth * _terrainTile.CellSize, _noiseWidth * _terrainTile.CellSize, 0, _noiseHeight, 8, heights, numberOfSamples, numberOfSamples); float tileSize = (numberOfSamples - 1) * _terrainTile.CellSize; _terrainTile.OriginX = -tileSize / 2; _terrainTile.OriginZ = -tileSize / 2; Debug.Assert(Numeric.IsZero(_terrainTile.OriginX % _terrainTile.CellSize), "The tile origin must be an integer multiple of the cell size."); Debug.Assert(Numeric.IsZero(_terrainTile.OriginZ % _terrainTile.CellSize), "The tile origin must be an integer multiple of the cell size."); //TerrainHelper.SmoothTexture(heights, numberOfSamples, numberOfSamples, 1e10f); // Rebuild height texture. Texture2D heightTexture = _terrainTile.HeightTexture; TerrainHelper.CreateHeightTexture( _graphicsService.GraphicsDevice, heights, numberOfSamples, numberOfSamples, false, ref heightTexture); // Rebuild normal texture. Texture2D normalTexture = _terrainTile.NormalTexture; TerrainHelper.CreateNormalTexture( _graphicsService.GraphicsDevice, heights, numberOfSamples, numberOfSamples, _terrainTile.CellSize, false, ref normalTexture); _terrainTile.HeightTexture = heightTexture; _terrainTile.NormalTexture = normalTexture; // Set the height field data for collision detection. var heightField = (HeightField)_rigidBody.Shape; heightField.OriginX = _terrainTile.OriginX; heightField.OriginZ = _terrainTile.OriginZ; heightField.WidthX = tileSize; heightField.WidthZ = tileSize; heightField.SetSamples(heights, heightTexture.Width, heightTexture.Height); heightField.Invalidate(); TerrainNode.Terrain.Invalidate(); }
// 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); } }
// Initialize the terrain geometry from height textures. public void InitializeHeightsAndNormals() { var content = _services.GetInstance<ContentManager>(); // Create the height and normal texture for the 4 tiles. // We can do this in parallel. We use following lock for parts which are not thread-safe. var lockObject = new object(); Parallel.For(0, 2, row => //for (int row = 0; row < 2; row++) { Parallel.For(0, 2, column => //for (int column = 0; column < 2; column++) { string tilePostfix = "-" + row + "-" + column; // e.g. "-0-1" var tile = _tiles[row, column]; var terrainTile = tile.TerrainTile; // The height data is loaded from a 16-bit grayscale texture. Texture2D inputHeightTexture; lock (lockObject) inputHeightTexture = content.Load<Texture2D>("Terrain/Terrain001-Height" + tilePostfix); Debug.Assert(inputHeightTexture.Width == inputHeightTexture.Height, "This code assumes that terrain tiles are square."); // The height textures of the tiles overlap by one texel to avoid gaps. That means, the // input height texture for each tile is 1025 x 1025. The last texture column of the top // left tile is the same as the first column of the top right tile. The last row of the // top left tile is the same as the first row of the bottom left tile. // The tile size in world space units is: float tileSize = (inputHeightTexture.Width - 1) * terrainTile.CellSize; terrainTile.OriginX = -tileSize + tileSize * column; terrainTile.OriginZ = -tileSize + tileSize * row; Debug.Assert(Numeric.IsZero(terrainTile.OriginX % terrainTile.CellSize), "The tile origin must be an integer multiple of the cell size."); Debug.Assert(Numeric.IsZero(terrainTile.OriginZ % terrainTile.CellSize), "The tile origin must be an integer multiple of the cell size."); // Extract the height values from the texture. float[] heights = TerrainHelper.GetTextureLevelSingle(inputHeightTexture, 0); // Transform height values from [0, 1] to [_minHeight, _maxheight]. TerrainHelper.TransformTexture(heights, (_maxHeight - _minHeight), _minHeight); // Optional: Smooth the processed height map. (Very useful to remove artifacts from 8-bit // height maps.) TerrainHelper.SmoothTexture(heights, inputHeightTexture.Width, inputHeightTexture.Height, _smoothness); // We have to create a height and normal texture for each tile. // Reuse existing textures to avoid unnecessary allocation. (XNA can run out of memory // if we allocate to many textures in a short time.) Texture2D heightTexture = terrainTile.HeightTexture; Texture2D normalTexture = terrainTile.NormalTexture; // Convert the height value array into a suitable height texture. TerrainHelper.CreateHeightTexture( _graphicsService.GraphicsDevice, heights, inputHeightTexture.Width, inputHeightTexture.Height, _useNearestNeighborMipmaps, ref heightTexture); // Create a normal texture from the height values. TerrainHelper.CreateNormalTexture( _graphicsService.GraphicsDevice, heights, inputHeightTexture.Width, inputHeightTexture.Height, terrainTile.CellSize, _useNearestNeighborMipmaps, ref normalTexture); // If terrainTile.HeightTexture/NormalTexture was null, then CreateHeight/CreateNormal // has created a new texture. We have to dispose this textures in Dispose(). terrainTile.HeightTexture = heightTexture; terrainTile.NormalTexture = normalTexture; // Set the height field data for collision detection. var heightField = (HeightField)tile.RigidBody.Shape; heightField.OriginX = terrainTile.OriginX; heightField.OriginZ = terrainTile.OriginZ; heightField.WidthX = tileSize; heightField.WidthZ = tileSize; lock (lockObject) { heightField.SetSamples(heights, heightTexture.Width, heightTexture.Height); heightField.Invalidate(); } }); }); // Inform the terrain that the old texture content is invalid. TerrainNode.Terrain.Invalidate(); }
public TerrainTextureSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; _graphicsScreen = new DeferredGraphicsScreen(Services); _graphicsScreen.DrawReticle = true; GraphicsService.Screens.Insert(0, _graphicsScreen); GameObjectService.Objects.Add(new DeferredGraphicsOptionsObject(Services)); Services.Register(typeof(DebugRenderer), null, _graphicsScreen.DebugRenderer); var scene = _graphicsScreen.Scene; Services.Register(typeof(IScene), null, scene); // Add gravity and damping to the physics simulation. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Add a custom game object which controls the camera. var cameraGameObject = new CameraObject(Services, 60); cameraGameObject.ResetPose(new Vector3F(0, 1.8f, 0), 0, 0); GameObjectService.Objects.Add(cameraGameObject); _graphicsScreen.ActiveCameraNode = cameraGameObject.CameraNode; for (int i = 0; i < 10; i++) { GameObjectService.Objects.Add(new DynamicObject(Services, 1)); } GameObjectService.Objects.Add(new DynamicSkyObject(Services, true, false, true)); // Create a simple flat terrain. var terrain = new Terrain(); _terrainTile = new TerrainTile(GraphicsService) { OriginX = -100, OriginZ = -100, CellSize = 1, }; terrain.Tiles.Add(_terrainTile); // Create a flat dummy height texture. float[] heights = new float[200 * 200]; Texture2D heightTexture = null; TerrainHelper.CreateHeightTexture( GraphicsService.GraphicsDevice, heights, 200, 200, false, ref heightTexture); _terrainTile.HeightTexture = heightTexture; var shadowMapEffect = ContentManager.Load <Effect>("DigitalRune/Terrain/TerrainShadowMap"); var gBufferEffect = ContentManager.Load <Effect>("DigitalRune/Terrain/TerrainGBuffer"); var materialEffect = ContentManager.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) } }; var terrainNode = new TerrainNode(terrain, material) { DetailClipmap = { CellsPerLevel = 1364, NumberOfLevels = 9, EnableMipMap = true, }, }; scene.Children.Add(terrainNode); // Add 3 detail textures layers: gravel, rock, snow. float detailCellSize = terrainNode.DetailClipmap.CellSizes[0]; var materialGravel = new TerrainMaterialLayer(GraphicsService) { DiffuseTexture = ContentManager.Load <Texture2D>("Terrain/Gravel-Diffuse"), NormalTexture = ContentManager.Load <Texture2D>("Terrain/Gravel-Normal"), SpecularTexture = ContentManager.Load <Texture2D>("Terrain/Gravel-Specular"), TileSize = detailCellSize * 512, BlendRange = 0.1f, }; _terrainTile.Layers.Add(materialGravel); var noiseTexture = NoiseHelper.GetNoiseTexture(GraphicsService, 128, 60); var materialRock = new TerrainMaterialLayer(GraphicsService) { DiffuseTexture = ContentManager.Load <Texture2D>("Terrain/Rock-02-Diffuse"), NormalTexture = ContentManager.Load <Texture2D>("Terrain/Rock-02-Normal"), SpecularTexture = ContentManager.Load <Texture2D>("Terrain/Rock-02-Specular"), HeightTexture = ContentManager.Load <Texture2D>("Terrain/Rock-02-Height"), TileSize = detailCellSize * 1024, DiffuseColor = new Vector3F(1 / 0.702f), BlendTexture = noiseTexture, BlendTextureChannel = 0, BlendRange = 0.1f, TerrainHeightBlendRange = 0.1f, }; _terrainTile.Layers.Add(materialRock); var materialSnow = new TerrainMaterialLayer(GraphicsService) { DiffuseTexture = ContentManager.Load <Texture2D>("Terrain/Snow-Diffuse"), NormalTexture = ContentManager.Load <Texture2D>("Terrain/Snow-Normal"), SpecularTexture = ContentManager.Load <Texture2D>("Terrain/Snow-Specular"), TileSize = detailCellSize * 512, BlendTexture = noiseTexture, BlendTextureChannel = 1, BlendRange = 0.1f, }; _terrainTile.Layers.Add(materialSnow); // Create a flat plane for collision detection. var rigidBody = new RigidBody(new PlaneShape(), new MassFrame(), null) { MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(rigidBody); CreateGuiControls(); }