/// <summary> /// Writes the mesh data to the specified stream. /// </summary> /// <param name="stream">The stream.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="stream"/> is <see langword="null"/>. /// </exception> public void Save(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } var writer = new BinaryWriter(stream); // Version writer.Write(0); // Number of levels and cells per level. writer.Write(NumberOfLevels); writer.Write(CellsPerLevel); // Number of vertices int vertexCount = Submesh.VertexCount; writer.Write(vertexCount); // Vertices var vertices = new TerrainVertex[vertexCount]; Submesh.VertexBuffer.GetData(vertices); for (int i = 0; i < vertices.Length; i++) { writer.Write(vertices[i].Position.PackedValue); } // Number of indices. int indexCount = Submesh.PrimitiveCount * 3; writer.Write(indexCount); // Indices if (vertexCount <= ushort.MaxValue) { var indices = new ushort[indexCount]; Submesh.IndexBuffer.GetData(indices); for (int i = 0; i < indexCount; i++) { writer.Write(indices[i]); } } else { var indices = new int[indexCount]; Submesh.IndexBuffer.GetData(indices); for (int i = 0; i < indexCount; i++) { writer.Write(indices[i]); } } writer.Flush(); }
private static DeviceBuffer CreateVertexBuffer( GraphicsDevice graphicsDevice, HeightMap heightMap, Rectangle patchBounds, ushort[] indices, out BoundingBox boundingBox, out Triangle[] triangles) { var numVertices = patchBounds.Width * patchBounds.Height; var vertices = new TerrainVertex[numVertices]; var points = new Vector3[numVertices]; var vertexIndex = 0; for (var y = patchBounds.Y; y < patchBounds.Y + patchBounds.Height; y++) { for (var x = patchBounds.X; x < patchBounds.X + patchBounds.Width; x++) { var position = heightMap.GetPosition(x, y); points[vertexIndex] = position; vertices[vertexIndex++] = new TerrainVertex { Position = position, Normal = heightMap.Normals[x, y], UV = new Vector2(x, y) }; } } boundingBox = BoundingBox.CreateFromPoints(points); triangles = new Triangle[(patchBounds.Width - 1) * (patchBounds.Height) * 2]; var triangleIndex = 0; var indexIndex = 0; for (var y = 0; y < patchBounds.Height - 1; y++) { for (var x = 0; x < patchBounds.Width - 1; x++) { // Triangle 1 triangles[triangleIndex++] = new Triangle( points[indices[indexIndex++]], points[indices[indexIndex++]], points[indices[indexIndex++]]); // Triangle 2 triangles[triangleIndex++] = new Triangle( points[indices[indexIndex++]], points[indices[indexIndex++]], points[indices[indexIndex++]]); } } return(graphicsDevice.CreateStaticBuffer(vertices, BufferUsage.VertexBuffer)); }
float GainVertex(int X, int Y, int DX, int DY, int Seed = 0) { TerrainVertex v = new TerrainVertex(); v.Position.X = X; v.Position.Z = Y; v.Position.Y = 0; v.Color = Color.Red; v.TextureCoordinate.X = X / 4f; v.TextureCoordinate.Y = Y / 4f; //this will contain calculated height float H = 0; if (this.Map == null) { return(0);// v; } //return v; int MapX = X + DX * (BlockSize); int MapY = Y + DY * (BlockSize); //get lerped "rough" elevation float Elevation = GetBilineaer(X, Y, DX, DY, Map.ElevationData); //return v; //* float base1 = Simplex.CalcPixel2D(MapX + 1112, MapY + 13123, 1f / 1024f) / 1f; H = 80f + Elevation * base1; float random2 = Simplex.CalcPixel2D(MapX + 1112, MapY + 13123, 1f / 32f) / 256f; random2 += Simplex.CalcPixel2D(MapX + 1112, MapY + 13123, 1f / 512f) / 32f; H += random2; //*/ bool isRiver = false; bool isMountain = false;// 90f;// H; //return v; try { v.Color = Map.GetGrassColour(DX, DY); isRiver = Map.TileData[DX, DY] == WorldMap.TileType.River; isMountain = Map.TileData[DX, DY] == WorldMap.TileType.Mountain; } catch (Exception) { v.Color = Color.Red; } // if (Elevation <= 0.0f&&!isRiver) // H = 70f; v.Position.Y = H; if (v.Position.Y < 80f) { v.MultiTexData.Z = 1f; } return(v.Position.Y); }
public TerrainVertex[] GetVertices(GraphicsDevice gd) { if (VertexBuffer == null) { RegenTerrain(gd, Bp); } var dat = new TerrainVertex[VertexBuffer.VertexCount]; VertexBuffer.GetData <TerrainVertex>(dat); return(dat); }
public static TerrainVertex[] GetVerticesMC(uint octreeNodeHandle) { // Based on http://stackoverflow.com/a/1318929 uint noOfVertices; Validate(cuGetNoOfVerticesMC(octreeNodeHandle, out noOfVertices)); TerrainVertex[] result = new TerrainVertex[noOfVertices]; Validate(cuGetVerticesMC(octreeNodeHandle, out result)); return(result); }
public static void UpdateCellBuffers(GraphicsDevice graphics, TerrainGeometryCellsComponent geo, D3DTerrainRenderComponent render) { var vbuff = new SharpDX.Direct3D11.Buffer[geo.Cells.Length]; var ibuff = new SharpDX.Direct3D11.Buffer[geo.Cells.Length]; for (var cellIndex = 0; cellIndex < geo.Cells.Length; cellIndex++) { var cell = geo.Cells[cellIndex]; var pool = ArrayPool <TerrainVertex> .Shared; TerrainVertex[] vertex = null; try { //vertex = new TerrainVertex[pos.Length]; vertex = pool.Rent(cell.VertexCount);// for (var i = 0; i < cell.VertexCount; i++) { var weight = new Vector4( SharpDX.MathUtil.Clamp(1.0f - (float)Math.Abs(cell.Positions[i].Y - 0f) / 8.0f, 0f, 1f), SharpDX.MathUtil.Clamp(1.0f - (float)Math.Abs(cell.Positions[i].Y - 10) / 6.0f, 0f, 1f), SharpDX.MathUtil.Clamp(1.0f - (float)Math.Abs(cell.Positions[i].Y - 15) / 6.0f, 0f, 1f), SharpDX.MathUtil.Clamp(1.0f - (float)Math.Abs(cell.Positions[i].Y - geo.MaxHeight) / 4.0f, 0f, 1f) ); var total = weight.X + weight.Y + weight.Z + weight.W; weight.X /= total; weight.Y /= total; weight.Z /= total; weight.W /= total; vertex[i] = new TerrainVertex { position = cell.Positions[i], normal = cell.Normals[i], tangent = cell.Tangents[i], binormal = cell.Binormal[i], color = cell.Colors[i], texcoor = cell.TextureCoordinates[i], normapMapTexCoor = cell.NormalMapTexCoordinates[i], texWeights = weight, }; } vbuff[cellIndex] = graphics.CreateBuffer(BindFlags.VertexBuffer, vertex); ibuff[cellIndex] = graphics.CreateBuffer(BindFlags.IndexBuffer, cell.Indices); } finally { pool.Return(vertex); } } render.VertexBuffers.Set(vbuff); render.IndexBuffers.Set(ibuff); }
public void RegenTerrain(GraphicsDevice device, WorldState world, Blueprint blueprint) { if (GrassState == null) { TerrainDirty = true; //yikes! try again to see if we have it next frame return; } TerrainDirty = false; if (VertexBuffer != null) { IndexBuffer.Dispose(); BladeIndexBuffer.Dispose(); VertexBuffer.Dispose(); GridIndexBuffer?.Dispose(); TGridIndexBuffer?.Dispose(); } /** Convert rectangle to world units **/ var quads = Size.Width; var quadWidth = WorldSpace.GetWorldFromTile((float)Size.Width / (float)quads); var quadHeight = WorldSpace.GetWorldFromTile((float)Size.Height / (float)quads); var numQuads = quads * quads; var archSize = quads; TerrainVertex[] Geom = new TerrainVertex[numQuads * 4]; int[] Indexes = new int[numQuads * 6]; int[] BladeIndexes = new int[numQuads * 6]; NumPrimitives = (numQuads * 2); int geomOffset = 0; int indexOffset = 0; int bindexOffset = 0; var offsetX = WorldSpace.GetWorldFromTile(Size.X); var offsetY = WorldSpace.GetWorldFromTile(Size.Y); for (var y = 0; y < quads; y++) { for (var x = 0; x < quads; x++) { var tl = new Vector3(offsetX + (x * quadWidth), 0.0f, offsetY + (y * quadHeight)); var tr = new Vector3(tl.X + quadWidth, 0.0f, tl.Z); var bl = new Vector3(tl.X, 0.0f, tl.Z + quadHeight); var br = new Vector3(tl.X + quadWidth, 0.0f, tl.Z + quadHeight); tl.Y = GetElevationPoint(x, y); tr.Y = GetElevationPoint(x + 1, y); bl.Y = GetElevationPoint(x, y + 1); br.Y = GetElevationPoint(x + 1, y + 1); Indexes[indexOffset++] = geomOffset; Indexes[indexOffset++] = (geomOffset + 1); Indexes[indexOffset++] = (geomOffset + 2); Indexes[indexOffset++] = (geomOffset + 2); Indexes[indexOffset++] = (geomOffset + 3); Indexes[indexOffset++] = geomOffset; short tx = (short)x, ty = (short)y; if (blueprint.GetFloor(tx, ty, 1).Pattern == 0 && (blueprint.GetWall(tx, ty, 1).Segments & (WallSegments.HorizontalDiag | WallSegments.VerticalDiag)) == 0) { BladeIndexes[bindexOffset++] = geomOffset; BladeIndexes[bindexOffset++] = (geomOffset + 1); BladeIndexes[bindexOffset++] = (geomOffset + 2); BladeIndexes[bindexOffset++] = (geomOffset + 2); BladeIndexes[bindexOffset++] = (geomOffset + 3); BladeIndexes[bindexOffset++] = geomOffset; } Color tlCol = Color.Lerp(LightGreen, LightBrown, GetGrassState(x, y)); Color trCol = Color.Lerp(LightGreen, LightBrown, GetGrassState(x + 1, y)); Color blCol = Color.Lerp(LightGreen, LightBrown, GetGrassState(x, y + 1)); Color brCol = Color.Lerp(LightGreen, LightBrown, GetGrassState(x + 1, y + 1)); Geom[geomOffset++] = new TerrainVertex(tl, tlCol.ToVector4(), new Vector2(((x - y) + 1) * 0.5f, (x + y) * 0.5f), GetGrassState(x, y), GetNormalAt(x, y)); Geom[geomOffset++] = new TerrainVertex(tr, trCol.ToVector4(), new Vector2(((x - y) + 2) * 0.5f, (x + 1 + y) * 0.5f), GetGrassState(x + 1, y), GetNormalAt(x + 1, y)); Geom[geomOffset++] = new TerrainVertex(br, brCol.ToVector4(), new Vector2(((x - y) + 1) * 0.5f, (x + y + 2) * 0.5f), GetGrassState(x + 1, y + 1), GetNormalAt(x + 1, y + 1)); Geom[geomOffset++] = new TerrainVertex(bl, blCol.ToVector4(), new Vector2((x - y) * 0.5f, (x + y + 1) * 0.5f), GetGrassState(x, y + 1), GetNormalAt(x, y + 1)); } } var GridIndices = GetGridIndicesForArea(Bp.BuildableArea, quads); var TGridIndices = GetGridIndicesForArea(Bp.TargetBuildableArea, quads); VertexBuffer = new VertexBuffer(device, typeof(TerrainVertex), Geom.Length, BufferUsage.None); VertexBuffer.SetData(Geom); IndexBuffer = new IndexBuffer(device, IndexElementSize.ThirtyTwoBits, sizeof(int) * Indexes.Length, BufferUsage.None); IndexBuffer.SetData(Indexes); BladePrimitives = (bindexOffset / 3); BladeIndexBuffer = new IndexBuffer(device, IndexElementSize.ThirtyTwoBits, sizeof(int) * Indexes.Length, BufferUsage.None); BladeIndexBuffer.SetData(BladeIndexes); GeomLength = Geom.Length; if (GridIndices.Length > 0) { GridIndexBuffer = new IndexBuffer(device, IndexElementSize.ThirtyTwoBits, sizeof(int) * GridIndices.Length, BufferUsage.None); GridIndexBuffer.SetData(GridIndices); GridPrimitives = GridIndices.Length / 2; } if (TGridIndices.Length > 0) { TGridIndexBuffer = new IndexBuffer(device, IndexElementSize.ThirtyTwoBits, sizeof(int) * TGridIndices.Length, BufferUsage.None); TGridIndexBuffer.SetData(TGridIndices); TGridPrimitives = TGridIndices.Length / 2; } }
/// <summary> /// Creates the vertex buffer for the bottom right interior trim /// </summary> /// <param name="pGraphicsDevice"></param> private void PopulateVerticesBottomRight(TerrainVertex.Shared[] pVertices) { int nCounter = 0; // bottom segment for (short y = Settings.BLOCK_SIZE_M_MINUS_ONE; y < Settings.BLOCK_SIZE_M + 1; y++) { for (short x = Settings.BLOCK_SIZE_M_MINUS_ONE; x < Settings.GRID_SIZE_N - Settings.BLOCK_SIZE_M_MINUS_ONE; x++) { // write shared data pVertices[nCounter++] = new TerrainVertex.Shared(x, y); } } // right segment for (short y = Settings.BLOCK_SIZE_M; y < (3 * Settings.BLOCK_SIZE_M); y++) { for (short x = Settings.GRID_SIZE_N - Settings.BLOCK_SIZE_M - 1; x < Settings.GRID_SIZE_N - Settings.BLOCK_SIZE_M_MINUS_ONE; x++) { // write shared data pVertices[nCounter++] = new TerrainVertex.Shared(x, y); } } }
public static TerrainRendererMesh Load(GraphicsDevice graphicsDevice, Stream stream) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (stream == null) { throw new ArgumentNullException("stream"); } var reader = new BinaryReader(stream); // Version reader.ReadInt32(); // Number of levels and cells per level. int numberOfLevels = reader.ReadInt32(); int cellsPerLevel = reader.ReadInt32(); // Number of vertices int vertexCount = reader.ReadInt32(); // Vertices var vertices = new TerrainVertex[vertexCount]; for (int i = 0; i < vertices.Length; i++) { vertices[i].Position = new HalfVector4(); vertices[i].Position.PackedValue = reader.ReadUInt64(); } // Number of indices. int indexCount = reader.ReadInt32(); // Indices Submesh submesh; if (vertexCount <= ushort.MaxValue) { var indices = new ushort[indexCount]; for (int i = 0; i < indices.Length; i++) { indices[i] = reader.ReadUInt16(); } submesh = CreateSubmesh(graphicsDevice, vertices, indices); } else { var indices = new int[indexCount]; for (int i = 0; i < indices.Length; i++) { indices[i] = reader.ReadInt32(); } submesh = CreateSubmesh(graphicsDevice, vertices, indices); } return(new TerrainRendererMesh(numberOfLevels, cellsPerLevel, submesh)); }
private static Submesh CreateGeoClipmapMesh(GraphicsDevice graphicsDevice, int numberOfLevels, int cellsPerLevel, bool useDiamondTessellation) { if (graphicsDevice == null) { throw new ArgumentNullException("graphicsDevice"); } if (cellsPerLevel < 1) { throw new ArgumentOutOfRangeException("cellsPerLevel", "The number of cells per level must be greater than 0."); } // Round to even number of cells. if (cellsPerLevel % 2 != 0) { cellsPerLevel++; } // Allocate a mesh with conservative list capacities. int cellsPerLevelUpperBound = (cellsPerLevel + 3) * (cellsPerLevel + 3); int verticesPerLevelUpperBound = useDiamondTessellation ? cellsPerLevelUpperBound * 9 : cellsPerLevelUpperBound * 4; int indicesPerLevelUpperBound = useDiamondTessellation ? cellsPerLevelUpperBound * 8 * 3 : cellsPerLevelUpperBound * 2 * 3; var triangleMesh = new TriangleMesh(verticesPerLevelUpperBound * numberOfLevels, indicesPerLevelUpperBound * numberOfLevels); // The levels are created in a separate mesh and then combined into the final mesh. var triangleMeshes = new TriangleMesh[numberOfLevels]; triangleMeshes[0] = triangleMesh; for (int i = 1; i < numberOfLevels; i++) { triangleMeshes[i] = new TriangleMesh(verticesPerLevelUpperBound, indicesPerLevelUpperBound); } //for (int level = 0; level < numberOfLevels; level++) Parallel.For(0, numberOfLevels, level => { var levelTriangleMesh = triangleMeshes[level]; int cellSize = (1 << level); float y = level; // Store LOD in Y coordinate. Debug.Assert(cellsPerLevel % 2 == 0); int halfWidthInCells = cellsPerLevel / 2; int halfWidth = cellSize * (halfWidthInCells); int minInclusive = -halfWidth - cellSize; // We add an extra border on the top and left. int maxInclusive = halfWidth - cellSize; // Normal loop limit without extra border. int previousHalfWidth = (level > 0) ? halfWidth / 2 : -1; if (useDiamondTessellation) { int index = 0; for (int z = minInclusive; z <= maxInclusive; z += cellSize) { for (int x = minInclusive; x <= maxInclusive; x += cellSize) { // No triangles in the area which are covered by previous levels. // (We compare cell centers with radius of last LOD.) if (Math.Max(Math.Abs(x + cellSize / 2), Math.Abs(z + cellSize / 2)) < previousHalfWidth) { continue; } // Each cell will be tessellated like this: // A-----B-----C // | \ | / | // | \ | / | // D-----E-----F // | / | \ | // | / | \ | // G-----H-----I var indexA = index++; levelTriangleMesh.Vertices.Add(new Vector3(x, y, z)); var indexB = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + 0.5f * cellSize, y, z)); var indexC = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + cellSize, y, z)); var indexD = index++; levelTriangleMesh.Vertices.Add(new Vector3(x, y, z + 0.5f * cellSize)); var indexE = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + 0.5f * cellSize, y, z + 0.5f * cellSize)); var indexF = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + cellSize, y, z + 0.5f * cellSize)); var indexG = index++; levelTriangleMesh.Vertices.Add(new Vector3(x, y, z + cellSize)); var indexH = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + 0.5f * cellSize, y, z + cellSize)); var indexI = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + cellSize, y, z + cellSize)); // Triangles using ADEG: if (x != minInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexD); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexG); levelTriangleMesh.Indices.Add(indexD); } else { // The outer cells are tessellated differently to stitch to the next level. // A-----B-----C // | \ | / | // | \ | / | // | E-----F // | / | \ | // | / | \ | // G-----H-----I levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexG); levelTriangleMesh.Indices.Add(indexA); } // Triangles using ABCE: if (z != minInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexB); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexB); } else { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexC); } // Triangles using CEFI: if (x != maxInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexF); levelTriangleMesh.Indices.Add(indexI); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexF); } else { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexI); } // Triangles using EGHI: if (z != maxInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexH); levelTriangleMesh.Indices.Add(indexG); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexI); levelTriangleMesh.Indices.Add(indexH); } else { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexI); levelTriangleMesh.Indices.Add(indexG); } } } Debug.Assert(levelTriangleMesh.Vertices.Count <= verticesPerLevelUpperBound, "Bad estimate for upper bound of vertices."); Debug.Assert(levelTriangleMesh.Indices.Count <= indicesPerLevelUpperBound, "Bad estimate for upper bound of indices."); levelTriangleMesh.WeldVertices(0.1f); } else { // Add one extra border to hide gaps. minInclusive -= cellSize; maxInclusive += cellSize; int index = 0; for (int z = minInclusive; z <= maxInclusive; z += cellSize) { for (int x = minInclusive; x <= maxInclusive; x += cellSize) { // No triangles in the area which are covered by previous levels. // (We compare cell centers with radius of last LOD.) if (Math.Max(Math.Abs(x + cellSize / 2), Math.Abs(z + cellSize / 2)) < previousHalfWidth) { continue; } // Each 2x2 cells will be tessellated like this: // A-----B // | / | // | / | // C-----D int indexA = index++; levelTriangleMesh.Vertices.Add(new Vector3(x, y, z)); int indexB = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + cellSize, y, z)); int indexC = index++; levelTriangleMesh.Vertices.Add(new Vector3(x, y, z + cellSize)); int indexD = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + cellSize, y, z + cellSize)); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexB); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexB); levelTriangleMesh.Indices.Add(indexD); } } Debug.Assert(levelTriangleMesh.Vertices.Count <= verticesPerLevelUpperBound, "Bad estimate for upper bound of vertices."); Debug.Assert(levelTriangleMesh.Indices.Count <= indicesPerLevelUpperBound, "Bad estimate for upper bound of indices."); levelTriangleMesh.WeldVertices(0.1f); } }); // Combine meshes. for (int i = 1; i < numberOfLevels; i++) { triangleMesh.Add(triangleMeshes[i]); } var vertices = new TerrainVertex[triangleMesh.Vertices.Count]; { int index = 0; for (int i = 0; i < vertices.Length; i++) { Vector3 v = triangleMesh.Vertices[i]; vertices[index++] = new TerrainVertex(new HalfVector4(v.X, v.Y, v.Z, 1)); } } var submesh = CreateSubmesh(graphicsDevice, vertices, triangleMesh.Indices.ToArray()); //Debug.WriteLine("Number of terrain vertices:" + vertices.Length); //Debug.WriteLine("Number of terrain triangles:" + triangleMesh.Indices.Count / 3); return(submesh); }
/// <summary> /// Creates patch skirt vertices /// </summary> /// <param name="firstVertex">First vertex in the patch</param> private static unsafe void CreateSkirtVertices( TerrainVertex* firstVertex ) { if ( DebugInfo.DisableTerainSkirts ) { return; } int vRes = TerrainPatchConstants.PatchResolution - 1; // First horizontal skirt TerrainVertex* skirtVertex = firstVertex + TerrainPatchConstants.PatchArea; CreateSkirtVertices( firstVertex, 1, skirtVertex ); // First vertical skirt skirtVertex += TerrainPatchConstants.PatchResolution; CreateSkirtVertices( firstVertex, TerrainPatchConstants.PatchResolution, skirtVertex ); // Last horizontal skirt skirtVertex += TerrainPatchConstants.PatchResolution; CreateSkirtVertices( firstVertex + vRes * TerrainPatchConstants.PatchResolution, 1, skirtVertex ); // Last vertical skirt skirtVertex += TerrainPatchConstants.PatchResolution; CreateSkirtVertices( firstVertex + vRes, TerrainPatchConstants.PatchResolution, skirtVertex ); }
/// <summary> /// This implements the method in resource, to load the mesh data(vertex buffer, index buffer) /// of this terrain tile. /// </summary> protected override void load() { // 读取地形数据 // This line will load the terrain data in this tile. float[] data = TerrainData.Instance.GetData(tileX, tileY, terrEdgeSize); float radtc = MathEx.Degree2Radian(tileCol); float radtl = MathEx.Degree2Radian(tileLat); float radSpan = MathEx.Degree2Radian(10); int vertexCount = terrEdgeSize * terrEdgeSize; int terrEdgeLen = terrEdgeSize - 1; if (terrEdgeSize == 33) material.SetEffect(EffectManager.Instance.GetModelEffect(TerrainEffect33Factory.Name)); else material.SetEffect(EffectManager.Instance.GetModelEffect(TerrainEffect17Factory.Name)); #region 顶点数据 vtxDecl = factory.CreateVertexDeclaration(TerrainVertex.Elements); vtxBuffer = factory.CreateVertexBuffer(vertexCount, vtxDecl, BufferUsage.WriteOnly); TerrainVertex[] vtxArray = new TerrainVertex[vertexCount]; float cellAngle = radSpan / (float)terrEdgeLen; #region 计算顶点坐标 // Caluclate the position of each vertex // i为纬度方向 // i is in the latitude direction for (int i = 0; i < terrEdgeSize; i++) { // j为经度方向 // j is in the longitude direction for (int j = 0; j < terrEdgeSize; j++) { Vector3 pos = PlanetEarth.GetPosition(radtc + j * cellAngle, radtl - i * cellAngle); int index = i * terrEdgeSize + j; // 计算海拔高度 // calculate the elevation float height = (data[index] - TerrainMeshManager.PostZeroLevel) * TerrainMeshManager.PostHeightScale; //if (height > 0) //{ // height = (height - 0) * TerrainMeshManager.PostHeightScale; //} //else //{ // height *= TerrainMeshManager.PostHeightScale; // height -= 10; // //if (height < -30) // // height = -30; //} Vector3 normal = pos; normal.Normalize(); vtxArray[index].Position = pos + normal * height; // this index is used to generate detailed texture coordinate in vertex shader vtxArray[index].Index = index; // map the texture coordinate for global texturing float curCol = radtc + j * cellAngle; float curLat = radSpan + radtl - i * cellAngle; curCol += MathEx.PIf; curLat -= MathEx.Degree2Radian(10); vtxArray[index].u = 0.5f * curCol / MathEx.PIf; vtxArray[index].v = (-curLat + MathEx.PiOver2) / MathEx.PIf; } } #endregion #endregion #region 索引数据 SharedIndexData sindexData = TerrainMeshManager.Instance.GetIndexData(terrEdgeSize); indexBuffer = sindexData.Index; #endregion #region 构造GeomentryData defGeometryData = new GeomentryData(); defGeometryData.VertexDeclaration = vtxDecl; defGeometryData.VertexSize = TerrainVertex.Size; defGeometryData.VertexBuffer = vtxBuffer; defGeometryData.IndexBuffer = indexBuffer; defGeometryData.PrimCount = indexBuffer.IndexCount / 3; defGeometryData.VertexCount = terrEdgeSize * terrEdgeSize; defGeometryData.PrimitiveType = RenderPrimitiveType.TriangleList; defGeometryData.BaseVertex = 0; #endregion vtxBuffer.SetData<TerrainVertex>(vtxArray); }
/// <summary> /// Generates vertices for a patch. Calculates maximum error between this patch and next higher detail patch /// </summary> /// <param name="patch">Patch</param> /// <param name="res">Patch resolution</param> /// <param name="firstVertex">Patch vertices</param> /// <param name="error">Maximum error value between this patch and higher level patch</param> public unsafe void GenerateTerrainPatchVertices( ITerrainPatch patch, int res, TerrainVertex* firstVertex, out float error ) { SetPatchPlanetParameters( patch ); SafeTerrainGenerator.GenerateVertices( patch.LocalOrigin, patch.LocalUStep, patch.LocalVStep, res, res, patch.Uv, patch.UvResolution, firstVertex, out error ); }
private static Submesh CreateSubmesh(GraphicsDevice graphicsDevice, TerrainVertex[] vertices, Array indices) { var submesh = new Submesh(); submesh.VertexBuffer = new VertexBuffer( graphicsDevice, TerrainVertex.VertexDeclaration, vertices.Length, BufferUsage.None); submesh.VertexBuffer.SetData(vertices); submesh.VertexCount = submesh.VertexBuffer.VertexCount; // Build array of indices. if (vertices.Length <= ushort.MaxValue) { var indicesUInt16 = indices as ushort[]; if (indicesUInt16 == null) { var indicesInt32 = (int[])indices; indicesUInt16 = new ushort[indices.Length]; for (int i = 0; i < indicesUInt16.Length; i++) indicesUInt16[i] = (ushort)indicesInt32[i]; } submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.SixteenBits, indicesUInt16.Length, BufferUsage.None); submesh.IndexBuffer.SetData(indicesUInt16); } else { var indicesInt32 = (int[])indices; submesh.IndexBuffer = new IndexBuffer( graphicsDevice, IndexElementSize.ThirtyTwoBits, indicesInt32.Length, BufferUsage.None); submesh.IndexBuffer.SetData(indicesInt32); } submesh.PrimitiveType = PrimitiveType.TriangleList; submesh.PrimitiveCount = indices.Length / 3; return submesh; }
private static Submesh CreateGeoClipmapMesh(GraphicsDevice graphicsDevice, int numberOfLevels, int cellsPerLevel, bool useDiamondTessellation) { if (graphicsDevice == null) throw new ArgumentNullException("graphicsDevice"); if (cellsPerLevel < 1) throw new ArgumentOutOfRangeException("cellsPerLevel", "The number of cells per level must be greater than 0."); // Round to even number of cells. if (cellsPerLevel % 2 != 0) cellsPerLevel++; // Allocate a mesh with conservative list capacities. int cellsPerLevelUpperBound = (cellsPerLevel + 3) * (cellsPerLevel + 3); int verticesPerLevelUpperBound = useDiamondTessellation ? cellsPerLevelUpperBound * 9 : cellsPerLevelUpperBound * 4; int indicesPerLevelUpperBound = useDiamondTessellation ? cellsPerLevelUpperBound * 8 * 3 : cellsPerLevelUpperBound * 2 * 3; var triangleMesh = new TriangleMesh(verticesPerLevelUpperBound * numberOfLevels, indicesPerLevelUpperBound * numberOfLevels); // The levels are created in a separate mesh and then combined into the final mesh. var triangleMeshes = new TriangleMesh[numberOfLevels]; triangleMeshes[0] = triangleMesh; for (int i = 1; i < numberOfLevels; i++) triangleMeshes[i] = new TriangleMesh(verticesPerLevelUpperBound, indicesPerLevelUpperBound); //for (int level = 0; level < numberOfLevels; level++) Parallel.For(0, numberOfLevels, level => { var levelTriangleMesh = triangleMeshes[level]; int cellSize = (1 << level); float y = level; // Store LOD in Y coordinate. Debug.Assert(cellsPerLevel % 2 == 0); int halfWidthInCells = cellsPerLevel / 2; int halfWidth = cellSize * (halfWidthInCells); int minInclusive = -halfWidth - cellSize; // We add an extra border on the top and left. int maxInclusive = halfWidth - cellSize; // Normal loop limit without extra border. int previousHalfWidth = (level > 0) ? halfWidth / 2 : -1; if (useDiamondTessellation) { #region ----- Diamond tessellation ----- int index = 0; for (int z = minInclusive; z <= maxInclusive; z += cellSize) { for (int x = minInclusive; x <= maxInclusive; x += cellSize) { // No triangles in the area which are covered by previous levels. // (We compare cell centers with radius of last LOD.) if (Math.Max(Math.Abs(x + cellSize / 2), Math.Abs(z + cellSize / 2)) < previousHalfWidth) continue; // Each cell will be tessellated like this: // A-----B-----C // | \ | / | // | \ | / | // D-----E-----F // | / | \ | // | / | \ | // G-----H-----I var indexA = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x, y, z)); var indexB = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + 0.5f * cellSize, y, z)); var indexC = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + cellSize, y, z)); var indexD = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x, y, z + 0.5f * cellSize)); var indexE = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + 0.5f * cellSize, y, z + 0.5f * cellSize)); var indexF = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + cellSize, y, z + 0.5f * cellSize)); var indexG = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x, y, z + cellSize)); var indexH = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + 0.5f * cellSize, y, z + cellSize)); var indexI = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + cellSize, y, z + cellSize)); // Triangles using ADEG: if (x != minInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexD); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexG); levelTriangleMesh.Indices.Add(indexD); } else { // The outer cells are tessellated differently to stitch to the next level. // A-----B-----C // | \ | / | // | \ | / | // | E-----F // | / | \ | // | / | \ | // G-----H-----I levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexG); levelTriangleMesh.Indices.Add(indexA); } // Triangles using ABCE: if (z != minInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexB); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexB); } else { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexC); } // Triangles using CEFI: if (x != maxInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexF); levelTriangleMesh.Indices.Add(indexI); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexF); } else { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexI); } // Triangles using EGHI: if (z != maxInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexH); levelTriangleMesh.Indices.Add(indexG); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexI); levelTriangleMesh.Indices.Add(indexH); } else { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexI); levelTriangleMesh.Indices.Add(indexG); } } } Debug.Assert(levelTriangleMesh.Vertices.Count <= verticesPerLevelUpperBound, "Bad estimate for upper bound of vertices."); Debug.Assert(levelTriangleMesh.Indices.Count <= indicesPerLevelUpperBound, "Bad estimate for upper bound of indices."); levelTriangleMesh.WeldVertices(0.1f); #endregion } else { #region ----- Simple tessellation ----- // Add one extra border to hide gaps. minInclusive -= cellSize; maxInclusive += cellSize; int index = 0; for (int z = minInclusive; z <= maxInclusive; z += cellSize) { for (int x = minInclusive; x <= maxInclusive; x += cellSize) { // No triangles in the area which are covered by previous levels. // (We compare cell centers with radius of last LOD.) if (Math.Max(Math.Abs(x + cellSize / 2), Math.Abs(z + cellSize / 2)) < previousHalfWidth) continue; // Each 2x2 cells will be tessellated like this: // A-----B // | / | // | / | // C-----D int indexA = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x, y, z)); int indexB = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + cellSize, y, z)); int indexC = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x, y, z + cellSize)); int indexD = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + cellSize, y, z + cellSize)); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexB); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexB); levelTriangleMesh.Indices.Add(indexD); } } Debug.Assert(levelTriangleMesh.Vertices.Count <= verticesPerLevelUpperBound, "Bad estimate for upper bound of vertices."); Debug.Assert(levelTriangleMesh.Indices.Count <= indicesPerLevelUpperBound, "Bad estimate for upper bound of indices."); levelTriangleMesh.WeldVertices(0.1f); #endregion } }); // Combine meshes. for (int i = 1; i < numberOfLevels; i++) triangleMesh.Add(triangleMeshes[i]); var vertices = new TerrainVertex[triangleMesh.Vertices.Count]; { int index = 0; for (int i = 0; i < vertices.Length; i++) { Vector3F v = triangleMesh.Vertices[i]; vertices[index++] = new TerrainVertex(new HalfVector4(v.X, v.Y, v.Z, 1)); } } var submesh = CreateSubmesh(graphicsDevice, vertices, triangleMesh.Indices.ToArray()); //Debug.WriteLine("Number of terrain vertices:" + vertices.Length); //Debug.WriteLine("Number of terrain triangles:" + triangleMesh.Indices.Count / 3); return submesh; }
/// <summary> /// Writes the mesh data to the specified stream. /// </summary> /// <param name="stream">The stream.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="stream"/> is <see langword="null"/>. /// </exception> public void Save(Stream stream) { if (stream == null) throw new ArgumentNullException("stream"); var writer = new BinaryWriter(stream); // Version writer.Write(0); // Number of levels and cells per level. writer.Write(NumberOfLevels); writer.Write(CellsPerLevel); // Number of vertices int vertexCount = Submesh.VertexCount; writer.Write(vertexCount); // Vertices var vertices = new TerrainVertex[vertexCount]; Submesh.VertexBuffer.GetData(vertices); for (int i = 0; i < vertices.Length; i++) writer.Write(vertices[i].Position.PackedValue); // Number of indices. int indexCount = Submesh.PrimitiveCount * 3; writer.Write(indexCount); // Indices if (vertexCount <= ushort.MaxValue) { var indices = new ushort[indexCount]; Submesh.IndexBuffer.GetData(indices); for (int i = 0; i < indexCount; i++) writer.Write(indices[i]); } else { var indices = new int[indexCount]; Submesh.IndexBuffer.GetData(indices); for (int i = 0; i < indexCount; i++) writer.Write(indices[i]); } writer.Flush(); }
public static TerrainRendererMesh Load(GraphicsDevice graphicsDevice, Stream stream) { if (graphicsDevice == null) throw new ArgumentNullException("graphicsDevice"); if (stream == null) throw new ArgumentNullException("stream"); var reader = new BinaryReader(stream); // Version reader.ReadInt32(); // Number of levels and cells per level. int numberOfLevels = reader.ReadInt32(); int cellsPerLevel = reader.ReadInt32(); // Number of vertices int vertexCount = reader.ReadInt32(); // Vertices var vertices = new TerrainVertex[vertexCount]; for (int i = 0; i < vertices.Length; i++) { vertices[i].Position = new HalfVector4(); vertices[i].Position.PackedValue = reader.ReadUInt64(); } // Number of indices. int indexCount = reader.ReadInt32(); // Indices Submesh submesh; if (vertexCount <= ushort.MaxValue) { var indices = new ushort[indexCount]; for (int i = 0; i < indices.Length; i++) indices[i] = reader.ReadUInt16(); submesh = CreateSubmesh(graphicsDevice, vertices, indices); } else { var indices = new int[indexCount]; for (int i = 0; i < indices.Length; i++) indices[i] = reader.ReadInt32(); submesh = CreateSubmesh(graphicsDevice, vertices, indices); } return new TerrainRendererMesh(numberOfLevels, cellsPerLevel, submesh); }
/// <summary> /// Creates a strip of patch skirt vertices /// </summary> /// <param name="srcVertex"></param> /// <param name="srcOffset"></param> /// <param name="dstVertex"></param> private static unsafe void CreateSkirtVertices( TerrainVertex* srcVertex, int srcOffset, TerrainVertex* dstVertex ) { float skirtSize = -10; for ( int i = 0; i < TerrainPatchConstants.PatchResolution; ++i ) { Vector3 offset = srcVertex->Normal * skirtSize; srcVertex->CopyTo( dstVertex, offset ); srcVertex += srcOffset; ++dstVertex; } }
public void MeshRects(int level, GraphicsDevice device) { var rects = RoofRects[level - 2]; if (rects == null) { return; } if (Drawgroups[level - 2] != null && Drawgroups[level - 2].NumPrimitives > 0) { Drawgroups[level - 2].VertexBuffer.Dispose(); Drawgroups[level - 2].IndexBuffer.Dispose(); } var numQuads = rects.Count * 4; //4 sides for each roof rectangle TerrainVertex[] Geom = new TerrainVertex[numQuads * 4]; int[] Indexes = new int[numQuads * 6]; var numPrimitives = (numQuads * 2); int geomOffset = 0; int indexOffset = 0; foreach (var rect in rects) { //determine roof height of the smallest edge. This height will be used on all edges var height = Math.Min(rect.x2 - rect.x1, rect.y2 - rect.y1) / 2; // / \ // / \ // /________\ Draw 4 segments like this. two segments will have the top middle section so short it will not appear. var heightMod = height / 400f; var pitch = RoofPitch; var tl = ToWorldPos(rect.x1, rect.y1, 0, level, pitch) + new Vector3(0, heightMod, 0); var tr = ToWorldPos(rect.x2, rect.y1, 0, level, pitch) + new Vector3(0, heightMod, 0); var bl = ToWorldPos(rect.x1, rect.y2, 0, level, pitch) + new Vector3(0, heightMod, 0); var br = ToWorldPos(rect.x2, rect.y2, 0, level, pitch) + new Vector3(0, heightMod, 0); //middle vertices. todo: height modifier (not hard) var m_tl = ToWorldPos(rect.x1 + height, rect.y1 + height, height, level, pitch) + new Vector3(0, heightMod, 0); var m_tr = ToWorldPos(rect.x2 - height, rect.y1 + height, height, level, pitch) + new Vector3(0, heightMod, 0); var m_bl = ToWorldPos(rect.x1 + height, rect.y2 - height, height, level, pitch) + new Vector3(0, heightMod, 0); var m_br = ToWorldPos(rect.x2 - height, rect.y2 - height, height, level, pitch) + new Vector3(0, heightMod, 0); Color topCol = Color.Lerp(Color.White, new Color(175, 175, 175), pitch); Color rightCol = Color.White; Color btmCol = Color.Lerp(Color.White, new Color(200, 200, 200), pitch); Color leftCol = Color.Lerp(Color.White, new Color(150, 150, 150), pitch); Vector4 darken = new Vector4(0.8f, 0.8f, 0.8f, 1.0f); //quad as two tris for (int j = 0; j < 16; j += 4) { Indexes[indexOffset++] = geomOffset + j; Indexes[indexOffset++] = (geomOffset + 1) + j; Indexes[indexOffset++] = (geomOffset + 2) + j; Indexes[indexOffset++] = (geomOffset + 2) + j; Indexes[indexOffset++] = (geomOffset + 3) + j; Indexes[indexOffset++] = geomOffset + j; } Vector2 texScale = new Vector2(2 / 3f, 1f); Geom[geomOffset++] = new TerrainVertex(tl, topCol.ToVector4() * darken, new Vector2(tl.X, tl.Z * -1) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(tr, topCol.ToVector4() * darken, new Vector2(tr.X, tr.Z * -1) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(m_tr, topCol.ToVector4(), new Vector2(m_tr.X, m_tr.Z * -1) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(m_tl, topCol.ToVector4(), new Vector2(m_tl.X, m_tl.Z * -1) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(tr, rightCol.ToVector4() * darken, new Vector2(tr.Z, tr.X) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(br, rightCol.ToVector4() * darken, new Vector2(br.Z, br.X) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(m_br, rightCol.ToVector4(), new Vector2(m_br.Z, m_br.X) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(m_tr, rightCol.ToVector4(), new Vector2(m_tr.Z, m_tr.X) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(br, btmCol.ToVector4() * darken, new Vector2(br.X, br.Z) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(bl, btmCol.ToVector4() * darken, new Vector2(bl.X, bl.Z) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(m_bl, btmCol.ToVector4(), new Vector2(m_bl.X, m_bl.Z) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(m_br, btmCol.ToVector4(), new Vector2(m_br.X, m_br.Z) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(bl, leftCol.ToVector4() * darken, new Vector2(bl.Z, bl.X * -1) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(tl, leftCol.ToVector4() * darken, new Vector2(tl.Z, tl.X * -1) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(m_tl, leftCol.ToVector4(), new Vector2(m_tl.Z, m_tl.X * -1) * texScale, 0); Geom[geomOffset++] = new TerrainVertex(m_bl, leftCol.ToVector4(), new Vector2(m_bl.Z, m_bl.X * -1) * texScale, 0); } var result = new RoofDrawGroup(); if (numPrimitives > 0) { result.VertexBuffer = new VertexBuffer(device, typeof(TerrainVertex), Geom.Length, BufferUsage.None); if (Geom.Length > 0) { result.VertexBuffer.SetData(Geom); } result.IndexBuffer = new IndexBuffer(device, IndexElementSize.ThirtyTwoBits, sizeof(int) * Indexes.Length, BufferUsage.None); if (Geom.Length > 0) { result.IndexBuffer.SetData(Indexes); } } result.NumPrimitives = numPrimitives; Drawgroups[level - 2] = result; }
/// <summary> /// Generates vertices for a terrain patch /// </summary> /// <param name="patch">Terrain patch</param> /// <param name="res">Terrain patch resolution</param> /// <param name="firstVertex">Pointer to terrain vertices</param> /// <param name="error">Terrain patch error output here</param> public unsafe void GenerateTerrainPatchVertices( ITerrainPatch patch, int res, TerrainVertex* firstVertex, out float error ) { m_Gen.GenerateVertices( patch.LocalOrigin, patch.LocalUStep, patch.LocalVStep, res, res, patch.Uv, patch.UvResolution, firstVertex, out error ); }
public Unit GenerateBlock(int X, int Y) { int x = 0; int y = 0; BlockTitle = ""; Unit block = new Unit(); block.X = X; block.Y = Y; CreateGrid(); SetHeights(X, Y); SetupVertexField(_heightmap); SetTileID(50, 50, 1); SetTileColour(51, 50, Color.Red); SetTileColour(49, 50, Color.Green); /* * for (x = 0; x < BlockSize; x++) * for (y = 0; y < BlockSize; y++) * { * SetTileID(x, y, 2); * } * //*/ CalculateNormals(); block.indices = _indices2; block.vertices = _vertices;// new TerrainVertex[(BlockSize * 2 ) * (BlockSize * 2 )]; block.indices = _indices; block.vertices = new TerrainVertex[(BlockSize * 2) * (BlockSize * 2)]; block.heightmap = new float[BlockSize + 1, BlockSize + 1]; TerrainVertex current; for (x = 0; x < BlockSize + 1; x++) { for (y = 0; y < BlockSize + 1; y++) { block.heightmap[x, y] = _heightmap[x + 1, y + 1]; // block.vertices[x + y * BlockSize].Position.X = x; //block.vertices[x + y * BlockSize].Position.Z = y; } } for (x = 0; x < BlockSize; x++) { for (y = 0; y < BlockSize; y++) { //* block.vertices[(y * 4 * BlockSize) + (x * 4) + 0] = _vertices[(1 + y) * 4 * (BlockSize + 2) + (1 + x) * 4 + 0]; block.vertices[(y * 4 * BlockSize) + (x * 4) + 1] = _vertices[(1 + y) * 4 * (BlockSize + 2) + (1 + x) * 4 + 1]; block.vertices[(y * 4 * BlockSize) + (x * 4) + 2] = _vertices[(1 + y) * 4 * (BlockSize + 2) + (1 + x) * 4 + 2]; block.vertices[(y * 4 * BlockSize) + (x * 4) + 3] = _vertices[(1 + y) * 4 * (BlockSize + 2) + (1 + x) * 4 + 3]; /* * block.vertices[(y * 4 * BlockSize) + (x * 4) + 0].Position.X = x; * block.vertices[(y * 4 * BlockSize) + (x * 4) + 1].Position.X = x+1; * block.vertices[(y * 4 * BlockSize) + (x * 4) + 2].Position.X = x; * block.vertices[(y * 4 * BlockSize) + (x * 4) + 3].Position.X = x+1; * block.vertices[(y * 4 * BlockSize) + (x * 4) + 0].Position.Z = y; * block.vertices[(y * 4 * BlockSize) + (x * 4) + 1].Position.Z = y; * block.vertices[(y * 4 * BlockSize) + (x * 4) + 2].Position.Z = y+1; * block.vertices[(y * 4 * BlockSize) + (x * 4) + 3].Position.Z = y+1; * //*/ } } TerrainVertex probe1 = block.vertices[(y - 1) * 4 * BlockSize + (x - 1) * 4]; TerrainVertex probe2 = block.vertices[(y - 1) * 4 * BlockSize + (x - 2) * 4]; TerrainVertex probe3 = block.vertices[(y - 1) * 4 * BlockSize + (x - 1) * 4 + 1]; // Console.Write("^00A000 Generated block at " + X.ToString() + "," + Y.ToString() + "."); block.Name = BlockTitle; return(block); }
public Tuple <TerrainVertex[], int[], int> MeshRectData(int level) { var rects = RoofRects[level - 2]; if (rects == null) { return(null); } var numQuads = rects.Count * 4; //4 sides for each roof rectangle TerrainVertex[] Geom = new TerrainVertex[numQuads * 4]; int[] Indexes = new int[numQuads * 6]; var numPrimitives = (numQuads * 2); int geomOffset = 0; int indexOffset = 0; foreach (var rect in rects) { //determine roof height of the smallest edge. This height will be used on all edges var height = Math.Min(rect.x2 - rect.x1, rect.y2 - rect.y1) / 2; // / \ // / \ // /________\ Draw 4 segments like this. two segments will have the top middle section so short it will not appear. var heightMod = height / 400f; var pitch = RoofPitch; var tl = ToWorldPos(rect.x1, rect.y1, 0, level, pitch) + new Vector3(0, heightMod, 0); var tr = ToWorldPos(rect.x2, rect.y1, 0, level, pitch) + new Vector3(0, heightMod, 0); var bl = ToWorldPos(rect.x1, rect.y2, 0, level, pitch) + new Vector3(0, heightMod, 0); var br = ToWorldPos(rect.x2, rect.y2, 0, level, pitch) + new Vector3(0, heightMod, 0); //middle vertices. todo: height modifier (not hard) var m_tl = ToWorldPos(rect.x1 + height, rect.y1 + height, height, level, pitch) + new Vector3(0, heightMod, 0); var m_tr = ToWorldPos(rect.x2 - height, rect.y1 + height, height, level, pitch) + new Vector3(0, heightMod, 0); var m_bl = ToWorldPos(rect.x1 + height, rect.y2 - height, height, level, pitch) + new Vector3(0, heightMod, 0); var m_br = ToWorldPos(rect.x2 - height, rect.y2 - height, height, level, pitch) + new Vector3(0, heightMod, 0); Color topCol = Color.White; //Color.Lerp(Color.White, new Color(175, 175, 175), pitch); Color rightCol = Color.White; //Color.White; Color btmCol = Color.White; //Color.Lerp(Color.White, new Color(200, 200, 200), pitch); Color leftCol = Color.White; //Color.Lerp(Color.White, new Color(150, 150, 150), pitch); Vector4 darken = new Vector4(0.8f, 0.8f, 0.8f, 1.0f); //quad as two tris for (int j = 0; j < 16; j += 4) { Indexes[indexOffset++] = (geomOffset + 2) + j; Indexes[indexOffset++] = (geomOffset + 1) + j; Indexes[indexOffset++] = geomOffset + j; Indexes[indexOffset++] = geomOffset + j; Indexes[indexOffset++] = (geomOffset + 3) + j; Indexes[indexOffset++] = (geomOffset + 2) + j; } var n1 = -Vector3.Normalize(Vector3.Cross(tl - tr, tr - m_tr)); var n1m = Vector3.Normalize(n1 + Vector3.Up); Vector2 texScale = new Vector2(2 / 3f, 1f); Geom[geomOffset++] = new TerrainVertex(tl, topCol.ToVector4(), new Vector2(tl.X, tl.Z * -1) * texScale, 0, n1); Geom[geomOffset++] = new TerrainVertex(tr, topCol.ToVector4(), new Vector2(tr.X, tr.Z * -1) * texScale, 0, n1); Geom[geomOffset++] = new TerrainVertex(m_tr, topCol.ToVector4(), new Vector2(m_tr.X, m_tr.Z * -1) * texScale, 0, n1m); Geom[geomOffset++] = new TerrainVertex(m_tl, topCol.ToVector4(), new Vector2(m_tl.X, m_tl.Z * -1) * texScale, 0, n1m); n1 = -Vector3.Normalize(Vector3.Cross(tr - br, br - m_br)); n1m = Vector3.Normalize(n1 + Vector3.Up); Geom[geomOffset++] = new TerrainVertex(tr, rightCol.ToVector4(), new Vector2(tr.Z, tr.X) * texScale, 0, n1); Geom[geomOffset++] = new TerrainVertex(br, rightCol.ToVector4(), new Vector2(br.Z, br.X) * texScale, 0, n1); Geom[geomOffset++] = new TerrainVertex(m_br, rightCol.ToVector4(), new Vector2(m_br.Z, m_br.X) * texScale, 0, n1m); Geom[geomOffset++] = new TerrainVertex(m_tr, rightCol.ToVector4(), new Vector2(m_tr.Z, m_tr.X) * texScale, 0, n1m); n1 = -Vector3.Normalize(Vector3.Cross(br - bl, bl - m_bl)); n1m = Vector3.Normalize(n1 + Vector3.Up); Geom[geomOffset++] = new TerrainVertex(br, btmCol.ToVector4(), new Vector2(br.X, br.Z) * texScale, 0, n1); Geom[geomOffset++] = new TerrainVertex(bl, btmCol.ToVector4(), new Vector2(bl.X, bl.Z) * texScale, 0, n1); Geom[geomOffset++] = new TerrainVertex(m_bl, btmCol.ToVector4(), new Vector2(m_bl.X, m_bl.Z) * texScale, 0, n1m); Geom[geomOffset++] = new TerrainVertex(m_br, btmCol.ToVector4(), new Vector2(m_br.X, m_br.Z) * texScale, 0, n1m); n1 = -Vector3.Normalize(Vector3.Cross(bl - tl, tl - m_tl)); n1m = Vector3.Normalize(n1 + Vector3.Up); Geom[geomOffset++] = new TerrainVertex(bl, leftCol.ToVector4(), new Vector2(bl.Z, bl.X * -1) * texScale, 0, n1); Geom[geomOffset++] = new TerrainVertex(tl, leftCol.ToVector4(), new Vector2(tl.Z, tl.X * -1) * texScale, 0, n1); Geom[geomOffset++] = new TerrainVertex(m_tl, leftCol.ToVector4(), new Vector2(m_tl.Z, m_tl.X * -1) * texScale, 0, n1m); Geom[geomOffset++] = new TerrainVertex(m_bl, leftCol.ToVector4(), new Vector2(m_bl.Z, m_bl.X * -1) * texScale, 0, n1m); } return(new Tuple <TerrainVertex[], int[], int>(Geom, Indexes, numPrimitives)); }
public void RegenTerrain(GraphicsDevice device, WorldState world, Blueprint blueprint) { if (VertexBuffer != null) { IndexBuffer.Dispose(); BladeIndexBuffer.Dispose(); VertexBuffer.Dispose(); } /** Convert rectangle to world units **/ var quads = Size.Width; var quadWidth = WorldSpace.GetWorldFromTile((float)Size.Width / (float)quads); var quadHeight = WorldSpace.GetWorldFromTile((float)Size.Height / (float)quads); var numQuads = quads * quads; TerrainVertex[] Geom = new TerrainVertex[numQuads * 4]; int[] Indexes = new int[numQuads * 6]; int[] BladeIndexes = new int[numQuads * 6]; NumPrimitives = (numQuads * 2); int geomOffset = 0; int indexOffset = 0; int bindexOffset = 0; var offsetX = WorldSpace.GetWorldFromTile(Size.X); var offsetY = WorldSpace.GetWorldFromTile(Size.Y); for (var y = 0; y < quads; y++) { for (var x = 0; x < quads; x++) { var tl = new Vector3(offsetX + (x * quadWidth), 0.0f, offsetY + (y * quadHeight)); var tr = new Vector3(tl.X + quadWidth, 0.0f, tl.Z); var bl = new Vector3(tl.X, 0.0f, tl.Z + quadHeight); var br = new Vector3(tl.X + quadWidth, 0.0f, tl.Z + quadHeight); Indexes[indexOffset++] = geomOffset; Indexes[indexOffset++] = (geomOffset + 1); Indexes[indexOffset++] = (geomOffset + 2); Indexes[indexOffset++] = (geomOffset + 2); Indexes[indexOffset++] = (geomOffset + 3); Indexes[indexOffset++] = geomOffset; short tx = (short)(x + 1), ty = (short)(y + 1); if (blueprint.GetFloor(tx, ty, 1).Pattern == 0 && (blueprint.GetWall(tx, ty, 1).Segments & (WallSegments.HorizontalDiag | WallSegments.VerticalDiag)) == 0) { BladeIndexes[bindexOffset++] = geomOffset; BladeIndexes[bindexOffset++] = (geomOffset + 1); BladeIndexes[bindexOffset++] = (geomOffset + 2); BladeIndexes[bindexOffset++] = (geomOffset + 2); BladeIndexes[bindexOffset++] = (geomOffset + 3); BladeIndexes[bindexOffset++] = geomOffset; } Color tlCol = Color.Lerp(LightGreen, LightBrown, GrassState[y * quads + x]); Color trCol = Color.Lerp(LightGreen, LightBrown, GrassState[y * quads + ((x + 1) % quads)]); Color blCol = Color.Lerp(LightGreen, LightBrown, GrassState[((y + 1) % quads) * quads + x]); Color brCol = Color.Lerp(LightGreen, LightBrown, GrassState[((y + 1) % quads) * quads + ((x + 1) % quads)]); Geom[geomOffset++] = new TerrainVertex(tl, tlCol.ToVector4(), new Vector2(x * 64, y * 64), GrassState[y * quads + x]); Geom[geomOffset++] = new TerrainVertex(tr, trCol.ToVector4(), new Vector2((x + 1) * 64, y * 64), GrassState[y * quads + ((x + 1) % quads)]); Geom[geomOffset++] = new TerrainVertex(br, brCol.ToVector4(), new Vector2((x + 1) * 64, (y + 1) * 64), GrassState[((y + 1) % quads) * quads + ((x + 1) % quads)]); Geom[geomOffset++] = new TerrainVertex(bl, blCol.ToVector4(), new Vector2(x * 64, (y + 1) * 64), GrassState[((y + 1) % quads) * quads + x]); } } var rand = new Random(); VertexBuffer = new VertexBuffer(device, typeof(TerrainVertex), Geom.Length, BufferUsage.None); VertexBuffer.SetData(Geom); IndexBuffer = new IndexBuffer(device, IndexElementSize.ThirtyTwoBits, sizeof(int) * Indexes.Length, BufferUsage.None); IndexBuffer.SetData(Indexes); BladePrimitives = (bindexOffset / 3); BladeIndexBuffer = new IndexBuffer(device, IndexElementSize.ThirtyTwoBits, sizeof(int) * Indexes.Length, BufferUsage.None); BladeIndexBuffer.SetData(BladeIndexes); GeomLength = Geom.Length; }
private static StaticBuffer <TerrainVertex> CreateVertexBuffer( GraphicsDevice graphicsDevice, ResourceUploadBatch uploadBatch, HeightMap heightMap, Int32Rect patchBounds, ushort[] indices, out BoundingBox boundingBox, out Triangle[] triangles) { var numVertices = patchBounds.Width * patchBounds.Height; var vertices = new TerrainVertex[numVertices]; var points = new Vector3[numVertices]; var vertexIndex = 0; for (var y = patchBounds.Y; y < patchBounds.Y + patchBounds.Height; y++) { for (var x = patchBounds.X; x < patchBounds.X + patchBounds.Width; x++) { var position = heightMap.GetPosition(x, y); points[vertexIndex] = position; vertices[vertexIndex++] = new TerrainVertex { Position = position, Normal = heightMap.Normals[x, y], UV = new Vector2(x, y) }; } } boundingBox = BoundingBox.CreateFromPoints(points); triangles = new Triangle[(patchBounds.Width - 1) * (patchBounds.Height) * 2]; var triangleIndex = 0; var indexIndex = 0; for (var y = 0; y < patchBounds.Height - 1; y++) { for (var x = 0; x < patchBounds.Width - 1; x++) { // Triangle 1 triangles[triangleIndex++] = new Triangle { V0 = points[indices[indexIndex++]], V1 = points[indices[indexIndex++]], V2 = points[indices[indexIndex++]] }; // Triangle 2 triangles[triangleIndex++] = new Triangle { V0 = points[indices[indexIndex++]], V1 = points[indices[indexIndex++]], V2 = points[indices[indexIndex++]] }; } } return(StaticBuffer.Create( graphicsDevice, uploadBatch, vertices)); }
public PatchContent Build() { #region Local vertex buffer // create local vertex buffer for patch int numVertices = _patchSize * _patchSize; VertexBufferContent localVertexBuffer = new VertexBufferContent(numVertices); // fill vertex buffer TerrainVertex[] vertices = new TerrainVertex[numVertices]; int nStartX = _patchOffsetX * (_patchSize - 1); int nStartY = _patchOffsetY * (_patchSize - 1); int nEndX = nStartX + _patchSize; int nEndY = nStartY + _patchSize; float fMinZ = float.MaxValue, fMaxZ = float.MinValue; int index = 0; for (int y = nStartY; y < nEndY; y++) { for (int x = nStartX; x < nEndX; x++) { // write local data float fZ = _heightMap[x, y]; if (fZ < fMinZ) fMinZ = fZ; if (fZ > fMaxZ) fMaxZ = fZ; Vector2 texCoords1 = new Vector2(x / (float) (_heightMap.Width - 1), y / (float) (_heightMap.Height - 1)); Vector2 texCoords2 = texCoords1 * _detailTextureTiling; vertices[index++] = new TerrainVertex( new Vector3(x * _horizontalScale, fZ, y * _horizontalScale), new Vector3(0, 1, 0), texCoords1, texCoords2); } } localVertexBuffer.Write(0, VertexBufferContent.SizeOf(typeof(TerrainVertex)), vertices); localVertexBuffer.VertexDeclaration = new VertexDeclarationContent(); foreach (VertexElement vertexElement in TerrainVertex.VertexDeclaration.GetVertexElements()) localVertexBuffer.VertexDeclaration.VertexElements.Add(vertexElement); #endregion LevelContent[] levels = new LevelContent[_numLevels]; for (int i = 0; i < _numLevels; i++) { LevelContentBuilder levelContentBuilder = new LevelContentBuilder(_heightMap, _patchSize, _numLevels, i, nStartX, nEndX, nStartY, nEndY); levels[i] = levelContentBuilder.Build(); } #region Bounding box, centre, and offset BoundingBox boundingBox = new BoundingBox( new Vector3(nStartX * _horizontalScale, fMinZ, nStartY * _horizontalScale), new Vector3(nEndX * _horizontalScale, fMaxZ, nEndY * _horizontalScale)); float fAverageZ = (fMinZ + fMaxZ) / 2.0f; Vector3 center = new Vector3( (nStartX + ((_patchSize - 1) / 2.0f)) * _horizontalScale, fAverageZ, (nStartY + ((_patchSize - 1) / 2.0f)) * _horizontalScale); Vector2 offset = new Vector2( (_patchOffsetX * (_patchSize - 1)) * _horizontalScale, (_patchOffsetY * (_patchSize - 1)) * _horizontalScale); #endregion return new PatchContent { VertexBuffer = localVertexBuffer, Levels = levels, BoundingBox =boundingBox, Center = center, Offset = offset }; }
//this function calculates the actual height/colour/etc of a vertex. //this should be corrected to generate from a WorldMap. TerrainVertex GainVertex2(int X, int Y, int DX, int DY, int Seed = 0) { TerrainVertex v = new TerrainVertex(); float One32nd = 1.0f / 64.0f; v.Position.X = X; v.Position.Z = Y; int MapX = X + DX * (BlockSize); int MapY = Y + DY * (BlockSize); // Z = 0; // v.Position.Y= noise.PerlinNoise3F(MapX, MapY, Z, 1, One32nd / 1, One32nd / 1, One32nd / 1); //* // v.Position.Y *= 10; //v.Position.Y += 6; // v.Position.Y *= 10; float to256 = 1 / 256; float H = 0.0f; Simplex.Seed = this.Seed; MapY += 65535; MapX += 65535; //land/water scaling H = Simplex.CalcPixel2D(MapX, MapY, 1f / 16384f) - 35f; //values from -35 to 220ish. H += (Simplex.CalcPixel2D(MapX, MapY, 1f / 256) / 16); //+-16 variation, values -51..246 float Hills = Simplex.CalcPixel2D(MapX, MapY, 1f / 256) - 128; Hills /= 16; //+-8 noise //*/ v.Position.Y = H; float Temp = Simplex.CalcPixel2D(MapX, MapY, 1f / 4024f); //0 to 255 temp int Blend1 = Math.Min((int)(Temp * 2), 160); int Blend2 = Math.Min((510 - (int)(Temp * 2)), 200); Color grass = new Color(Blend1, Blend2, 0); Color sea = new Color(0, 50, 255); Color Snow = new Color(245, 245, 255); Color sand = new Color(200, 200, 100); Color sand2 = new Color(150, 150, 50); sea = sand; v.Color = grass; // v.Color = float BeachRange = 12; float SnowRange = 185; float GroundBaseline = WaterHeight + BeachRange; if (H < WaterHeight) { v.MultiTexData.Z = 1; v.Color = sea; } if (H > WaterHeight && H < GroundBaseline) { float dif = H - WaterHeight; dif /= BeachRange; dif = (float)Math.Pow(dif, 7.3); v.Position.Y = WaterHeight + dif * BeachRange; v.Color = sand; v.MultiTexData.Z = 1; } if (H > GroundBaseline) { unchecked { Simplex.Seed = Simplex.Seed ^ (int)0xFFFFFFFF; } //hill horizontal scale float random1 = Simplex.CalcPixel2D(MapX + 1112, MapY + 13123, 2f) / 512f; float random2 = Simplex.CalcPixel2D(MapX + 1112, MapY + 13123, 1f) / 1024f; float ground = Simplex.CalcPixel2D(MapX + 1112, MapY + 13123, 0.0006125f / 1f); float beachdist = H - GroundBaseline; float evenness = 16f; ground /= 256f; ground = (float)(Math.Pow(ground, 4.4)); ground *= 256f; beachdist /= evenness; beachdist = MathHelper.Clamp(beachdist, 0.0f, 1.0f); float finalground = GroundBaseline + ground * beachdist + Hills * beachdist + (random1 + random2) * beachdist; v.Position.Y = finalground;// + random1 + random2; v.MultiTexData.Z = (float)Math.Pow((float)MathHelper.Clamp((((Temp / 2f) - finalground + 76f) / 100f), 0f, 1f), 3f); if (v.MultiTexData.Z < 0.2f) { v.MultiTexData.Z = 0f; } if (finalground > SnowRange) { v.Color = Snow; v.MultiTexData.Z = 0.20f; } } // v.Color = new Color(Math.Min(255, (int)H), 0, 0); v.TextureCoordinate.X = X / 4f; v.TextureCoordinate.Y = Y / 4f; v.Normal = new Vector3(0, 0, 0); // v.Position.Y = 6; return(v); }
/// <summary> /// This implements the method in resource, to load the mesh data(vertex buffer, index buffer) /// of this terrain tile. /// </summary> protected override void load() { // 读取地形数据 // This line will load the terrain data in this tile. float[] data = TerrainData.Instance.GetData(tileX, tileY, terrEdgeSize); float radtc = MathEx.Degree2Radian(tileCol); float radtl = MathEx.Degree2Radian(tileLat); float radSpan = MathEx.Degree2Radian(10); int vertexCount = terrEdgeSize * terrEdgeSize; int terrEdgeLen = terrEdgeSize - 1; if (terrEdgeSize == 33) { material.SetEffect(EffectManager.Instance.GetModelEffect(TerrainEffect33Factory.Name)); } else { material.SetEffect(EffectManager.Instance.GetModelEffect(TerrainEffect17Factory.Name)); } #region 顶点数据 vtxDecl = factory.CreateVertexDeclaration(TerrainVertex.Elements); vtxBuffer = factory.CreateVertexBuffer(vertexCount, vtxDecl, BufferUsage.WriteOnly); TerrainVertex[] vtxArray = new TerrainVertex[vertexCount]; float cellAngle = radSpan / (float)terrEdgeLen; #region 计算顶点坐标 // Caluclate the position of each vertex // i为纬度方向 // i is in the latitude direction for (int i = 0; i < terrEdgeSize; i++) { // j为经度方向 // j is in the longitude direction for (int j = 0; j < terrEdgeSize; j++) { Vector3 pos = PlanetEarth.GetPosition(radtc + j * cellAngle, radtl - i * cellAngle); int index = i * terrEdgeSize + j; // 计算海拔高度 // calculate the elevation float height = (data[index] - TerrainMeshManager.PostZeroLevel) * TerrainMeshManager.PostHeightScale; //if (height > 0) //{ // height = (height - 0) * TerrainMeshManager.PostHeightScale; //} //else //{ // height *= TerrainMeshManager.PostHeightScale; // height -= 10; // //if (height < -30) // // height = -30; //} Vector3 normal = pos; normal.Normalize(); vtxArray[index].Position = pos + normal * height; // this index is used to generate detailed texture coordinate in vertex shader vtxArray[index].Index = index; // map the texture coordinate for global texturing float curCol = radtc + j * cellAngle; float curLat = radSpan + radtl - i * cellAngle; curCol += MathEx.PIf; curLat -= MathEx.Degree2Radian(10); vtxArray[index].u = 0.5f * curCol / MathEx.PIf; vtxArray[index].v = (-curLat + MathEx.PiOver2) / MathEx.PIf; } } #endregion #endregion #region 索引数据 SharedIndexData sindexData = TerrainMeshManager.Instance.GetIndexData(terrEdgeSize); indexBuffer = sindexData.Index; #endregion #region 构造GeomentryData defGeometryData = new GeomentryData(); defGeometryData.VertexDeclaration = vtxDecl; defGeometryData.VertexSize = TerrainVertex.Size; defGeometryData.VertexBuffer = vtxBuffer; defGeometryData.IndexBuffer = indexBuffer; defGeometryData.PrimCount = indexBuffer.IndexCount / 3; defGeometryData.VertexCount = terrEdgeSize * terrEdgeSize; defGeometryData.PrimitiveType = RenderPrimitiveType.TriangleList; defGeometryData.BaseVertex = 0; #endregion vtxBuffer.SetData <TerrainVertex>(vtxArray); }