public Node(Node parent, Vector3 position, int subNodeID, int LOD, RenderType renderType) { densityChangeData = new DensityData(); this.parent = parent; this.position = position; this.subNodeID = subNodeID; this.LOD = LOD; float chunkWidth = (NodeManager.LODSize[LOD] * NodeManager.nodeSize) / 2f; center = new Vector3(position.x + chunkWidth, position.y + chunkWidth, position.z + chunkWidth); setRenderType(renderType); if (parent != null && parent.permanent) permanent = true; NodeManager.nodeCount[LOD]++; float nWidth = NodeManager.LODSize[LOD] * NodeManager.nodeSize; chunkPos.x = (int)(center.x / nWidth); chunkPos.y = (int)(center.y / nWidth); chunkPos.z = (int)(center.z / nWidth); if (LOD == 0) { string dir = getDirectory(); if (Directory.Exists(dir) && File.Exists(dir + "\\densities.txt")) MeshFactory.requestLoad(this); } regenReq = true; MeshFactory.requestMesh(this); }
/// <summary> /// Calculates the normal. /// </summary> /// <param name="p"></param> private static Vector3 calculateDensityNormal(Vector3I p, DensityData densities, int lod) { Vector3 normal = new Vector3(); normal.x = (densities.get(p.x + 1, p.y, p.z) - densities.get(p.x - 1, p.y, p.z)) / (NodeManager.LODSize[lod]); normal.y = (densities.get(p.x, p.y + 1, p.z) - densities.get(p.x, p.y - 1, p.z)) / (NodeManager.LODSize[lod]); normal.z = (densities.get(p.x, p.y, p.z + 1) - densities.get(p.x, p.y, p.z - 1)) / (NodeManager.LODSize[lod]); normal.Normalize(); return(normal); }
/// <summary> /// Applies changes (additive) using another Density Data /// </summary> /// <param name="other"></param> public void applyChanges(DensityData other) { for (int x = 0; x < 19; x++) { for (int y = 0; y < 19; y++) { for (int z = 0; z < 19; z++) { if (other.Values[x, y, z] > -99999f) { Values[x, y, z] = other.Values[x, y, z]; } } } } }
public Node(Node parent, Vector3 position, int subNodeID, int LOD, RenderType renderType) { densityChangeData = new DensityData(); this.parent = parent; this.position = position; this.subNodeID = subNodeID; this.LOD = LOD; float chunkWidth = (NodeManager.LODSize[LOD] * NodeManager.nodeSize) / 2f; center = new Vector3(position.x + chunkWidth, position.y + chunkWidth, position.z + chunkWidth); setRenderType(renderType); if (parent != null && parent.permanent) { permanent = true; } NodeManager.nodeCount[LOD]++; float nWidth = NodeManager.LODSize[LOD] * NodeManager.nodeSize; chunkPos.x = (int)(center.x / nWidth); chunkPos.y = (int)(center.y / nWidth); chunkPos.z = (int)(center.z / nWidth); if (LOD == 0) { string dir = getDirectory(); if (Directory.Exists(dir) && File.Exists(dir + "\\densities.txt")) { MeshFactory.requestLoad(this); } } regenReq = true; MeshFactory.requestMesh(this); }
/// <summary> /// Continuously check if we have chunks to generate. /// </summary> private void recycleLoop() { while (QuixelEngine.isActive()) { Thread.Sleep(1); if (genQueue.Count > 0) { DensityData d = null; lock (genQueue) d = genQueue.Dequeue(); d.dispose(); lock (finishedQueue) finishedQueue.Enqueue(d); } else { Thread.Sleep(30); } } }
/// <summary> /// Recycles (threaded) a density data array. /// </summary> /// <param name="arr"></param> public static void recycleDensityData(DensityData arr) { //Visual bug caused by recycling densities //densityThread.queueRecycleDensity(arr); return; }
/// <summary> /// Sets the change data for this density array. /// Values are pulled from here if available. /// </summary> public void setChangeData(DensityData data) { changeData = data; }
/// <summary> /// Generates the triangles for a specific voxel /// </summary> /// <param name="node">The node that contains the voxel</param> /// <param name="pos">The voxel position (Not real position) [16,16,16]</param> /// <param name="triangleList">The list used to contain triangles made so far</param> /// <param name="densities">The array that contains density information</param> /// <param name="densityNormals">The array that contains density normals</param> private static void generateTriangles(Node node, Vector3I pos, List<Triangle> triangleList, List<int> submeshIDList, int[] subMeshTriCount, DensityData densities, Vector3[, ,] densityNormals) { float size = NodeManager.LODSize[node.LOD]; float[] denses = new float[8]; denses[0] = densities.get(pos.x, pos.y, pos.z + 1); denses[1] = densities.get(pos.x + 1, pos.y, pos.z + 1); denses[2] = densities.get(pos.x + 1, pos.y, pos.z); denses[3] = densities.get(pos.x, pos.y, pos.z); denses[4] = densities.get(pos.x, pos.y + 1, pos.z + 1); denses[5] = densities.get(pos.x + 1, pos.y + 1, pos.z + 1); denses[6] = densities.get(pos.x + 1, pos.y + 1, pos.z); denses[7] = densities.get(pos.x, pos.y + 1, pos.z); byte cubeIndex = 0; if (denses[0] < isolevel) cubeIndex |= 1; if (denses[1] < isolevel) cubeIndex |= 2; if (denses[2] < isolevel) cubeIndex |= 4; if (denses[3] < isolevel) cubeIndex |= 8; if (denses[4] < isolevel) cubeIndex |= 16; if (denses[5] < isolevel) cubeIndex |= 32; if (denses[6] < isolevel) cubeIndex |= 64; if (denses[7] < isolevel) cubeIndex |= 128; if (cubeIndex == 0 || cubeIndex == 255) return; Vector3 origin = new Vector3((size * (pos.x)) , (size * (pos.y)) , (size * (pos.z))); Vector3[] positions = new Vector3[8]; positions[0] = new Vector3(origin.x, origin.y, origin.z + size); positions[1] = new Vector3(origin.x + size, origin.y, origin.z + size); positions[2] = new Vector3(origin.x + size, origin.y, origin.z); positions[3] = new Vector3(origin.x, origin.y, origin.z); positions[4] = new Vector3(origin.x, origin.y + size, origin.z + size); positions[5] = new Vector3(origin.x + size, origin.y + size, origin.z + size); positions[6] = new Vector3(origin.x + size, origin.y + size, origin.z); positions[7] = new Vector3(origin.x, origin.y + size, origin.z); Vector3[][] vertlist = new Vector3[12][]; if (IsBitSet(edgeTable[cubeIndex], 1)) vertlist[0] = VertexInterp(isolevel, positions[0], positions[1], denses[0], denses[1], densityNormals[pos.x, pos.y, pos.z + 1], densityNormals[pos.x + 1, pos.y, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 2)) vertlist[1] = VertexInterp(isolevel, positions[1], positions[2], denses[1], denses[2], densityNormals[pos.x + 1, pos.y, pos.z + 1], densityNormals[pos.x + 1, pos.y, pos.z]); if (IsBitSet(edgeTable[cubeIndex], 4)) vertlist[2] = VertexInterp(isolevel, positions[2], positions[3], denses[2], denses[3], densityNormals[pos.x + 1, pos.y, pos.z], densityNormals[pos.x, pos.y, pos.z]); if (IsBitSet(edgeTable[cubeIndex], 8)) vertlist[3] = VertexInterp(isolevel, positions[3], positions[0], denses[3], denses[0], densityNormals[pos.x, pos.y, pos.z], densityNormals[pos.x, pos.y, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 16)) vertlist[4] = VertexInterp(isolevel, positions[4], positions[5], denses[4], denses[5], densityNormals[pos.x, pos.y + 1, pos.z + 1], densityNormals[pos.x + 1, pos.y + 1, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 32)) vertlist[5] = VertexInterp(isolevel, positions[5], positions[6], denses[5], denses[6], densityNormals[pos.x + 1, pos.y + 1, pos.z + 1], densityNormals[pos.x + 1, pos.y + 1, pos.z]); if (IsBitSet(edgeTable[cubeIndex], 64)) vertlist[6] = VertexInterp(isolevel, positions[6], positions[7], denses[6], denses[7], densityNormals[pos.x + 1, pos.y + 1, pos.z], densityNormals[pos.x, pos.y + 1, pos.z]); if (IsBitSet(edgeTable[cubeIndex], 128)) vertlist[7] = VertexInterp(isolevel, positions[7], positions[4], denses[7], denses[4], densityNormals[pos.x, pos.y + 1, pos.z], densityNormals[pos.x, pos.y + 1, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 256)) vertlist[8] = VertexInterp(isolevel, positions[0], positions[4], denses[0], denses[4], densityNormals[pos.x, pos.y, pos.z + 1], densityNormals[pos.x, pos.y + 1, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 512)) vertlist[9] = VertexInterp(isolevel, positions[1], positions[5], denses[1], denses[5], densityNormals[pos.x + 1, pos.y, pos.z + 1], densityNormals[pos.x + 1, pos.y + 1, pos.z + 1]); if (IsBitSet(edgeTable[cubeIndex], 1024)) vertlist[10] = VertexInterp(isolevel, positions[2], positions[6], denses[2], denses[6], densityNormals[pos.x + 1, pos.y, pos.z], densityNormals[pos.x + 1, pos.y + 1, pos.z]); if (IsBitSet(edgeTable[cubeIndex], 2048)) vertlist[11] = VertexInterp(isolevel, positions[3], positions[7], denses[3], denses[7], densityNormals[pos.x, pos.y, pos.z], densityNormals[pos.x, pos.y + 1, pos.z]); int submesh = densities.getMaterial(pos.x, pos.y, pos.z); for (int i = 0; triTable[cubeIndex][i] != -1; i += 3) { submeshIDList.Add(submesh); subMeshTriCount[submesh] = subMeshTriCount[submesh] + 1; triangleList.Add(new Triangle(vertlist[triTable[cubeIndex][i]][0], vertlist[triTable[cubeIndex][i + 1]][0], vertlist[triTable[cubeIndex][i + 2]][0], vertlist[triTable[cubeIndex][i]][1], vertlist[triTable[cubeIndex][i + 1]][1], vertlist[triTable[cubeIndex][i + 2]][1])); } }
/// <summary> /// Calculates the normal. /// </summary> /// <param name="p"></param> private static Vector3 calculateDensityNormal(Vector3I p, DensityData densities, int lod) { Vector3 normal = new Vector3(); normal.x = (densities.get(p.x + 1, p.y, p.z) - densities.get(p.x - 1, p.y, p.z)) / (NodeManager.LODSize[lod]); normal.y = (densities.get(p.x, p.y + 1, p.z) - densities.get(p.x, p.y - 1, p.z)) / (NodeManager.LODSize[lod]); normal.z = (densities.get(p.x, p.y, p.z + 1) - densities.get(p.x, p.y, p.z - 1)) / (NodeManager.LODSize[lod]); normal.Normalize(); return normal; }
/// <summary> /// Generates the triangles for a specific voxel /// </summary> /// <param name="node">The node that contains the voxel</param> /// <param name="pos">The voxel position (Not real position) [16,16,16]</param> /// <param name="triangleList">The list used to contain triangles made so far</param> /// <param name="densities">The array that contains density information</param> /// <param name="densityNormals">The array that contains density normals</param> private static void generateTriangles(Node node, Vector3I pos, List <Triangle> triangleList, List <int> submeshIDList, int[] subMeshTriCount, DensityData densities, Vector3[, ,] densityNormals) { float size = NodeManager.LODSize[node.LOD]; float[] denses = new float[8]; denses[0] = densities.get(pos.x, pos.y, pos.z + 1); denses[1] = densities.get(pos.x + 1, pos.y, pos.z + 1); denses[2] = densities.get(pos.x + 1, pos.y, pos.z); denses[3] = densities.get(pos.x, pos.y, pos.z); denses[4] = densities.get(pos.x, pos.y + 1, pos.z + 1); denses[5] = densities.get(pos.x + 1, pos.y + 1, pos.z + 1); denses[6] = densities.get(pos.x + 1, pos.y + 1, pos.z); denses[7] = densities.get(pos.x, pos.y + 1, pos.z); byte cubeIndex = 0; if (denses[0] < isolevel) { cubeIndex |= 1; } if (denses[1] < isolevel) { cubeIndex |= 2; } if (denses[2] < isolevel) { cubeIndex |= 4; } if (denses[3] < isolevel) { cubeIndex |= 8; } if (denses[4] < isolevel) { cubeIndex |= 16; } if (denses[5] < isolevel) { cubeIndex |= 32; } if (denses[6] < isolevel) { cubeIndex |= 64; } if (denses[7] < isolevel) { cubeIndex |= 128; } if (cubeIndex == 0 || cubeIndex == 255) { return; } Vector3 origin = new Vector3((size * (pos.x)) , (size * (pos.y)) , (size * (pos.z))); Vector3[] positions = new Vector3[8]; positions[0] = new Vector3(origin.x, origin.y, origin.z + size); positions[1] = new Vector3(origin.x + size, origin.y, origin.z + size); positions[2] = new Vector3(origin.x + size, origin.y, origin.z); positions[3] = new Vector3(origin.x, origin.y, origin.z); positions[4] = new Vector3(origin.x, origin.y + size, origin.z + size); positions[5] = new Vector3(origin.x + size, origin.y + size, origin.z + size); positions[6] = new Vector3(origin.x + size, origin.y + size, origin.z); positions[7] = new Vector3(origin.x, origin.y + size, origin.z); Vector3[][] vertlist = new Vector3[12][]; if (IsBitSet(edgeTable[cubeIndex], 1)) { vertlist[0] = VertexInterp(isolevel, positions[0], positions[1], denses[0], denses[1], densityNormals[pos.x, pos.y, pos.z + 1], densityNormals[pos.x + 1, pos.y, pos.z + 1]); } if (IsBitSet(edgeTable[cubeIndex], 2)) { vertlist[1] = VertexInterp(isolevel, positions[1], positions[2], denses[1], denses[2], densityNormals[pos.x + 1, pos.y, pos.z + 1], densityNormals[pos.x + 1, pos.y, pos.z]); } if (IsBitSet(edgeTable[cubeIndex], 4)) { vertlist[2] = VertexInterp(isolevel, positions[2], positions[3], denses[2], denses[3], densityNormals[pos.x + 1, pos.y, pos.z], densityNormals[pos.x, pos.y, pos.z]); } if (IsBitSet(edgeTable[cubeIndex], 8)) { vertlist[3] = VertexInterp(isolevel, positions[3], positions[0], denses[3], denses[0], densityNormals[pos.x, pos.y, pos.z], densityNormals[pos.x, pos.y, pos.z + 1]); } if (IsBitSet(edgeTable[cubeIndex], 16)) { vertlist[4] = VertexInterp(isolevel, positions[4], positions[5], denses[4], denses[5], densityNormals[pos.x, pos.y + 1, pos.z + 1], densityNormals[pos.x + 1, pos.y + 1, pos.z + 1]); } if (IsBitSet(edgeTable[cubeIndex], 32)) { vertlist[5] = VertexInterp(isolevel, positions[5], positions[6], denses[5], denses[6], densityNormals[pos.x + 1, pos.y + 1, pos.z + 1], densityNormals[pos.x + 1, pos.y + 1, pos.z]); } if (IsBitSet(edgeTable[cubeIndex], 64)) { vertlist[6] = VertexInterp(isolevel, positions[6], positions[7], denses[6], denses[7], densityNormals[pos.x + 1, pos.y + 1, pos.z], densityNormals[pos.x, pos.y + 1, pos.z]); } if (IsBitSet(edgeTable[cubeIndex], 128)) { vertlist[7] = VertexInterp(isolevel, positions[7], positions[4], denses[7], denses[4], densityNormals[pos.x, pos.y + 1, pos.z], densityNormals[pos.x, pos.y + 1, pos.z + 1]); } if (IsBitSet(edgeTable[cubeIndex], 256)) { vertlist[8] = VertexInterp(isolevel, positions[0], positions[4], denses[0], denses[4], densityNormals[pos.x, pos.y, pos.z + 1], densityNormals[pos.x, pos.y + 1, pos.z + 1]); } if (IsBitSet(edgeTable[cubeIndex], 512)) { vertlist[9] = VertexInterp(isolevel, positions[1], positions[5], denses[1], denses[5], densityNormals[pos.x + 1, pos.y, pos.z + 1], densityNormals[pos.x + 1, pos.y + 1, pos.z + 1]); } if (IsBitSet(edgeTable[cubeIndex], 1024)) { vertlist[10] = VertexInterp(isolevel, positions[2], positions[6], denses[2], denses[6], densityNormals[pos.x + 1, pos.y, pos.z], densityNormals[pos.x + 1, pos.y + 1, pos.z]); } if (IsBitSet(edgeTable[cubeIndex], 2048)) { vertlist[11] = VertexInterp(isolevel, positions[3], positions[7], denses[3], denses[7], densityNormals[pos.x, pos.y, pos.z], densityNormals[pos.x, pos.y + 1, pos.z]); } int submesh = densities.getMaterial(pos.x, pos.y, pos.z); for (int i = 0; triTable[cubeIndex][i] != -1; i += 3) { submeshIDList.Add(submesh); subMeshTriCount[submesh] = subMeshTriCount[submesh] + 1; triangleList.Add(new Triangle(vertlist[triTable[cubeIndex][i]][0], vertlist[triTable[cubeIndex][i + 1]][0], vertlist[triTable[cubeIndex][i + 2]][0], vertlist[triTable[cubeIndex][i]][1], vertlist[triTable[cubeIndex][i + 1]][1], vertlist[triTable[cubeIndex][i + 2]][1])); } }
/// <summary> /// Generates the mesh data /// </summary> public static void GenerateMeshData(MeshRequest request) { MeshData meshData = new MeshData(); request.meshData = meshData; //Check if the node requesting a generation has been disposed if (request == null || request.node.disposed) { meshData.triangleArray = new Vector3[0]; meshData.indexArray = new int[0][]; meshData.uvArray = new Vector2[0]; meshData.normalArray = new Vector3[0]; request.isDone = true; return; } DensityData densityArray = request.densities; Vector3[, ,] densityNormals = new Vector3[17, 17, 17]; List <Triangle> triangleList = new List <Triangle>(); List <int> subMeshIDList = new List <int>(); int[] subMeshTriCount = new int[QuixelEngine.materials.Length]; Node node = request.node; request.meshData = meshData; //Unoptimized generation densityNormals = new Vector3[18, 18, 18]; if (!request.hasDensities) { for (int x = -1; x < 18; x++) { for (int y = -1; y < 18; y++) { for (int z = -1; z < 18; z++) { VoxelData data = calculateDensity(node, new Vector3I(x, y, z)); densityArray.set(x, y, z, data.density); densityArray.setMaterial(x, y, z, data.material); } } } } for (int x = 0; x < 17; x++) { for (int y = 0; y < 17; y++) { for (int z = 0; z < 17; z++) { densityNormals[x, y, z] = calculateDensityNormal(new Vector3I(x, y, z), densityArray, node.LOD); } } } for (int x = 0; x < 16; x++) { for (int y = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { generateTriangles(node, new Vector3I(x, y, z), triangleList, subMeshIDList, subMeshTriCount, densityArray, densityNormals); } } } int ppos = 0; int li = 0; try { meshData.triangleArray = new Vector3[triangleList.Count * 3]; meshData.indexArray = new int[QuixelEngine.materials.Length][]; for (int i = 0; i < QuixelEngine.materials.Length; i++) { meshData.indexArray[i] = new int[(subMeshTriCount[i] * 3) * 3]; } meshData.uvArray = new Vector2[meshData.triangleArray.Length]; meshData.normalArray = new Vector3[meshData.triangleArray.Length]; int count = 0; int[] indCount = new int[QuixelEngine.materials.Length]; for (int i = 0; i < triangleList.Count; i++) { ppos = i; Triangle triangle = triangleList[i]; meshData.triangleArray[count + 2] = triangle.pointOne; meshData.triangleArray[count + 1] = triangle.pointTwo; meshData.triangleArray[count + 0] = triangle.pointThree; meshData.normalArray[count + 2] = triangle.nOne; meshData.normalArray[count + 1] = triangle.nTwo; meshData.normalArray[count + 0] = triangle.nThree; int ind = subMeshIDList[i]; li = subMeshIDList[i]; meshData.indexArray[ind][indCount[ind] + 0] = count + 0; meshData.indexArray[ind][indCount[ind] + 1] = count + 1; meshData.indexArray[ind][indCount[ind] + 2] = count + 2; meshData.uvArray[count + 0] = new Vector2(meshData.triangleArray[count + 0].x, meshData.triangleArray[count + 0].z); meshData.uvArray[count + 1] = new Vector2(meshData.triangleArray[count + 1].x, meshData.triangleArray[count + 1].z); meshData.uvArray[count + 2] = new Vector2(meshData.triangleArray[count + 2].x, meshData.triangleArray[count + 2].z); count += 3; indCount[subMeshIDList[i]] += 3; } } catch (Exception e) { StreamWriter sw = new StreamWriter("Error Log.txt"); sw.WriteLine(e.Message + "\r\n" + e.StackTrace); for (int i = 0; i < QuixelEngine.materials.Length; i++) { sw.WriteLine(i + ": " + subMeshTriCount[i]); } sw.WriteLine(ppos); sw.WriteLine(li); sw.Close(); } request.isDone = true; }
/// <summary> /// Applies changes (additive) using another Density Data /// </summary> /// <param name="other"></param> public void applyChanges(DensityData other) { for (int x = 0; x < 19; x++) for (int y = 0; y < 19; y++) for (int z = 0; z < 19; z++) if (other.Values[x, y, z] > -99999f) Values[x, y, z] = other.Values[x, y, z]; }
/// <summary> /// Queue a mesh for generation. /// </summary> /// <param name="req"></param> public void queueRecycleDensity(DensityData req) { lock (genQueue) genQueue.Enqueue(req); }