示例#1
0
		public void Init( TerrainOptions options )
		{
			this.options = options;

			this.numMipMaps = options.maxMipmap;
			this.size = options.size;

			this.terrain = new VertexData();
			this.terrain.vertexStart = 0;

			this.terrain.vertexCount = options.size*options.size;

			VertexDeclaration decl = this.terrain.vertexDeclaration;
			VertexBufferBinding binding = this.terrain.vertexBufferBinding;

			int offset = 0;

			// Position/Normal
			decl.AddElement( POSITION, 0, VertexElementType.Float3, VertexElementSemantic.Position );
			decl.AddElement( NORMAL, 0, VertexElementType.Float3, VertexElementSemantic.Normal );

			// TexCoords
			decl.AddElement( TEXCOORD, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 0 );
			offset += VertexElement.GetTypeSize( VertexElementType.Float2 );
			decl.AddElement( TEXCOORD, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 1 );
			offset += VertexElement.GetTypeSize( VertexElementType.Float2 );
			// TODO: Color

			HardwareVertexBuffer buffer = HardwareBufferManager.Instance.CreateVertexBuffer( decl.Clone( POSITION ),
			                                                                                 this.terrain.vertexCount,
			                                                                                 BufferUsage.StaticWriteOnly, true );

			binding.SetBinding( POSITION, buffer );

			buffer = HardwareBufferManager.Instance.CreateVertexBuffer( decl.Clone( NORMAL ), this.terrain.vertexCount,
			                                                            BufferUsage.StaticWriteOnly, true );

			binding.SetBinding( NORMAL, buffer );

			buffer = HardwareBufferManager.Instance.CreateVertexBuffer( decl.Clone( TEXCOORD ), this.terrain.vertexCount,
			                                                            BufferUsage.StaticWriteOnly, true );

			binding.SetBinding( TEXCOORD, buffer );

			this.minLevelDistSqr = new float[this.numMipMaps];

			int endx = options.startx + options.size;
			int endz = options.startz + options.size;

			// TODO: name buffers different so we can unlock
			HardwareVertexBuffer posBuffer = binding.GetBuffer( POSITION );
			var pos = posBuffer.Lock( BufferLocking.Discard );

			HardwareVertexBuffer texBuffer = binding.GetBuffer( TEXCOORD );
			var tex = texBuffer.Lock( BufferLocking.Discard );

			float min = 99999999, max = 0;

#if !AXIOM_SAFE_ONLY
			unsafe
#endif
			{
				var posPtr = pos.ToFloatPointer();
				var texPtr = tex.ToFloatPointer();

				int posCount = 0;
				int texCount = 0;

				for ( int j = options.startz; j < endz; j++ )
				{
					for ( int i = options.startx; i < endx; i++ )
					{
						float height = options.GetWorldHeight( i, j )*options.scaley;

						posPtr[ posCount++ ] = i*options.scalex;
						posPtr[ posCount++ ] = height;
						posPtr[ posCount++ ] = j*options.scalez;

						texPtr[ texCount++ ] = (float)i/options.worldSize;
						texPtr[ texCount++ ] = (float)j/options.worldSize;

						texPtr[ texCount++ ] = ( (float)i/options.size )*options.detailTile;
						texPtr[ texCount++ ] = ( (float)j/options.size )*options.detailTile;

						if ( height < min )
						{
							min = height;
						}

						if ( height > max )
						{
							max = height;
						}
					} // for i
				} // for j
			} // unsafe

			// unlock the buffers
			posBuffer.Unlock();
			texBuffer.Unlock();

			this.box.SetExtents( new Vector3( options.startx*options.scalex, min, options.startz*options.scalez ),
			                     new Vector3( ( endx - 1 )*options.scalex, max, ( endz - 1 )*options.scalez ) );

			this.center = new Vector3( ( options.startx*options.scalex + endx - 1 )/2, ( min + max )/2,
			                           ( options.startz*options.scalez + endz - 1 )/2 );

			float C = CalculateCFactor();

			CalculateMinLevelDist2( C );
		}
        public void Init(TerrainOptions options)
        {
            this.options = options;

            numMipMaps = options.maxMipmap;
            size       = options.size;

            terrain             = new VertexData();
            terrain.vertexStart = 0;
            // Turbo: appended factor 3
            //        Not sure about that, but without that the terrain manager seems
            //        to mess up memory because of buffer overruns
            //terrain.vertexCount = options.size * options.size;
            terrain.vertexCount = options.size * options.size * 3;

            VertexDeclaration   decl    = terrain.vertexDeclaration;
            VertexBufferBinding binding = terrain.vertexBufferBinding;

            int offset = 0;

            // Position/Normal
            decl.AddElement(POSITION, 0, VertexElementType.Float3, VertexElementSemantic.Position);
            decl.AddElement(NORMAL, 0, VertexElementType.Float3, VertexElementSemantic.Normal);

            // TexCoords
            decl.AddElement(TEXCOORD, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 0);
            offset += VertexElement.GetTypeSize(VertexElementType.Float2);
            decl.AddElement(TEXCOORD, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 1);
            offset += VertexElement.GetTypeSize(VertexElementType.Float2);
            // TODO: Color

            HardwareVertexBuffer buffer =
                HardwareBufferManager.Instance.CreateVertexBuffer(
                    decl.GetVertexSize(POSITION),
                    terrain.vertexCount,
                    BufferUsage.StaticWriteOnly, true);

            binding.SetBinding(POSITION, buffer);


            buffer =
                HardwareBufferManager.Instance.CreateVertexBuffer(
                    decl.GetVertexSize(NORMAL),
                    terrain.vertexCount,
                    BufferUsage.StaticWriteOnly, true);

            binding.SetBinding(NORMAL, buffer);

            buffer =
                HardwareBufferManager.Instance.CreateVertexBuffer(
                    offset,
                    terrain.vertexCount,
                    BufferUsage.StaticWriteOnly, true);

            binding.SetBinding(TEXCOORD, buffer);

            minLevelDistSqr = new float[numMipMaps];

            int endx = options.startx + options.size;
            int endz = options.startz + options.size;

            // TODO: name buffers different so we can unlock
            HardwareVertexBuffer posBuffer = binding.GetBuffer(POSITION);
            IntPtr pos = posBuffer.Lock(BufferLocking.Discard);

            HardwareVertexBuffer texBuffer = binding.GetBuffer(TEXCOORD);
            IntPtr tex = texBuffer.Lock(BufferLocking.Discard);

            float min = 99999999, max = 0;

            unsafe {
                float *posPtr = (float *)pos.ToPointer();
                float *texPtr = (float *)tex.ToPointer();

                int posCount = 0;
                int texCount = 0;

                for (int j = options.startz; j < endz; j++)
                {
                    for (int i = options.startx; i < endx; i++)
                    {
                        float height = options.GetWorldHeight(i, j) * options.scaley;

                        posPtr[posCount++] = (float)i * options.scalex;
                        posPtr[posCount++] = height;
                        posPtr[posCount++] = (float)j * options.scalez;

                        texPtr[texCount++] = (float)i / (float)options.worldSize;
                        texPtr[texCount++] = (float)j / (float)options.worldSize;

                        texPtr[texCount++] = ((float)i / (float)options.size) * (float)options.detailTile;
                        texPtr[texCount++] = ((float)j / (float)options.size) * (float)options.detailTile;

                        if (height < min)
                        {
                            min = height;
                        }

                        if (height > max)
                        {
                            max = height;
                        }
                    } // for i
                }     // for j
            }         // unsafe

            // unlock the buffers
            posBuffer.Unlock();
            texBuffer.Unlock();

            box.SetExtents(
                new Vector3((float)options.startx * options.scalex, min, (float)options.startz * options.scalez),
                new Vector3((float)(endx - 1) * options.scalex, max, (float)(endz - 1) * options.scalez));


            center = new Vector3((options.startx * options.scalex + endx - 1) / 2,
                                 (min + max) / 2,
                                 (options.startz * options.scalez + endz - 1) / 2);

            float C = CalculateCFactor();

            CalculateMinLevelDist2(C);
        }
示例#3
0
        public override void LoadWorldGeometry(string fileName)
        {
            var serializer = new XmlSerializer(typeof(TerrainOptions));

            this.options = (TerrainOptions)serializer.Deserialize(ResourceGroupManager.Instance.OpenResource(fileName));

            this.scale    = new Vector3(this.options.scalex, this.options.scaley, this.options.scalez);
            this.tileSize = this.options.size;
            // load the heightmap
            {
                Image image = Image.FromStream(ResourceGroupManager.Instance.OpenResource(this.options.Terrain),
                                               this.options.Terrain.Split('.')[1]);
                worldSize = this.options.worldSize = image.Width;
                var    dest = new Real[(int)worldSize * (int)worldSize];
                byte[] src  = image.Data;
                Real   invScale;

                //if ( image.Format != PixelFormat.L8 && image.Format != PixelFormat.L16 )
                //    throw new AxiomException( "Heightmap is not a grey scale image!" );

                bool is16bit = (image.Format == PixelFormat.L16);

                // Determine mapping from fixed to floating
                ulong rowSize;
                if (is16bit)
                {
                    invScale = 1.0f / 65535.0f;
                    rowSize  = (ulong)worldSize * 2;
                }
                else
                {
                    invScale = 1.0f;                     // / 255.0f;
                    rowSize  = (ulong)worldSize;
                }
                // Read the data
                int srcIndex = 0;
                int dstIndex = 0;
                for (ulong j = 0; j < (ulong)worldSize; ++j)
                {
                    for (ulong i = 0; i < (ulong)worldSize; ++i)
                    {
                        if (is16bit)
                        {
#if AXIOM_BIG_ENDIAN
                            ushort val = (ushort)(src[srcIndex++] << 8);
                            val += src[srcIndex++];
#else
                            ushort val = src[srcIndex++];
                            val += (ushort)(src[srcIndex++] << 8);
#endif
                            dest[dstIndex++] = new Real(val) * invScale;
                        }
                        else
                        {
                            dest[dstIndex++] = new Real(src[srcIndex++]);                                   // *invScale;
#if (XBOX || XBOX360)
                            srcIndex += 3;
#endif
                        }
                    }
                }

                // get the data from the heightmap
                this.options.data = dest;
            }

            float maxx = this.options.scalex * this.options.worldSize;
            float maxy = 255 * this.options.scaley;
            float maxz = this.options.scalez * this.options.worldSize;

            Resize(new AxisAlignedBox(Vector3.Zero, new Vector3(maxx, maxy, maxz)));

            this.terrainMaterial =
                (Material)
                (MaterialManager.Instance.CreateOrRetrieve(
                     !String.IsNullOrEmpty(this.options.MaterialName) ? this.options.MaterialName : "Terrain",
                     ResourceGroupManager.Instance.WorldResourceGroupName).First);

            if (this.options.WorldTexture != "")
            {
                this.terrainMaterial.GetTechnique(0).GetPass(0).CreateTextureUnitState(this.options.WorldTexture, 0);
            }

            if (this.options.DetailTexture != "")
            {
                this.terrainMaterial.GetTechnique(0).GetPass(0).CreateTextureUnitState(this.options.DetailTexture, 1);
            }

            this.terrainMaterial.Lighting = this.options.isLit;
            this.terrainMaterial.Load();

            this.terrainRoot = (SceneNode)RootSceneNode.CreateChild("TerrainRoot");

            this.numTiles = (this.options.worldSize - 1) / (this.options.size - 1);

            this.tiles = new TerrainRenderable[this.numTiles, this.numTiles];

            int p = 0, q = 0;

            for (int j = 0; j < this.options.worldSize - 1; j += (this.options.size - 1))
            {
                p = 0;

                for (int i = 0; i < this.options.worldSize - 1; i += (this.options.size - 1))
                {
                    this.options.startx = i;
                    this.options.startz = j;

                    string name = string.Format("Tile[{0},{1}]", p, q);

                    var node = (SceneNode)this.terrainRoot.CreateChild(name);
                    var tile = new TerrainRenderable();
                    tile.Name = name;

                    tile.RenderQueueGroup = WorldGeometryRenderQueueId;

                    tile.SetMaterial(this.terrainMaterial);
                    tile.Init(this.options);

                    this.tiles[p, q] = tile;

                    node.AttachObject(tile);

                    p++;
                }

                q++;
            }

            int size1 = this.tiles.GetLength(0);
            int size2 = this.tiles.GetLength(1);

            for (int j = 0; j < size1; j++)
            {
                for (int i = 0; i < size2; i++)
                {
                    if (j != size1 - 1)
                    {
                        this.tiles[i, j].SetNeighbor(Neighbor.South, this.tiles[i, j + 1]);
                        this.tiles[i, j + 1].SetNeighbor(Neighbor.North, this.tiles[i, j]);
                    }

                    if (i != size2 - 1)
                    {
                        this.tiles[i, j].SetNeighbor(Neighbor.East, this.tiles[i + 1, j]);
                        this.tiles[i + 1, j].SetNeighbor(Neighbor.West, this.tiles[i, j]);
                    }
                }
            }

            if (this.options.isLit)
            {
                for (int j = 0; j < size1; j++)
                {
                    for (int i = 0; i < size2; i++)
                    {
                        this.tiles[i, j].CalculateNormals();
                    }
                }
            }
        }
		public override void LoadWorldGeometry( string fileName )
		{
			var serializer = new XmlSerializer( typeof ( TerrainOptions ) );
			this.options = (TerrainOptions)serializer.Deserialize( ResourceGroupManager.Instance.OpenResource( fileName ) );

			this.scale = new Vector3( this.options.scalex, this.options.scaley, this.options.scalez );
			this.tileSize = this.options.size;
			// load the heightmap
			{
				Image image = Image.FromStream( ResourceGroupManager.Instance.OpenResource( this.options.Terrain ),
				                                this.options.Terrain.Split( '.' )[ 1 ] );
				worldSize = this.options.worldSize = image.Width;
				var dest = new Real[(int)worldSize*(int)worldSize];
				byte[] src = image.Data;
				Real invScale;

				//if ( image.Format != PixelFormat.L8 && image.Format != PixelFormat.L16 )
				//    throw new AxiomException( "Heightmap is not a grey scale image!" );

				bool is16bit = ( image.Format == PixelFormat.L16 );

				// Determine mapping from fixed to floating
				ulong rowSize;
				if ( is16bit )
				{
					invScale = 1.0f/65535.0f;
					rowSize = (ulong)worldSize*2;
				}
				else
				{
					invScale = 1.0f; // / 255.0f;
					rowSize = (ulong)worldSize;
				}
				// Read the data
				int srcIndex = 0;
				int dstIndex = 0;
				for ( ulong j = 0; j < (ulong)worldSize; ++j )
				{
					for ( ulong i = 0; i < (ulong)worldSize; ++i )
					{
						if ( is16bit )
						{
#if AXIOM_BIG_ENDIAN
							ushort val = (ushort)(src[srcIndex++] << 8);
							val += src[srcIndex++];
#else
							ushort val = src[ srcIndex++ ];
							val += (ushort)( src[ srcIndex++ ] << 8 );
#endif
							dest[ dstIndex++ ] = new Real( val )*invScale;
						}
						else
						{
							dest[ dstIndex++ ] = new Real( src[ srcIndex++ ] ); // *invScale;
#if (XBOX || XBOX360 )
							srcIndex += 3;
#endif
						}
					}
				}

				// get the data from the heightmap
				this.options.data = dest;
			}

			float maxx = this.options.scalex*this.options.worldSize;
			float maxy = 255*this.options.scaley;
			float maxz = this.options.scalez*this.options.worldSize;

			Resize( new AxisAlignedBox( Vector3.Zero, new Vector3( maxx, maxy, maxz ) ) );

			this.terrainMaterial =
				(Material)
				( MaterialManager.Instance.CreateOrRetrieve(
					!String.IsNullOrEmpty( this.options.MaterialName ) ? this.options.MaterialName : "Terrain",
					ResourceGroupManager.Instance.WorldResourceGroupName ).First );

			if ( this.options.WorldTexture != "" )
			{
				this.terrainMaterial.GetTechnique( 0 ).GetPass( 0 ).CreateTextureUnitState( this.options.WorldTexture, 0 );
			}

			if ( this.options.DetailTexture != "" )
			{
				this.terrainMaterial.GetTechnique( 0 ).GetPass( 0 ).CreateTextureUnitState( this.options.DetailTexture, 1 );
			}

			this.terrainMaterial.Lighting = this.options.isLit;
			this.terrainMaterial.Load();

			this.terrainRoot = (SceneNode)RootSceneNode.CreateChild( "TerrainRoot" );

			this.numTiles = ( this.options.worldSize - 1 )/( this.options.size - 1 );

			this.tiles = new TerrainRenderable[this.numTiles,this.numTiles];

			int p = 0, q = 0;

			for ( int j = 0; j < this.options.worldSize - 1; j += ( this.options.size - 1 ) )
			{
				p = 0;

				for ( int i = 0; i < this.options.worldSize - 1; i += ( this.options.size - 1 ) )
				{
					this.options.startx = i;
					this.options.startz = j;

					string name = string.Format( "Tile[{0},{1}]", p, q );

					var node = (SceneNode)this.terrainRoot.CreateChild( name );
					var tile = new TerrainRenderable();
					tile.Name = name;

					tile.RenderQueueGroup = WorldGeometryRenderQueueId;

					tile.SetMaterial( this.terrainMaterial );
					tile.Init( this.options );

					this.tiles[ p, q ] = tile;

					node.AttachObject( tile );

					p++;
				}

				q++;
			}

			int size1 = this.tiles.GetLength( 0 );
			int size2 = this.tiles.GetLength( 1 );

			for ( int j = 0; j < size1; j++ )
			{
				for ( int i = 0; i < size2; i++ )
				{
					if ( j != size1 - 1 )
					{
						this.tiles[ i, j ].SetNeighbor( Neighbor.South, this.tiles[ i, j + 1 ] );
						this.tiles[ i, j + 1 ].SetNeighbor( Neighbor.North, this.tiles[ i, j ] );
					}

					if ( i != size2 - 1 )
					{
						this.tiles[ i, j ].SetNeighbor( Neighbor.East, this.tiles[ i + 1, j ] );
						this.tiles[ i + 1, j ].SetNeighbor( Neighbor.West, this.tiles[ i, j ] );
					}
				}
			}

			if ( this.options.isLit )
			{
				for ( int j = 0; j < size1; j++ )
				{
					for ( int i = 0; i < size2; i++ )
					{
						this.tiles[ i, j ].CalculateNormals();
					}
				}
			}
		}
        public override void LoadWorldGeometry(string fileName)
        {
            TerrainOptions options = new TerrainOptions();

            DataSet optionData = new DataSet();

            optionData.ReadXml(fileName);
            DataTable table = optionData.Tables[0];
            DataRow   row   = table.Rows[0];

            string terrainFileName = "";
            string detailTexture   = "";
            string worldTexture    = "";

            if (table.Columns["Terrain"] != null)
            {
                terrainFileName = (string)row["Terrain"];
            }

            if (table.Columns["DetailTexture"] != null)
            {
                detailTexture = (string)row["DetailTexture"];
            }

            if (table.Columns["WorldTexture"] != null)
            {
                worldTexture = (string)row["WorldTexture"];
            }

            if (table.Columns["MaxMipMapLevel"] != null)
            {
                options.maxMipmap = Convert.ToInt32(row["MaxMipMapLevel"]);
            }

            if (table.Columns["DetailTile"] != null)
            {
                options.detailTile = Convert.ToInt32(row["DetailTile"]);
            }

            if (table.Columns["MaxPixelError"] != null)
            {
                options.maxPixelError = Convert.ToInt32(row["MaxPixelError"]);
            }

            if (table.Columns["TileSize"] != null)
            {
                options.size = Convert.ToInt32(row["TileSize"]);
            }

            if (table.Columns["ScaleX"] != null)
            {
                options.scalex = StringConverter.ParseFloat((string)row["ScaleX"]);
            }

            if (table.Columns["ScaleY"] != null)
            {
                options.scaley = StringConverter.ParseFloat((string)row["ScaleY"]);
            }

            if (table.Columns["ScaleZ"] != null)
            {
                options.scalez = StringConverter.ParseFloat((string)row["ScaleZ"]);
            }

            if (table.Columns["VertexNormals"] != null)
            {
                options.isLit = ((string)row["VertexNormals"]) == "yes" ? true : false;
            }

            scale    = new Vector3(options.scalex, options.scaley, options.scalez);
            tileSize = options.size;

            // load the heightmap
            Image image = Image.FromFile(terrainFileName);

            // TODO: Check terrain size for 2^n + 1

            // get the data from the heightmap
            options.data = image.Data;

            options.worldSize = image.Width;

            float maxx = options.scalex * options.worldSize;
            float maxy = 255 * options.scaley;
            float maxz = options.scalez * options.worldSize;

            Resize(new AxisAlignedBox(Vector3.Zero, new Vector3(maxx, maxy, maxz)));

            terrainMaterial = CreateMaterial("Terrain");

            if (worldTexture != "")
            {
                terrainMaterial.GetTechnique(0).GetPass(0).CreateTextureUnitState(worldTexture, 0);
            }

            if (detailTexture != "")
            {
                terrainMaterial.GetTechnique(0).GetPass(0).CreateTextureUnitState(detailTexture, 1);
            }

            terrainMaterial.Lighting = options.isLit;
            terrainMaterial.Load();

            terrainRoot = (SceneNode)RootSceneNode.CreateChild("TerrainRoot");

            numTiles = (options.worldSize - 1) / (options.size - 1);

            tiles = new TerrainRenderable[numTiles, numTiles];

            int p = 0, q = 0;

            for (int j = 0; j < options.worldSize - 1; j += (options.size - 1))
            {
                p = 0;

                for (int i = 0; i < options.worldSize - 1; i += (options.size - 1))
                {
                    options.startx = i;
                    options.startz = j;

                    string name = string.Format("Tile[{0},{1}]", p, q);

                    SceneNode         node = (SceneNode)terrainRoot.CreateChild(name);
                    TerrainRenderable tile = new TerrainRenderable();
                    tile.Name = name;

                    tile.SetMaterial(terrainMaterial);
                    tile.Init(options);

                    tiles[p, q] = tile;

                    node.AttachObject(tile);

                    p++;
                }

                q++;
            }

            int size1 = tiles.GetLength(0);
            int size2 = tiles.GetLength(1);

            for (int j = 0; j < size1; j++)
            {
                for (int i = 0; i < size2; i++)
                {
                    if (j != size1 - 1)
                    {
                        ((TerrainRenderable)tiles[i, j]).SetNeighbor(Neighbor.South, (TerrainRenderable)tiles[i, j + 1]);
                        ((TerrainRenderable)tiles[i, j + 1]).SetNeighbor(Neighbor.North, (TerrainRenderable)tiles[i, j]);
                    }

                    if (i != size2 - 1)
                    {
                        ((TerrainRenderable)tiles[i, j]).SetNeighbor(Neighbor.East, (TerrainRenderable)tiles[i + 1, j]);
                        ((TerrainRenderable)tiles[i + 1, j]).SetNeighbor(Neighbor.West, (TerrainRenderable)tiles[i, j]);
                    }
                }
            }

#if NOT_USED
            if (false)            // && options.isLit) //TODO: Fix
            {
                for (int j = 0; j < size1; j++)
                {
                    for (int i = 0; i < size2; i++)
                    {
                        ((TerrainRenderable)tiles[i, j]).CalculateNormals();
                    }
                }
            }
#endif
            this.terrainOptions = options;             //we need these later for GetHeightAt, so make them a member variable
        }
示例#6
0
        public void Init(TerrainOptions options)
        {
            this.options = options;

            this.numMipMaps = options.maxMipmap;
            this.size       = options.size;

            this.terrain             = new VertexData();
            this.terrain.vertexStart = 0;

            this.terrain.vertexCount = options.size * options.size;

            VertexDeclaration   decl    = this.terrain.vertexDeclaration;
            VertexBufferBinding binding = this.terrain.vertexBufferBinding;

            int offset = 0;

            // Position/Normal
            decl.AddElement(POSITION, 0, VertexElementType.Float3, VertexElementSemantic.Position);
            decl.AddElement(NORMAL, 0, VertexElementType.Float3, VertexElementSemantic.Normal);

            // TexCoords
            decl.AddElement(TEXCOORD, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 0);
            offset += VertexElement.GetTypeSize(VertexElementType.Float2);
            decl.AddElement(TEXCOORD, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 1);
            offset += VertexElement.GetTypeSize(VertexElementType.Float2);
            // TODO: Color

            HardwareVertexBuffer buffer = HardwareBufferManager.Instance.CreateVertexBuffer(decl.Clone(POSITION),
                                                                                            this.terrain.vertexCount,
                                                                                            BufferUsage.StaticWriteOnly, true);

            binding.SetBinding(POSITION, buffer);

            buffer = HardwareBufferManager.Instance.CreateVertexBuffer(decl.Clone(NORMAL), this.terrain.vertexCount,
                                                                       BufferUsage.StaticWriteOnly, true);

            binding.SetBinding(NORMAL, buffer);

            buffer = HardwareBufferManager.Instance.CreateVertexBuffer(decl.Clone(TEXCOORD), this.terrain.vertexCount,
                                                                       BufferUsage.StaticWriteOnly, true);

            binding.SetBinding(TEXCOORD, buffer);

            this.minLevelDistSqr = new float[this.numMipMaps];

            int endx = options.startx + options.size;
            int endz = options.startz + options.size;

            // TODO: name buffers different so we can unlock
            HardwareVertexBuffer posBuffer = binding.GetBuffer(POSITION);
            var pos = posBuffer.Lock(BufferLocking.Discard);

            HardwareVertexBuffer texBuffer = binding.GetBuffer(TEXCOORD);
            var tex = texBuffer.Lock(BufferLocking.Discard);

            float min = 99999999, max = 0;

#if !AXIOM_SAFE_ONLY
            unsafe
#endif
            {
                var posPtr = pos.ToFloatPointer();
                var texPtr = tex.ToFloatPointer();

                int posCount = 0;
                int texCount = 0;

                for (int j = options.startz; j < endz; j++)
                {
                    for (int i = options.startx; i < endx; i++)
                    {
                        float height = options.GetWorldHeight(i, j) * options.scaley;

                        posPtr[posCount++] = i * options.scalex;
                        posPtr[posCount++] = height;
                        posPtr[posCount++] = j * options.scalez;

                        texPtr[texCount++] = (float)i / options.worldSize;
                        texPtr[texCount++] = (float)j / options.worldSize;

                        texPtr[texCount++] = ((float)i / options.size) * options.detailTile;
                        texPtr[texCount++] = ((float)j / options.size) * options.detailTile;

                        if (height < min)
                        {
                            min = height;
                        }

                        if (height > max)
                        {
                            max = height;
                        }
                    }     // for i
                }         // for j
            }             // unsafe

            // unlock the buffers
            posBuffer.Unlock();
            texBuffer.Unlock();

            this.box.SetExtents(new Vector3(options.startx * options.scalex, min, options.startz * options.scalez),
                                new Vector3((endx - 1) * options.scalex, max, (endz - 1) * options.scalez));

            this.center = new Vector3((options.startx * options.scalex + endx - 1) / 2, (min + max) / 2,
                                      (options.startz * options.scalez + endz - 1) / 2);

            float C = CalculateCFactor();

            CalculateMinLevelDist2(C);
        }
        public override void LoadWorldGeometry(string fileName)
        {
            TerrainOptions options = new TerrainOptions();

            DataSet optionData = new DataSet();
            optionData.ReadXml(fileName);
            DataTable table = optionData.Tables[0];
            DataRow row = table.Rows[0];

            string terrainFileName = "";
            string detailTexture = "";
            string worldTexture = "";

            if(table.Columns["Terrain"] != null)
            {
                terrainFileName = (string)row["Terrain"];
            }

            if(table.Columns["DetailTexture"] != null)
            {
                detailTexture = (string)row["DetailTexture"];
            }

            if(table.Columns["WorldTexture"] != null)
            {
                worldTexture = (string)row["WorldTexture"];
            }

            if(table.Columns["MaxMipMapLevel"] != null)
            {
                options.maxMipmap = Convert.ToInt32(row["MaxMipMapLevel"]);
            }

            if(table.Columns["DetailTile"] != null)
            {
                options.detailTile = Convert.ToInt32(row["DetailTile"]);
            }

            if(table.Columns["MaxPixelError"] != null)
            {
                options.maxPixelError = Convert.ToInt32(row["MaxPixelError"]);
            }

            if(table.Columns["TileSize"] != null)
            {
                options.size = Convert.ToInt32(row["TileSize"]);
            }

            if(table.Columns["ScaleX"] != null)
            {
                options.scalex = StringConverter.ParseFloat((string)row["ScaleX"]);
            }

            if(table.Columns["ScaleY"] != null)
            {
                options.scaley = StringConverter.ParseFloat((string)row["ScaleY"]);
            }

            if(table.Columns["ScaleZ"] != null)
            {
                options.scalez = StringConverter.ParseFloat((string)row["ScaleZ"]);
            }

            if(table.Columns["VertexNormals"] != null)
            {
                options.isLit = ((string)row["VertexNormals"]) == "yes" ? true : false;
            }

            scale = new Vector3(options.scalex, options.scaley, options.scalez);
            tileSize = options.size;

            // load the heightmap
            Image image = Image.FromFile(terrainFileName);

            // TODO: Check terrain size for 2^n + 1

            // get the data from the heightmap
            options.data = image.Data;

            options.worldSize = image.Width;

            float maxx = options.scalex * options.worldSize;
            float maxy = 255 * options.scaley;
            float maxz = options.scalez * options.worldSize;

            Resize(new AxisAlignedBox(Vector3.Zero, new Vector3(maxx, maxy, maxz)));

            terrainMaterial = CreateMaterial("Terrain");

            if(worldTexture != "")
            {
                terrainMaterial.GetTechnique(0).GetPass(0).CreateTextureUnitState(worldTexture, 0);
            }

            if(detailTexture != "")
            {
                terrainMaterial.GetTechnique(0).GetPass(0).CreateTextureUnitState(detailTexture, 1);
            }

            terrainMaterial.Lighting = options.isLit;
            terrainMaterial.Load();

            terrainRoot = (SceneNode)RootSceneNode.CreateChild("TerrainRoot");

            numTiles = (options.worldSize - 1) / (options.size - 1);

            tiles = new TerrainRenderable[numTiles, numTiles];

            int p = 0, q = 0;

            for(int j = 0; j < options.worldSize - 1; j += (options.size - 1))
            {
                p = 0;

                for(int i = 0; i < options.worldSize - 1; i += (options.size - 1))
                {
                    options.startx = i;
                    options.startz = j;

                    string name = string.Format("Tile[{0},{1}]", p, q);

                    SceneNode node = (SceneNode)terrainRoot.CreateChild(name);
                    TerrainRenderable tile = new TerrainRenderable();
                    tile.Name = name;

                    tile.SetMaterial(terrainMaterial);
                    tile.Init(options);

                    tiles[p,q] = tile;

                    node.AttachObject(tile);

                    p++;
                }

                q++;
            }

            int size1 = tiles.GetLength(0);
            int size2 = tiles.GetLength(1);

            for(int j = 0; j < size1; j++)
            {
                for(int i = 0; i < size2; i++)
                {
                    if(j != size1 - 1)
                    {
                        ((TerrainRenderable)tiles[i,j]).SetNeighbor(Neighbor.South, (TerrainRenderable)tiles[i, j + 1]);
                        ((TerrainRenderable)tiles[i,j + 1]).SetNeighbor(Neighbor.North, (TerrainRenderable)tiles[i, j]);
                    }

                    if(i != size2 - 1)
                    {
                        ((TerrainRenderable)tiles[i,j]).SetNeighbor(Neighbor.East, (TerrainRenderable)tiles[i + 1, j]);
                        ((TerrainRenderable)tiles[i + 1,j]).SetNeighbor(Neighbor.West, (TerrainRenderable)tiles[i, j]);
                    }
                }
            }

            #if NOT_USED
            if(false) // && options.isLit) //TODO: Fix
            {
                for(int j = 0; j < size1; j++)
                {
                    for(int i = 0; i < size2; i++)
                    {
                        ((TerrainRenderable)tiles[i,j]).CalculateNormals();
                    }
                }
            }
            #endif
            this.terrainOptions = options; //we need these later for GetHeightAt, so make them a member variable
        }
        public void Init(TerrainOptions options)
        {
            this.options = options;

            numMipMaps = options.maxMipmap;
            size = options.size;

            terrain = new VertexData();
            terrain.vertexStart = 0;
            // Turbo: appended factor 3
            //        Not sure about that, but without that the terrain manager seems
            //        to mess up memory because of buffer overruns
            //terrain.vertexCount = options.size * options.size;
            terrain.vertexCount = options.size * options.size * 3;

            VertexDeclaration decl = terrain.vertexDeclaration;
            VertexBufferBinding binding = terrain.vertexBufferBinding;

            int offset = 0;

            // Position/Normal
            decl.AddElement(POSITION, 0, VertexElementType.Float3, VertexElementSemantic.Position);
            decl.AddElement(NORMAL, 0, VertexElementType.Float3, VertexElementSemantic.Normal);

            // TexCoords
            decl.AddElement(TEXCOORD, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 0);
            offset += VertexElement.GetTypeSize(VertexElementType.Float2);
            decl.AddElement(TEXCOORD, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 1);
            offset += VertexElement.GetTypeSize(VertexElementType.Float2);
            // TODO: Color

            HardwareVertexBuffer buffer =
                HardwareBufferManager.Instance.CreateVertexBuffer(
                decl.GetVertexSize(POSITION),
                terrain.vertexCount,
                BufferUsage.StaticWriteOnly, true);

            binding.SetBinding(POSITION, buffer);

            buffer =
                HardwareBufferManager.Instance.CreateVertexBuffer(
                decl.GetVertexSize(NORMAL),
                terrain.vertexCount,
                BufferUsage.StaticWriteOnly, true);

            binding.SetBinding(NORMAL, buffer);

            buffer =
                HardwareBufferManager.Instance.CreateVertexBuffer(
                offset,
                terrain.vertexCount,
                BufferUsage.StaticWriteOnly, true);

            binding.SetBinding(TEXCOORD, buffer);

            minLevelDistSqr = new float[numMipMaps];

            int endx = options.startx + options.size;
            int endz = options.startz + options.size;

            // TODO: name buffers different so we can unlock
            HardwareVertexBuffer posBuffer = binding.GetBuffer(POSITION);
            IntPtr pos = posBuffer.Lock(BufferLocking.Discard);

            HardwareVertexBuffer texBuffer = binding.GetBuffer(TEXCOORD);
            IntPtr tex = texBuffer.Lock(BufferLocking.Discard);

            float min = 99999999, max = 0;

            unsafe {
                float* posPtr = (float*)pos.ToPointer();
                float* texPtr = (float*)tex.ToPointer();

                int posCount = 0;
                int texCount = 0;

                for(int j = options.startz; j < endz; j++) {
                    for(int i = options.startx; i < endx; i++) {
                        float height = options.GetWorldHeight(i, j) * options.scaley;

                        posPtr[posCount++] = (float)i * options.scalex;
                        posPtr[posCount++] = height;
                        posPtr[posCount++] = (float)j * options.scalez;

                        texPtr[texCount++] = (float)i / (float)options.worldSize;
                        texPtr[texCount++] = (float)j / (float)options.worldSize;

                        texPtr[texCount++] = ((float)i / (float)options.size) * (float)options.detailTile;
                        texPtr[texCount++] = ((float)j / (float)options.size) * (float)options.detailTile;

                        if(height < min) {
                            min = height;
                        }

                        if(height > max) {
                            max = height;
                        }
                    } // for i
                } // for j
            } // unsafe

            // unlock the buffers
            posBuffer.Unlock();
            texBuffer.Unlock();

            box.SetExtents(
                new Vector3((float) options.startx * options.scalex, min, (float)options.startz * options.scalez),
                new Vector3((float)(endx - 1) * options.scalex, max, (float)(endz - 1) * options.scalez));

            center = new Vector3((options.startx * options.scalex + endx - 1) / 2,
                (min + max) / 2,
                (options.startz * options.scalez + endz - 1) / 2);

            float C = CalculateCFactor();

            CalculateMinLevelDist2(C);
        }