public static ObjectPoint[] CreateObjectPoints(int uniformSize, MeshSettings meshSettings, ResourceMapSettings resourceMapSettings, BiomeMap biomeMap, HeightMap heightMap, TerrainChunk terrainChunk) { string fileName = $"chunkInfo{terrainChunk.Coord.x}{terrainChunk.Coord.y}.dat"; // Try to load a save file if (File.Exists(SaveDataManager.WorldDataPath + fileName)) { return(ObjectMapLoader.LoadObjectMap(terrainChunk, SaveDataManager.WorldDataPath).ObjectPoints); } else { //ResourceMap resourceMap = ResourceMapGenerator.GenerateResourceMap(uniformSize, resourceMapSettings, terrainChunk.SampleCenter); ResourceMap resourceMap = ResourceMapGenerator.GenerateResourceMap(uniformSize, resourceMapSettings, terrainChunk.SampleCenter, biomeMap, heightMap); List <ObjectPoint> tempObjectPoints = new List <ObjectPoint>(); // Resource points to object points foreach (var resourcePoint in resourceMap.resourcePoints) { int x = resourcePoint.x; int z = resourcePoint.z; HeightMapLayer layer = heightMap.GetLayer(x + 1, z + 1); // Don't spawn objects on water. if (layer.IsWater) { continue; } float height = heightMap.Values[x + 1, z + 1]; Vector3 position = new Vector3(terrainChunk.Bounds.center.x, 0f, terrainChunk.Bounds.center.y) + new Vector3((x - (uniformSize - 1) / 2f) * meshSettings.MeshScale, height, (z - (uniformSize - 1) / 2f) * -meshSettings.MeshScale); // Create a seeded System.Random for the rotation based on the position float a = position.x + position.y; float b = position.z + position.y; System.Random rand = new System.Random((int)(0.5 * (a + b) * (a + b + 1) + b)); Quaternion rotation = Quaternion.Euler(new Vector3(0f, rand.Next(0, 360), 0f)); int chunkPartSize = uniformSize / meshSettings.ChunkPartSizeRoot + 1; int coordX = Mathf.FloorToInt(x / chunkPartSize) - 1; int coordZ = Mathf.FloorToInt(z / chunkPartSize) - 1; Vector2 chunkPartCoords = terrainChunk.Coord * meshSettings.ChunkPartSizeRoot + new Vector2(coordX, -coordZ); tempObjectPoints.Add(new ObjectPoint(position, rotation, resourcePoint.biomeId, resourcePoint.worldResourcePrefabId, chunkPartCoords.x, chunkPartCoords.y)); } return(tempObjectPoints.ToArray()); } }
/// <summary> /// Generates a <see cref="DataMap"/> that consists of a heightmap, biomeMap and resourceMap based on a single size. Divides the resourceMap in <see cref="DataMap.ChunkParts"/> based on <see cref="MeshSettings.ChunkPartSizeRoot"/> /// </summary> /// <param name="terrainChunk">The terrain chunk that requested the DataMap.</param> /// <returns></returns> public static DataMap GenerateDataMap(MeshSettings meshSettings, HeightMapSettings heightMapSettings, BiomeMapSettings biomeMapSettings, ResourceMapSettings resourceMapSettings, TerrainChunk terrainChunk) { int size = meshSettings.NumVertsPerLine; int uniformSize = size - 2; // Generate data maps HeightMap heightMap = HeightMapGenerator.GenerateHeightMap(size, heightMapSettings, terrainChunk.SampleCenter); BiomeMap biomeMap = BiomeMapGenerator.GenerateBiomeMap(uniformSize, biomeMapSettings, terrainChunk.SampleCenter); // Create chunk parts Dictionary <Vector2, TerrainChunkPart> chunkParts = CreateChunkParts(uniformSize, meshSettings, terrainChunk); FillChunkParts(uniformSize, ref chunkParts, meshSettings, resourceMapSettings, biomeMap, heightMap, terrainChunk); return(new DataMap(uniformSize, heightMap, biomeMap, chunkParts)); }
public TerrainChunk(Vector2 coord, HeightMapSettings heightMapSettings, BiomeMapSettings biomeMapSettings, ResourceMapSettings resourceMapSettings, MeshSettings meshSettings, LODInfo[] detailLevels, int colliderLODIndex, Transform parent, TerrainViewer viewer, Material terrainMeshMaterial) { this.Coord = coord; this.HeightMapSettings = heightMapSettings; this.BiomeMapSettings = biomeMapSettings; this.ResourceMapSettings = resourceMapSettings; this.MeshSettings = meshSettings; this.detailLevels = detailLevels; this.colliderLODIndex = colliderLODIndex; this.Viewer = viewer; SampleCenter = coord * meshSettings.MeshWorldSize / meshSettings.MeshScale; Vector2 position = coord * meshSettings.MeshWorldSize; Bounds = new Bounds(position, Vector2.one * meshSettings.MeshWorldSize); MeshObject = new GameObject("Terrain Chunk"); meshRenderer = MeshObject.AddComponent <MeshRenderer>(); meshFilter = MeshObject.AddComponent <MeshFilter>(); meshCollider = MeshObject.AddComponent <MeshCollider>(); meshRenderer.material = terrainMeshMaterial; MeshObject.AddComponent <TerrainChunkInteraction>().TerrainChunk = this; MeshObject.transform.position = new Vector3(position.x, 0, position.y); MeshObject.transform.parent = parent; MeshObject.layer = parent.gameObject.layer; SetVisible(false); lodMeshes = new LODMesh[detailLevels.Length]; for (int i = 0; i < detailLevels.Length; i++) { lodMeshes[i] = new LODMesh(detailLevels[i].Lod); lodMeshes[i].UpdateCallback += UpdateTerrainChunk; if (i == colliderLODIndex) { lodMeshes[i].UpdateCallback += UpdateCollisionMesh; } } MaxViewDistance = detailLevels[detailLevels.Length - 1].VisibleDistanceThreshold; }
private static void FillChunkParts(int uniformSize, ref Dictionary <Vector2, TerrainChunkPart> chunkParts, MeshSettings meshSettings, ResourceMapSettings resourceMapSettings, BiomeMap biomeMap, HeightMap heightMap, TerrainChunk terrainChunk) { ObjectPoint[] objectPoints = CreateObjectPoints(uniformSize, meshSettings, resourceMapSettings, biomeMap, heightMap, terrainChunk); // Fill chunk parts for (int i = 0; i < objectPoints.Length; i++) { Vector2 chunkPartCoords = new Vector2(objectPoints[i].chunkPartCoordX, objectPoints[i].chunkPartCoordZ); TerrainChunkPart terrainChunkPart = chunkParts[chunkPartCoords]; terrainChunkPart.AddObjectPoint(objectPoints[i]); } }
private static Dictionary <Vector2, TerrainChunkPart> CreateChunkParts(int uniformSize, MeshSettings meshSettings, TerrainChunk terrainChunk) { Dictionary <Vector2, TerrainChunkPart> chunkParts = new Dictionary <Vector2, TerrainChunkPart>(); int chunkRangeHalf = Mathf.FloorToInt(meshSettings.ChunkPartSizeRoot / 2f); // ONLY WORKS FOR UNEVEN NUMBERS AT THE MOMENT for (int x = -chunkRangeHalf; x <= chunkRangeHalf; x++) { for (int z = -chunkRangeHalf; z <= chunkRangeHalf; z++) { Vector2 chunkPartCoord = terrainChunk.Coord * meshSettings.ChunkPartSizeRoot + new Vector2(x, z); Vector3 partWorldPosition = new Vector3(terrainChunk.Bounds.size.x / meshSettings.ChunkPartSizeRoot * chunkPartCoord.x, 0f, terrainChunk.Bounds.size.y / meshSettings.ChunkPartSizeRoot * chunkPartCoord.y); chunkParts.Add(chunkPartCoord, new TerrainChunkPart(chunkPartCoord, partWorldPosition, terrainChunk)); } } return(chunkParts); }
public void RequestMesh(HeightMap heightMap, MeshSettings meshSettings) { HasRequestedMesh = true; ThreadedDataRequester.RequestData(() => MeshGenerator.GenerateTerrainMesh(heightMap.Values, meshSettings, lod), OnMeshDataReceived); }
/// <summary> /// Generates a MeshData object that can be used to construct a mesh. /// </summary> /// <param name="map">The map values.</param> /// <param name="meshSettings">The settings for the mesh.</param> /// <param name="levelOfDetail">0 for highest detail.</param> public static MeshData GenerateTerrainMesh(float[,] map, MeshSettings meshSettings, int levelOfDetail) { int skipIncrement = (levelOfDetail == 0) ? 1 : levelOfDetail * 2; int numVertsPerLine = meshSettings.NumVertsPerLine; Vector2 topLeft = new Vector2(-1, 1) * meshSettings.MeshWorldSize / 2f; MeshData meshData = new MeshData(numVertsPerLine, skipIncrement, meshSettings.UseFlatShading); int[,] vertexIndicesMap = new int[numVertsPerLine, numVertsPerLine]; int meshVertexIndex = 0; int outOfMeshVertexIndex = -1; for (int y = 0; y < numVertsPerLine; y++) { for (int x = 0; x < numVertsPerLine; x++) { bool isOutOfMeshVertex = y == 0 || y == numVertsPerLine - 1 || x == 0 || x == numVertsPerLine - 1; bool isSkippedVertex = x > 2 && x < numVertsPerLine - 3 && y > 2 && y < numVertsPerLine - 3 && ((x - 2) % skipIncrement != 0 || (y - 2) % skipIncrement != 0); if (isOutOfMeshVertex) { vertexIndicesMap[x, y] = outOfMeshVertexIndex; outOfMeshVertexIndex--; } else if (!isSkippedVertex) { vertexIndicesMap[x, y] = meshVertexIndex; meshVertexIndex++; } } } for (int y = 0; y < numVertsPerLine; y++) { for (int x = 0; x < numVertsPerLine; x++) { bool isSkippedVertex = x > 2 && x < numVertsPerLine - 3 && y > 2 && y < numVertsPerLine - 3 && ((x - 2) % skipIncrement != 0 || (y - 2) % skipIncrement != 0); if (!isSkippedVertex) { bool isOutOfMeshVertex = y == 0 || y == numVertsPerLine - 1 || x == 0 || x == numVertsPerLine - 1; bool isMeshEdgeVertex = (y == 1 || y == numVertsPerLine - 2 || x == 1 || x == numVertsPerLine - 2) && !isOutOfMeshVertex; bool isMainVertex = (x - 2) % skipIncrement == 0 && (y - 2) % skipIncrement == 0 && !isOutOfMeshVertex && !isMeshEdgeVertex; bool isEdgeConnectionVertex = (y == 2 || y == numVertsPerLine - 3 || x == 2 || x == numVertsPerLine - 3) && !isOutOfMeshVertex && !isMeshEdgeVertex && !isMainVertex; int vertexIndex = vertexIndicesMap[x, y]; Vector2 percent = new Vector2(x - 1, y - 1) / (numVertsPerLine - 3); Vector2 vertexPosition2D = topLeft + new Vector2(percent.x, -percent.y) * meshSettings.MeshWorldSize; float height = map[x, y]; if (isEdgeConnectionVertex) { bool isVertical = x == 2 || x == numVertsPerLine - 3; int dstToMainVertexA = ((isVertical) ? y - 2 : x - 2) % skipIncrement; int dstToMainVertexB = skipIncrement - dstToMainVertexA; float dstPercentFromAToB = dstToMainVertexA / (float)skipIncrement; Coord coordA = new Coord((isVertical) ? x : x - dstToMainVertexA, (isVertical) ? y - dstToMainVertexA : y); Coord coordB = new Coord((isVertical) ? x : x + dstToMainVertexB, (isVertical) ? y + dstToMainVertexB : y); float heightMainVertexA = map[coordA.x, coordA.y]; float heightMainVertexB = map[coordB.x, coordB.y]; height = heightMainVertexA * (1 - dstPercentFromAToB) + heightMainVertexB * dstPercentFromAToB; EdgeConnectionVertexData edgeConnectionVertexData = new EdgeConnectionVertexData(vertexIndex, vertexIndicesMap[coordA.x, coordA.y], vertexIndicesMap[coordB.x, coordB.y], dstPercentFromAToB); meshData.DeclareEdgeConnectionVertex(edgeConnectionVertexData); } meshData.AddVertex(new Vector3(vertexPosition2D.x, height, vertexPosition2D.y), percent, vertexIndex); bool createTriangle = x < numVertsPerLine - 1 && y < numVertsPerLine - 1 && (!isEdgeConnectionVertex || (x != 2 && y != 2)); if (createTriangle) { int currentIncrement = (isMainVertex && x != numVertsPerLine - 3 && y != numVertsPerLine - 3) ? skipIncrement : 1; int a = vertexIndicesMap[x, y]; int b = vertexIndicesMap[x + currentIncrement, y]; int c = vertexIndicesMap[x, y + currentIncrement]; int d = vertexIndicesMap[x + currentIncrement, y + currentIncrement]; meshData.AddTriangle(a, d, c); meshData.AddTriangle(d, a, b); } } } } meshData.ProcessMesh(); return(meshData); }