public void UpdateMeshFrom3dBoundedByHeightMap( float[,] heightMap, Defines.MapParams mapSize, Vector3 samplingRate, float spaceCreationThreshold) { var spaceMap = HeightMapAssembler.AssembleSpaceMap(mapSize); var vertices = new List <Vector3>(); var colors = new List <Color>(); for (int x = 0; x < mapSize.width; x += (int)samplingRate.x) { for (int y = 0; y < mapSize.height; y += (int)samplingRate.y) { for (int z = 0; z < mapSize.depth; z += (int)samplingRate.z) { //Debug.Log("Vertex " + x + ", " + y + ", " + z + " = " + spaceMap[x, y, z]); if (heightMap[x, y] * mapSize.depth > z && spaceMap[x, y, z] > spaceCreationThreshold) { vertices.Add(new Vector3(x / (int)samplingRate.x, y / (int)samplingRate.y, z)); colors.Add(ColorPicker.GetColor(colorParams, heightMap[x, y])); } } } } var mesh = VoxelHandler.CreateMulticubeMesh(vertices, colors); filter.mesh = mesh; }
public static Mesh CreateMulticubeMeshOptimized( float[,] heightMap, System.Func <float, float, float, bool> spaceProbeFunc, Vector3 samplingRate, Defines.MapParams mapSize, Defines.ColorThreshold[] colorParams) { var meshVertices = new List <Vector3>(); var triangles = new List <int>(); var vertColors = new List <Color>(); foreach (var face in MarchingCubeFaces.GetFacesToDraw(spaceProbeFunc, heightMap, samplingRate, mapSize)) { if (face.x >= heightMap.GetLength(0) || face.y >= heightMap.GetLength(1)) { continue; } //Debug.Log("FACE: " + face.x + " " + face.y + " " + face.z); var color = ColorPicker.GetColor(colorParams, heightMap[Mathf.Abs(Mathf.FloorToInt(face.x)), Mathf.Abs(Mathf.FloorToInt(face.y))]); vertColors.Add(color); vertColors.Add(color); vertColors.Add(color); vertColors.Add(color); var l = meshVertices.Count; // ToDo: I suppose this logic could be entirely moved to GetFaceVerts(face, cubeOfOrigin) var squareVerts = MarchingCubeFaces.GetFaceVerts(face); if (Mathf.Floor(face.y) != face.y && spaceProbeFunc(Mathf.Floor(face.x), Mathf.Floor(face.y), Mathf.Floor(face.z)) || Mathf.Floor(face.x) != face.x && !spaceProbeFunc(Mathf.Floor(face.x), Mathf.Floor(face.y), Mathf.Floor(face.z)) || Mathf.Floor(face.z) != face.z && !spaceProbeFunc(Mathf.Floor(face.x), Mathf.Floor(face.y), Mathf.Floor(face.z))) { meshVertices.Add(squareVerts[0]); meshVertices.Add(squareVerts[2]); meshVertices.Add(squareVerts[1]); meshVertices.Add(squareVerts[3]); } else { foreach (var v in squareVerts) { meshVertices.Add(v); } } foreach (var t in MarchingCubeFaces.trisList) { triangles.Add(t + l); } } var mesh = new Mesh { vertices = meshVertices.ToArray(), triangles = triangles.ToArray(), colors = vertColors.ToArray() }; mesh.RecalculateNormals(); mesh.RecalculateBounds(); return(mesh); }
public void UpdateMeshFrom3dBoundedByHeightMapOptimized( float[,] heightMap, Defines.MapParams mapSize, Vector3 samplingRate, System.Func <float, float, float, bool> spaceProbingFunction) { cleanupChunks(); // ToDo: Could pass height generating function instead of height map to // free ourselves from array.Length() sanity checks and solve potential rounding problems System.Func <float, float, float, bool> spaceProbingFunc = (x, y, z) => z == 0 || (spaceProbingFunction(x, y, z) && x >= 0 && y >= 0 && z >= 0 && x < heightMap.GetLength(0) && y < heightMap.GetLength(1) && z < heightMap[(int)x, (int)y] * mapSize.depth); int xBot = 0; while (xBot < mapSize.width) { int xTop = Mathf.Min(mapSize.width, xBot + chunkSize); int yBot = 0; while (yBot < mapSize.height) { int yTop = Mathf.Min(mapSize.height, yBot + chunkSize); var chunkMapSize = new Defines.MapParams( new Vector3(xTop - xBot, yTop - yBot, mapSize.depth), new Vector3(xBot, yBot, mapSize.offset.z)); GameObject newChunk = prepareNewChunk(); var chunkMesh = VoxelHandler.CreateMulticubeMeshOptimized(heightMap, spaceProbingFunc, samplingRate, chunkMapSize, colorParams); chunkMesh.Optimize(); newChunk.GetComponent <MeshFilter>().mesh = chunkMesh; yBot += chunkSize; } xBot += chunkSize; } }
public static List <V3> GetFacesToDraw(System.Func <float, float, float, bool> spaceProbeFunc, float[,] heightMap, V3 samplingRate, Defines.MapParams mapSize) { // Todo: Consider refactoring samplingRate into mapSize Queue <V3> pointsToHandle = new Queue <V3>(); HashSet <V3> pointsHandled = new HashSet <V3>(); List <V3> result = new List <V3>(); V3 startingPoint = FindStartingPoint(spaceProbeFunc, heightMap, samplingRate, mapSize); pointsToHandle.Enqueue(startingPoint); while (pointsToHandle.Count > 0) { var currentPoint = pointsToHandle.Dequeue(); pointsHandled.Add(currentPoint); foreach (var neighbor in NeumannNeighborhood3D(currentPoint)) { var inSpace = spaceProbeFunc( neighbor.x * samplingRate.x, neighbor.y * samplingRate.y, neighbor.z * samplingRate.z); var isThisInBounds = InBounds(neighbor, mapSize, samplingRate); // we know at this point that currentPoint is solid, so we only create faces // between it and its non-solid neighbors if (!inSpace && isThisInBounds) { var face = GetFaceMidpoint(currentPoint, neighbor); result.Add(face); foreach (var n in GetPositionsNeighboringFace(face)) { var isInBounds = InBounds(n, mapSize, samplingRate); var belowGround = isInBounds && ( heightMap[ (int)(n.x * samplingRate.x), (int)(n.y * samplingRate.y)] * mapSize.depth > n.z); var wasAlreadyHandled = pointsHandled.Contains(n); var spaceCreationAllowsIt = spaceProbeFunc( (n.x) * samplingRate.x, (n.y) * samplingRate.y, (n.z) * samplingRate.z); // ToDo: this could be optimized, I think, by looking at the output of all faces // and removing ones that cannot have neighboring face with the cube if (belowGround && !wasAlreadyHandled && spaceCreationAllowsIt) { pointsToHandle.Enqueue(n); pointsHandled.Add(n); } } } } } return(result); }
public static V3 FindStartingPoint(System.Func <float, float, float, bool> spaceProbeFunc, float[,] heightMap, V3 samplingRate, Defines.MapParams mapSize) { // Todo: find a sensible null check for C# // Todo: this looks from the middle of the chunk and iterates over 1/4 of its area // This will cause issues when there are holes in the map. // Might need to look for a more sophisticated way. int x = (int)(mapSize.width) / (int)samplingRate.x / 2 + (int)mapSize.offset.x; while (x < mapSize.width + mapSize.offset.x) { int y = (int)(mapSize.height) / (int)samplingRate.y / 2 + (int)mapSize.offset.y; while (y < mapSize.height + mapSize.offset.y) { int z = (int)(heightMap[x, y] * mapSize.depth) + 1; while (z > 0) { var isInSpace = spaceProbeFunc( x * samplingRate.x, y * samplingRate.y, z * samplingRate.z); var isBelowGround = heightMap[ (int)(x * samplingRate.x), (int)(y * samplingRate.y)] * mapSize.depth > z * samplingRate.z; if (isInSpace && isBelowGround) { return(new V3(x, y, z)); } z -= (int)samplingRate.z; } y += (int)samplingRate.y; } x += (int)samplingRate.x; } Debug.LogWarning("Not a single vertex is of use. Too bad."); return(new V3(0, 0, 0)); }
public static bool InBounds(V3 point, Defines.MapParams bounds, V3 sampling) { return(point.x >= bounds.offset.x && point.y >= bounds.offset.y && point.x < (bounds.width + bounds.offset.x) / sampling.x && point.y < (bounds.height + bounds.offset.y) / sampling.y); }
public static float[,] Generate(IEnumerable <Defines.GeneratorLayer> layerParams, Defines.MapParams mapSize, Vector3 globalScale) { float[,] result = new float[mapSize.width, mapSize.height]; foreach (Defines.GeneratorLayer layer in layerParams) { System.Func <float, float, float> generatorMethod = GeneratorMethods2d.ChooseFunc(layer); float[,] map = HeightMapAssembler.AssembleHeightMap(mapSize, generatorMethod, globalScale); // prone to change when I find an elegant way of doing this (Zip?) for (int i = 0; i < mapSize.width; i++) { for (int j = 0; j < mapSize.height; j++) { result[i, j] += map[i, j] * layer.significance; } } } Normalize(result); return(result); }