Ejemplo n.º 1
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);
            }
        }
Ejemplo n.º 2
0
        private void UpdateHoleTexture()
        {
            // Hole information could be loaded from a texture. Here, we procedurally create a pattern of
            // holes.
            // The hole buffer is array with 1 where there is no hole and 0 where there is a hole.
            // (This is similar to an alpha mask: 1 = opaque, 0 = transparent.)
            int numberOfSamples = _terrainObject.TerrainNode.Terrain.Tiles.First().HeightTexture.Width;

            float[] holes = new float[numberOfSamples * numberOfSamples];

            // Fill hole buffer with 1.
            for (int i = 0; i < holes.Length; i++)
            {
                holes[i] = 1;
            }

            if (_holeSize > 0)
            {
                // Add some 0 elements to create holes.
                int counterY = _holeSize;
                for (int y = 0; y < numberOfSamples - 1; y++)
                {
                    int counterX = _holeSize;
                    for (int x = 0; x < numberOfSamples - 1; x++)
                    {
                        holes[y * numberOfSamples + x] = 0;

                        counterX--;
                        if (counterX <= 0)
                        {
                            counterX = _holeSize;
                            x       += _holeSize * 2;
                        }
                    }

                    counterY--;
                    if (counterY <= 0)
                    {
                        counterY = _holeSize;
                        y       += _holeSize * 2;
                    }
                }
            }

            // Copy hole buffer to a texture.
            TerrainHelper.CreateHoleTexture(
                GraphicsService.GraphicsDevice,
                holes,
                numberOfSamples,
                numberOfSamples,
                false,
                ref _holeTexture);

            foreach (var tile in _terrainObject.TerrainNode.Terrain.Tiles)
            {
                // Assign the hole texture to all terrain tiles. (Normally, each tile would have a
                // different hole texture or no hole texture at all.)
                tile.HoleTexture = _holeTexture;

                // We also have to add the holes to the collision detection height fields.
                // Get rigid body that represents this tile.
                var rigidBody   = Simulation.RigidBodies.First(body => body.UserData == tile);
                var heightField = (HeightField)rigidBody.Shape;

                // Update the height values of the collision detection height field.
                float[] heights = TerrainHelper.GetTextureLevelSingle(tile.HeightTexture, 0);
                for (int z = 0; z < numberOfSamples; z++)
                {
                    for (int x = 0; x < numberOfSamples; x++)
                    {
                        if ((!(holes[z * numberOfSamples + x] > 0.5f)))
                        {
                            // The HeightField class treats NaN as holes.
                            heights[z * numberOfSamples + x] = float.NaN;
                        }
                    }
                }
                heightField.SetSamples(heights, numberOfSamples, numberOfSamples);
                heightField.Invalidate();
            }

            _terrainObject.TerrainNode.Terrain.Invalidate();
        }
Ejemplo n.º 3
0
    // 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();
    }