private List <SectorTuple> GetSectorTuplesForTriangle(GeoTerrainTriangle t) { List <SectorTuple> indices = new List <SectorTuple>(); float dividerWidth = mWidth / mSectorMap.GetLength(0); float dividerDepth = mDepth / mSectorMap.GetLength(1); foreach (Vector3 v in t.Vertices) { float tmpF1, tmpF2; tmpF1 = v.X + (mWidth / 2); int tmpIndexX1 = Math.Min((int)(tmpF1 / dividerWidth), mSectorMap.GetLength(0) - 1); int tmpIndexX2 = Math.Min((int)Math.Round(tmpF1 / dividerWidth, 0), mSectorMap.GetLength(0) - 1); tmpF2 = v.Z + (mDepth / 2); int tmpIndexZ1 = Math.Min((int)(tmpF2 / dividerDepth), mSectorMap.GetLength(1) - 1); int tmpIndexZ2 = Math.Min((int)Math.Round(tmpF2 / dividerDepth, 0), mSectorMap.GetLength(1) - 1); SectorTuple st1 = new SectorTuple() { X = tmpIndexX1, Y = tmpIndexZ1 }; SectorTuple st2 = new SectorTuple() { X = tmpIndexX1, Y = tmpIndexZ2 }; SectorTuple st3 = new SectorTuple() { X = tmpIndexX2, Y = tmpIndexZ1 }; SectorTuple st4 = new SectorTuple() { X = tmpIndexX2, Y = tmpIndexZ2 }; if (!indices.Contains(st1)) { indices.Add(st1); } if (!indices.Contains(st2)) { indices.Add(st2); } if (!indices.Contains(st3)) { indices.Add(st3); } if (!indices.Contains(st4)) { indices.Add(st4); } } return(indices); }
private List <int> GetSectorsForGeoTriangle(GeoTerrainTriangle triangle) { List <int> sectorlist = new List <int>(); for (int i = 0; i < mSectors.Count; i++) { foreach (Vector3 vertex in triangle.Vertices) { if (vertex.X >= mSectors[i].Left && vertex.X <= mSectors[i].Right && vertex.Z <= mSectors[i].Front && vertex.Z >= mSectors[i].Back ) { if (!sectorlist.Contains(i)) { sectorlist.Add(i); } } } } return(sectorlist); }
internal GeoMesh BuildTerrain(Vector3 position, string heightMap, float width, float height, float depth, float texRepeatX = 1, float texRepeatY = 1, bool isFile = true) { GeoMesh mmp; try { Assembly a = Assembly.GetEntryAssembly(); using (Stream s = isFile ? File.Open(heightMap, FileMode.Open) : a.GetManifestResourceStream(a.GetName().Name + "." + heightMap)) { using (Bitmap image = new Bitmap(s)) { mDots = image.Width * image.Height; mWidth = width; mDepth = depth; mScaleFactor = height > 0 ? height : 1; mTexX = texRepeatX > 0 ? texRepeatX : 1; mTexY = texRepeatY > 0 ? texRepeatY : 1; if (image.Width < 4 || image.Height < 4 || image.Height > 256 || image.Width > 256) { throw new Exception("Image size too small or too big: width and height need to be >= 4 and <= 256 pixels."); } Debug.WriteLine("Generating terrain from height map: " + heightMap); double mp = Math.Round(mDots / 1000000.0, 3); if (mDots > 1000) { Debug.WriteLine("\tImage pixel count:\t\t" + mp + " megapixel"); if (mp >= 0.5) { Debug.WriteLine("(WARNING: pixel count > 0.5 megapixel! You will experience SERIOUS performance issues with this terrain mapping.)"); } } else { Debug.WriteLine("\tImage pixel count:\t\t" + mDots); } long start = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; mSectorSize = image.Width < image.Height ? (int)Math.Sqrt(image.Width) : (int)Math.Sqrt(image.Height); while (mSectorSize % 4 != 0) { mSectorSize++; } mSectorSizeCoarse = mSectorSize / 4; if (mSectorSize > 144) { mSectorSize = 144; mSectorSizeCoarse = 12; } mSectorWidth = mWidth / mSectorSize; mSectorDepth = mDepth / mSectorSize; mSectorDiameter = (float)Math.Sqrt(mSectorWidth * mSectorWidth + mSectorDepth * mSectorDepth); mSectorWidthCoarse = mWidth / mSectorSizeCoarse; mSectorDepthCoarse = mDepth / mSectorSizeCoarse; for (int i = 0; i < mSectorSizeCoarse; i++) { for (int j = 0; j < mSectorSizeCoarse; j++) { Sector sec = new Sector( (position.X - mWidth / 2) + i * mSectorWidthCoarse, (position.X - mWidth / 2) + (i + 1) * mSectorWidthCoarse, (position.Z + mDepth / 2) - (j + 1) * mSectorDepthCoarse, (position.Z + mDepth / 2) - j * mSectorDepthCoarse ); mSectorsCoarse.Add(sec); mSectorCoarseMap.Add(sec, new List <Sector>()); } } for (int i = 0; i < mSectorSize; i++) { for (int j = 0; j < mSectorSize; j++) { Sector sec = new Sector( (position.X - mWidth / 2) + i * mSectorWidth, (position.X - mWidth / 2) + (i + 1) * mSectorWidth, (position.Z + mDepth / 2) - (j + 1) * mSectorDepth, (position.Z + mDepth / 2) - j * mSectorDepth ); sec.ID = mSectors.Count; mSectors.Add(sec); Sector coarseSector = GetSectorCoarseForSector(sec); mSectorCoarseMap[coarseSector].Add(sec); mSectorTriangleMap.Add(mSectors.Count - 1, new List <GeoTerrainTriangle>()); } } float[,] mHeightMap = new float[image.Width, image.Height]; mCompleteDiameter = (float)Math.Sqrt(mWidth * mWidth + mDepth * mDepth + mScaleFactor * mScaleFactor); float stepWidth = mWidth / (image.Width - 1); float stepDepth = mDepth / (image.Height - 1); Vector3[] points = new Vector3[mDots]; int c = 0; int cFBuffer = 0; int cFBufferUV = 0; float[] VBOVerticesBuffer = new float[mDots * 3]; float[] VBONormalsBuffer = new float[mDots * 3]; float[] VBOUVBuffer = new float[mDots * 2]; float[] VBOTangentBuffer = new float[mDots * 3]; float[] VBOBiTangentBuffer = new float[mDots * 3]; Dictionary <int, Vector3> normalMapping = new Dictionary <int, Vector3>(); Dictionary <int, Vector3> tangentMapping = new Dictionary <int, Vector3>(); Dictionary <int, Vector3> bitangentMapping = new Dictionary <int, Vector3>(); Dictionary <int, int> normalMappingCount = new Dictionary <int, int>(); for (int i = 0; i < image.Width; i++) { for (int j = 0; j < image.Height; j++) { Color tmpColor = image.GetPixel(i, j); float normalizedRGB = ((tmpColor.R + tmpColor.G + tmpColor.B) / 3f) / 255f; mHeightMap[i, j] = normalizedRGB; Vector3 tmp = new Vector3( position.X + i * stepWidth - mWidth / 2, position.Y + mScaleFactor * normalizedRGB, position.Z - mDepth / 2 + j * stepDepth ); points[c] = tmp; VBOVerticesBuffer[cFBuffer + 0] = points[c].X; VBOVerticesBuffer[cFBuffer + 1] = points[c].Y; VBOVerticesBuffer[cFBuffer + 2] = points[c].Z; VBOUVBuffer[cFBufferUV + 0] = i * (1f / (image.Width - 1)); //* mTexX; VBOUVBuffer[cFBufferUV + 1] = j * (1f / (image.Height - 1)); // * mTexY; normalMapping.Add(c, new Vector3(0, 0, 0)); tangentMapping.Add(c, new Vector3(0, 0, 0)); bitangentMapping.Add(c, new Vector3(0, 0, 0)); normalMappingCount.Add(c, 0); //increase counter: c++; cFBuffer += 3; cFBufferUV += 2; } } mmp = new GeoMesh(); mmp.Name = heightMap; // Build indices and triangles: mmp.VAO = GL.GenVertexArray(); GL.BindVertexArray(mmp.VAO); int triangles = 0; List <uint> mIndices = new List <uint>(); int imageHeight = image.Height; Vector3 normalT1 = new Vector3(0, 0, 0); Vector3 normalT2 = new Vector3(0, 0, 0); float deltaU1 = 0; float deltaV1 = 0; float deltaU2 = 0; float deltaV2 = 0; float f = 1.0f; Vector3 tangent = new Vector3(0, 0, 0); Vector3 bitangent = new Vector3(0, 0, 0); for (int i = 0; i < points.Length - imageHeight - 1; i++) { Vector3 tmp; if ((i + 1) % imageHeight == 0) { continue; } // Generate Indices: mIndices.Add((uint)(i + imageHeight + 1)); mIndices.Add((uint)(i + imageHeight)); mIndices.Add((uint)(i)); mIndices.Add((uint)(i)); mIndices.Add((uint)(i + 1)); mIndices.Add((uint)(i + imageHeight + 1)); // Generate Triangle objects: // T1: Vector3 v1 = new Vector3(points[i + imageHeight + 1]); Vector3 v2 = new Vector3(points[i + imageHeight]); Vector3 v3 = new Vector3(points[i]); GeoTerrainTriangle t123 = new GeoTerrainTriangle(v1, v2, v3); normalT1 = t123.Normal; List <int> sectorIds = GetSectorsForGeoTriangle(t123); foreach (int sID in sectorIds) { mSectorTriangleMap[sID].Add(t123); } // tangents and bitangent generation deltaU1 = VBOUVBuffer[(i + imageHeight) * 2] - VBOUVBuffer[(i + imageHeight + 1) * 2]; deltaV1 = VBOUVBuffer[(i + imageHeight) * 2 + 1] - VBOUVBuffer[(i + imageHeight + 1) * 2 + 1]; deltaU2 = VBOUVBuffer[(i + 0) * 2] - VBOUVBuffer[(i + imageHeight + 1) * 2]; deltaV2 = VBOUVBuffer[(i + 0) * 2 + 1] - VBOUVBuffer[(i + imageHeight + 1) * 2 + 1]; f = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1); tangent.X = f * (deltaV2 * t123.edge1.X - deltaV1 * t123.edge2.X); tangent.Y = f * (deltaV2 * t123.edge1.Y - deltaV1 * t123.edge2.Y); tangent.Z = f * (deltaV2 * t123.edge1.Z - deltaV1 * t123.edge2.Z); bitangent.X = f * (-deltaU2 * t123.edge1.X - deltaU1 * t123.edge2.X); bitangent.Y = f * (-deltaU2 * t123.edge1.Y - deltaU1 * t123.edge2.Y); bitangent.Z = f * (-deltaU2 * t123.edge1.Z - deltaU1 * t123.edge2.Z); // Generate their normals for VBO: normalMapping.TryGetValue(i, out tmp); tmp += normalT1; normalMapping[i] = tmp; normalMappingCount[i]++; normalMapping.TryGetValue(i + imageHeight, out tmp); tmp += normalT1; normalMapping[i + imageHeight] = tmp; normalMappingCount[i + imageHeight]++; normalMapping.TryGetValue(i + imageHeight + 1, out tmp); tmp += normalT1; normalMapping[i + imageHeight + 1] = tmp; normalMappingCount[i + imageHeight + 1]++; // map tangents & bitangent here: tangentMapping.TryGetValue(i, out tmp); tmp += tangent; tangentMapping[i] = tmp; tangentMapping.TryGetValue(i + imageHeight, out tmp); tmp += tangent; tangentMapping[i + imageHeight] = tmp; tangentMapping.TryGetValue(i + imageHeight + 1, out tmp); tmp += tangent; tangentMapping[i + imageHeight + 1] = tmp; bitangentMapping.TryGetValue(i, out tmp); tmp += bitangent; bitangentMapping[i] = tmp; bitangentMapping.TryGetValue(i + imageHeight, out tmp); tmp += bitangent; bitangentMapping[i + imageHeight] = tmp; bitangentMapping.TryGetValue(i + imageHeight + 1, out tmp); tmp += bitangent; bitangentMapping[i + imageHeight + 1] = tmp; // ============================ T2 ====================== Vector3 v4 = new Vector3(points[i]); Vector3 v5 = new Vector3(points[i + 1]); Vector3 v6 = new Vector3(points[i + imageHeight + 1]); GeoTerrainTriangle t456 = new GeoTerrainTriangle(v4, v5, v6); normalT2 = t456.Normal; sectorIds.Clear(); sectorIds = GetSectorsForGeoTriangle(t456); foreach (int sID in sectorIds) { mSectorTriangleMap[sID].Add(t456); } // tangents and bitangent generation deltaU1 = VBOUVBuffer[(i + 1) * 2] - VBOUVBuffer[(i) * 2]; deltaV1 = VBOUVBuffer[(i + 1) * 2 + 1] - VBOUVBuffer[(i) * 2 + 1]; deltaU2 = VBOUVBuffer[(i + imageHeight + 1) * 2] - VBOUVBuffer[(i) * 2]; deltaV2 = VBOUVBuffer[(i + imageHeight + 1) * 2 + 1] - VBOUVBuffer[(i) * 2 + 1]; f = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1); tangent.X = f * (deltaV2 * t456.edge1.X - deltaV1 * t456.edge2.X); tangent.Y = f * (deltaV2 * t456.edge1.Y - deltaV1 * t456.edge2.Y); tangent.Z = f * (deltaV2 * t456.edge1.Z - deltaV1 * t456.edge2.Z); bitangent.X = f * (-deltaU2 * t456.edge1.X - deltaU1 * t456.edge2.X); bitangent.Y = f * (-deltaU2 * t456.edge1.Y - deltaU1 * t456.edge2.Y); bitangent.Z = f * (-deltaU2 * t456.edge1.Z - deltaU1 * t456.edge2.Z); normalMapping.TryGetValue(i + imageHeight + 1, out tmp); tmp += normalT2; normalMapping[i + imageHeight + 1] = tmp; normalMappingCount[i + imageHeight + 1]++; normalMapping.TryGetValue(i + 1, out tmp); tmp += normalT2; normalMapping[i + 1] = tmp; normalMappingCount[i + 1]++; normalMapping.TryGetValue(i, out tmp); tmp += normalT2; normalMapping[i] = tmp; normalMappingCount[i]++; // map tangents & bitangent here: tangentMapping.TryGetValue(i + imageHeight + 1, out tmp); tmp += tangent; tangentMapping[i + imageHeight + 1] = tmp; tangentMapping.TryGetValue(i + 1, out tmp); tmp += tangent; tangentMapping[i + 1] = tmp; tangentMapping.TryGetValue(i, out tmp); tmp += tangent; tangentMapping[i] = tmp; bitangentMapping.TryGetValue(i + imageHeight + 1, out tmp); tmp += bitangent; bitangentMapping[i + imageHeight + 1] = tmp; bitangentMapping.TryGetValue(i + 1, out tmp); tmp += bitangent; bitangentMapping[i + 1] = tmp; bitangentMapping.TryGetValue(i, out tmp); tmp += bitangent; bitangentMapping[i] = tmp; triangles += 2; } Debug.WriteLine("\tGenerated triangles:\t" + triangles); cFBuffer = 0; for (int i = 0; i < points.Length; i++) { // Interpolate normals: Vector3 tmp = new Vector3(normalMapping[i].X / normalMappingCount[i], normalMapping[i].Y / normalMappingCount[i], normalMapping[i].Z / normalMappingCount[i]); tmp.Normalize(); Vector3 tTemp = new Vector3(tangentMapping[i].X / normalMappingCount[i], tangentMapping[i].Y / normalMappingCount[i], tangentMapping[i].Z / normalMappingCount[i]); tTemp.Normalize(); Vector3 btTemp = new Vector3(bitangentMapping[i].X / normalMappingCount[i], bitangentMapping[i].Y / normalMappingCount[i], bitangentMapping[i].Z / normalMappingCount[i]); btTemp.Normalize(); VBONormalsBuffer[cFBuffer + 0] = tmp.X; VBONormalsBuffer[cFBuffer + 1] = tmp.Y; VBONormalsBuffer[cFBuffer + 2] = tmp.Z; // tangents and bitangents: VBOTangentBuffer[cFBuffer + 0] = tTemp.X; VBOTangentBuffer[cFBuffer + 1] = tTemp.Y; VBOTangentBuffer[cFBuffer + 2] = tTemp.Z; // tangents and bitangents: VBOBiTangentBuffer[cFBuffer + 0] = btTemp.X; VBOBiTangentBuffer[cFBuffer + 1] = btTemp.Y; VBOBiTangentBuffer[cFBuffer + 2] = btTemp.Z; cFBuffer += 3; } // Generate VBOs: mmp.VBOPosition = GL.GenBuffer(); mmp.VBONormal = GL.GenBuffer(); mmp.VBOTexture1 = GL.GenBuffer(); mmp.VBOTangent = GL.GenBuffer(); mmp.VBOBiTangent = GL.GenBuffer(); mmp.VBOIndex = GL.GenBuffer(); // Vertices: GL.BindBuffer(BufferTarget.ArrayBuffer, mmp.VBOPosition); GL.BufferData(BufferTarget.ArrayBuffer, VBOVerticesBuffer.Length * 4, VBOVerticesBuffer, BufferUsageHint.StaticDraw); GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 0, 0); GL.EnableVertexAttribArray(0); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); // Normals GL.BindBuffer(BufferTarget.ArrayBuffer, mmp.VBONormal); GL.BufferData(BufferTarget.ArrayBuffer, VBONormalsBuffer.Length * 4, VBONormalsBuffer, BufferUsageHint.StaticDraw); GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 0, 0); GL.EnableVertexAttribArray(1); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); // UVs GL.BindBuffer(BufferTarget.ArrayBuffer, mmp.VBOTexture1); GL.BufferData(BufferTarget.ArrayBuffer, VBOUVBuffer.Length * 4, VBOUVBuffer, BufferUsageHint.StaticDraw); GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, 0, 0); GL.EnableVertexAttribArray(2); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); // Tangents GL.BindBuffer(BufferTarget.ArrayBuffer, mmp.VBOTangent); GL.BufferData(BufferTarget.ArrayBuffer, VBOTangentBuffer.Length * 4, VBOTangentBuffer, BufferUsageHint.StaticDraw); GL.VertexAttribPointer(4, 3, VertexAttribPointerType.Float, false, 0, 0); GL.EnableVertexAttribArray(4); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); // Bitangents GL.BindBuffer(BufferTarget.ArrayBuffer, mmp.VBOBiTangent); GL.BufferData(BufferTarget.ArrayBuffer, VBOBiTangentBuffer.Length * 4, VBOBiTangentBuffer, BufferUsageHint.StaticDraw); GL.VertexAttribPointer(5, 3, VertexAttribPointerType.Float, false, 0, 0); GL.EnableVertexAttribArray(5); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); mmp.Indices = mIndices.ToArray(); // Indices: GL.BindBuffer(BufferTarget.ElementArrayBuffer, mmp.VBOIndex); GL.BufferData(BufferTarget.ElementArrayBuffer, mIndices.Count * 4, mmp.Indices, BufferUsageHint.StaticDraw); GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0); mmp.Transform = Matrix4.Identity; GL.BindVertexArray(0); long diff = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - start; Debug.WriteLine("\t...done (" + Math.Round(diff / 1000f, 2) + " seconds)"); } } } catch (Exception ex) { throw new Exception("Terrain could not be created: " + ex.Message); } finally { GLWindow.StartGarbageCollection(); } mmp.Primitive = PrimitiveType.Triangles; return(mmp); }