예제 #1
0
        public void LoadTextureMap(Stream stream)
        {
            Log.Info("Loading terrain texture-map");

            var textureMap = NibbleGrid.Load(stream);

            if (textureMap.Width != _size.X / 3 || textureMap.Height != _size.Y / 3)
            {
                throw new IOException(Resources.TextureMapSizeThirdOfTerrain);
            }
            _textureMap = textureMap;
        }
예제 #2
0
        public void TestSaveLoad()
        {
            using (var tempFile = new TemporaryFile("unit-tests"))
            {
                var grid = new NibbleGrid(new byte[,] {{2, 4}, {5, 10}});
                grid.Save(tempFile);

                using (var stream = File.OpenRead(tempFile))
                    grid = NibbleGrid.Load(stream);

                grid[0, 0].Should().Be(2);
                grid[0, 1].Should().Be(4);
                grid[1, 0].Should().Be(5);
                grid[1, 1].Should().Be(10);
            }
        }
예제 #3
0
        /// <summary>
        /// Creates a new terrain. It is completely flat and has only one texture initially.
        /// </summary>
        /// <param name="size">The size of the terrain to create.</param>
        public Terrain(TerrainSize size)
        {
            Size        = size;
            _heightMap  = new ByteGrid(size.X, size.Y);
            _textureMap = new NibbleGrid(size.X / 3, size.Y / 3);

            // Try to use "Grass" as the default Terrain type
            try
            {
                Templates[0] = Template <TTemplate> .All["Grass"];
            }
            catch (KeyNotFoundException ex)
            {
                Log.Warn(ex);
            }
            catch (InvalidOperationException ex)
            {
                Log.Warn(ex);
            }
        }
예제 #4
0
        private static PositionMultiTextured[] GenerateVertexes(Size size, float stretchH, float stretchV, ByteGrid heightMap, NibbleGrid textureMap, ByteVector4Grid occlusionIntervalMap)
        {
            var vertexes = new PositionMultiTextured[size.Width * size.Height];

#if NETFX4
            Parallel.For(0, size.Width, x =>
#else
            for (int x = 0; x < size.Width; x++)
#endif
            {
                for (int y = 0; y < size.Height; y++)
                {
                    #region Texture blending
                    var texWeights = new float[16];

                    // Perform integer division (texture map has 1/3 of height map accuracy) but keep remainders
                    int xRemainder;
                    int xCoord = Math.DivRem(x, 3, out xRemainder);
                    int yRemainder;
                    int yCoord = Math.DivRem(y, 3, out yRemainder);

                    // Use remainders to determine intermediate blending levels for vertexes
                    switch (xRemainder)
                    {
                        case 0:
                            TextureBlendHelper(textureMap, xCoord, yCoord, texWeights, 0.25f);
                            TextureBlendHelper(textureMap, xCoord - 1, yCoord, texWeights, 0.25f);
                            break;
                        case 1:
                            TextureBlendHelper(textureMap, xCoord, yCoord, texWeights, 0.5f);
                            break;
                        case 2:
                            TextureBlendHelper(textureMap, xCoord, yCoord, texWeights, 0.25f);
                            TextureBlendHelper(textureMap, xCoord + 1, yCoord, texWeights, 0.25f);
                            break;
                    }
                    switch (yRemainder)
                    {
                        case 0:
                            TextureBlendHelper(textureMap, xCoord, yCoord, texWeights, 0.25f);
                            TextureBlendHelper(textureMap, xCoord, yCoord - 1, texWeights, 0.25f);
                            break;
                        case 1:
                            TextureBlendHelper(textureMap, xCoord, yCoord, texWeights, 0.5f);
                            break;
                        case 2:
                            TextureBlendHelper(textureMap, xCoord, yCoord, texWeights, 0.25f);
                            TextureBlendHelper(textureMap, xCoord, yCoord + 1, texWeights, 0.25f);
                            break;
                    }
                    #endregion

                    var occlusionIntervals = occlusionIntervalMap?[x, y] ?? new ByteVector4(0, 255, 255, 255);

                    // Generate vertex using 2D coords, stretch factors (and tex-coords based on them)
                    // Map X = Engine +X
                    // Map Y = Engine -Z
                    // Map height = Engine +Y
                    vertexes[x + size.Width * y] = new PositionMultiTextured(
                        new Vector3(x * stretchH, heightMap[x, y] * stretchV, -y * stretchH),
                        x * stretchH / 500f, y * stretchH / 500f,
                        occlusionIntervals.ByteToAngle(),
                        texWeights, Color.White);
                }
            }
예제 #5
0
        /// <summary>
        /// Creates a new textured model from a vertex and an index array.
        /// The <see cref="PrimitiveType"/> is <see cref="PrimitiveType.TriangleList"/>.
        /// </summary>
        /// <param name="engine">The <see cref="Engine"/> to create the mesh in</param>
        /// <param name="size">The size of the terrain</param>
        /// <param name="stretchH">A factor by which all horizontal distances are multiplied</param>
        /// <param name="stretchV">A factor by which all vertical distances are multiplied</param>
        /// <param name="heightMap">The height values of the terrain in a 2D grid.
        ///   Grid size = Terrain size</param>
        /// <param name="textureMap">The texture values of the terrain in a 2D grid.
        ///   Grid size = Terrain size / 3</param>
        /// <param name="occlusionIntervalMap">The angles at which the global light source occlusion begins and ends.
        ///   Grid size = Terrain size; may be <c>null</c> for no shadowing</param>
        /// <param name="lighting">Shall this mesh be prepared for lighting? (calculate normal vectors, make shaders support lighting, ...)</param>
        /// <param name="blockSize">How many points in X and Y direction shall one block for culling be?</param>
        /// <param name="subsetShaders">Shaders for all subsets the mesh was split into</param>
        /// <param name="subsetBoundingBoxes">Bounding boxes for all subsets the mesh was split into</param>
        /// <returns>The model that was created</returns>
        private static Mesh BuildMesh(Engine engine, Size size, float stretchH, float stretchV, ByteGrid heightMap, NibbleGrid textureMap, ByteVector4Grid occlusionIntervalMap, bool lighting, int blockSize, out SurfaceShader[] subsetShaders, out BoundingBox[] subsetBoundingBoxes)
        {
            #region Sanity checks
            if (heightMap.Width != size.Width || heightMap.Height != size.Height)
                throw new ArgumentException(Resources.WrongHeightMapSize, nameof(heightMap));

            if (occlusionIntervalMap != null && (occlusionIntervalMap.Width != size.Width || occlusionIntervalMap.Height != size.Height))
                throw new ArgumentException(Resources.WrongOcclusionIntervalMapSize, nameof(occlusionIntervalMap));

            if ((textureMap.Width) * 3 != size.Width || (textureMap.Height) * 3 != size.Height)
                throw new ArgumentException(Resources.WrongTextureMapSize, nameof(textureMap));
            #endregion

            using (new TimedLogEvent("Building terrain mesh"))
            {
                var vertexes = GenerateVertexes(size, stretchH, stretchV, heightMap, textureMap, occlusionIntervalMap);

                int[] attributes;
                ushort[] subsetTextureMasks;
                var indexes = GenerateIndexes(size, stretchH, stretchV, blockSize, vertexes, out attributes, out subsetTextureMasks, out subsetBoundingBoxes);

                subsetShaders = LoadShaders(engine, lighting, subsetTextureMasks);
                return CompileMesh(engine, vertexes, indexes, attributes, lighting);
            }
        }
예제 #6
0
        /// <summary>
        /// Creates a new terrain from a height-map and a texture-map
        /// </summary>
        /// <param name="engine">The <see cref="Engine"/> to create the terrain in</param>
        /// <param name="size">The size of the terrain</param>
        /// <param name="stretchH">A factor by which all horizontal distances are multiplied</param>
        /// <param name="stretchV">A factor by which all vertical distances are multiplied</param>
        /// <param name="heightMap">The height values of the terrain in a 2D array.
        ///   Grid size = Terrain size</param>
        /// <param name="occlusionIntervalMap">The angles at which the global light source occlusion begins and ends.
        ///   Grid size = Terrain size; may be <c>null</c> for no shadowing</param>
        /// <param name="textureMap">The texture values of the terrain in a 2D array.
        ///   Grid size = Terrain size / 3</param>
        /// <param name="textures">An array with a maximum of 16 texture names associated to <paramref name="textureMap"/></param>
        /// <param name="lighting">Shall this mesh be prepared for lighting? (calculate normal vectors, make shaders support lighting, ...)</param>
        /// <param name="blockSize">How many points in X and Y direction shall one block for culling be?</param>
        /// <returns>The newly created terrain</returns>
        /// <exception cref="FileNotFoundException">On of the specified texture files could not be found.</exception>
        /// <exception cref="IOException">There was an error reading one of the texture files.</exception>
        /// <exception cref="UnauthorizedAccessException">Read access to one of the texture files is not permitted.</exception>
        /// <exception cref="InvalidDataException">One of the texture files does not contain a valid texture.</exception>
        public static Terrain Create(Engine engine, Size size, float stretchH, float stretchV, ByteGrid heightMap, NibbleGrid textureMap, string[] textures, ByteVector4Grid occlusionIntervalMap, bool lighting, int blockSize)
        {
            #region Sanity checks
            if (engine == null)
            {
                throw new ArgumentNullException(nameof(engine));
            }
            if (heightMap == null)
            {
                throw new ArgumentNullException(nameof(heightMap));
            }
            if (textureMap == null)
            {
                throw new ArgumentNullException(nameof(textureMap));
            }
            if (textures == null)
            {
                throw new ArgumentNullException(nameof(textures));
            }
            #endregion

            if (TerrainShader.MinShaderModel > engine.Capabilities.MaxShaderModel)
            {
                throw new NotSupportedException(Resources.NotSupportedShader);
            }

            // Generate mesh with subsets and bounding bodies
            BoundingBox[]   subsetBoundingBoxes;
            SurfaceShader[] subsetShaders;
            var             terrain = new Terrain(
                BuildMesh(engine, size, stretchH, stretchV, heightMap, textureMap, occlusionIntervalMap, lighting, blockSize, out subsetShaders, out subsetBoundingBoxes),
                BuildMaterial(engine, textures),
                lighting)
            {
                // Set properties here to keep contructor nice and simple
                Size                 = size, StretchH = stretchH, StretchV = stretchV,
                _blockSize           = blockSize,
                _subsetBoundingBoxes = subsetBoundingBoxes,
                _subsetShaders       = subsetShaders,
                NumberSubsets        = subsetBoundingBoxes.Length
            };

            return(terrain);
        }