private static void GenerateVertexIndices(OctreeNode node, List <Vector3> verts, List <Vector3> norms) { if (node == null) { return; } if (node.type != OctreeNodeType.NODE_LEAF) { for (int i = 0; i < 8; i++) { GenerateVertexIndices(node.children[i], verts, norms); } } if (node.type != OctreeNodeType.NODE_INTERNAL) { OctreeDrawInfo drawInfo = node.drawInfo; //if (drawInfo == null) { //throw new NullReferenceException("Could not add vertex! DrawInfo was null!"); //} drawInfo.index = verts.Count; verts.Add(drawInfo.position); norms.Add(drawInfo.normal); } }
private void GenerateVertexIndices(OctreeNode node, List <Vector3> vertices, List <Vector3> normals, int nodeSize) { if (node == null) { return; } if (node.size > nodeSize) { if (node.type != OctreeNodeType.Node_Leaf) { for (int i = 0; i < 8; i++) { GenerateVertexIndices(node.children[i], vertices, normals, nodeSize); } } } if (node.type != OctreeNodeType.Node_Internal) { OctreeDrawInfo d = node.drawInfo; if (d == null) { Debug.LogError("Error! Could not add vertex!"); Application.Quit(); } d.index = vertices.Count; vertices.Add(new Vector3(d.position.x, d.position.y, d.position.z)); normals.Add(new Vector3(d.averageNormal.x, d.averageNormal.y, d.averageNormal.z)); } }
public OctreeNode(OctreeNodeType _type) { Type = _type; drawInfo = new OctreeDrawInfo(); Children = new OctreeNode[8]; for (int i = 0; i < 8; i++) { Children[i] = null; } }
public OctreeNode() { Type = OctreeNodeType.Node_None; drawInfo = new OctreeDrawInfo(); Children = new OctreeNode[8]; for (int i = 0; i < 8; i++) { Children[i] = null; } }
public OctreeNode(OctreeNodeType _type) { Type = _type; min = Vector3.zero; size = 0; drawInfo = new OctreeDrawInfo(); children = new OctreeNode[8]; for (int i = 0; i < 8; i++) { children[i] = null; } }
protected override void ThreadFunction() { if (i_Tree != null && i_Count > 0 && i_VoxMins.Length > 0 && i_VoxMaterials.Length > 0 && i_Voxs.Length > 0 && i_Size != 0) { List<OctreeNode> computedVoxels = new List<OctreeNode>(); int HIGHEST_VOXEL_RES = 64; int voxelSize = HIGHEST_VOXEL_RES / i_Size; for (int i = 0; i < i_Count; i++) { if (i_Voxs[i].numPoints != 0) { OctreeNode leaf = new OctreeNode(); leaf.type = OctreeNodeType.Node_Leaf; leaf.size = voxelSize; OctreeDrawInfo drawInfo = new OctreeDrawInfo(); drawInfo.position = i_Voxs[i].vertPoint; drawInfo.averageNormal = i_Voxs[i].avgNormal; drawInfo.corners = (int) i_VoxMaterials[i]; leaf.drawInfo = drawInfo; leaf.min = i_VoxMins[i]; computedVoxels.Add(leaf); } } //Debug.Log(computedVoxels.Count); if (computedVoxels.Count > 0) { if (updatingChunk && chunkToUpdate != null) chunkToUpdate.DestroyOctree(); m_Root = i_Tree.ConstructUpwards(computedVoxels, i_Min, HIGHEST_VOXEL_RES); if (m_Root != null) { i_Tree.GenerateMeshFromOctree(m_Root, m_Vertices, m_Normals, m_Indices, voxelSize); } } //m_Vertices.TrimExcess(); //m_Normals.TrimExcess(); //m_Indices.TrimExcess(); computedVoxels.Clear(); computedVoxels = null; i_Min = Vector3.zero; i_Size = 0; i_Count = 0; i_Tree = null; //Array.Clear(i_VoxMins, 0, i_VoxMins.Length); i_VoxMins = null; //Array.Clear(i_VoxMaterials, 0, i_VoxMaterials.Length); i_VoxMaterials = null; //Array.Clear(i_Voxs, 0, i_Voxs.Length); i_Voxs = null; //Debug.Log ("Finished loading chunk"); if (!updatingChunk) m_Test.informGame(); if (updatingChunk) { m_Test.informGame(chunkToUpdate); chunkToUpdate = null; updatingChunk = false; } } }
private List <OctreeNode> ComputeVoxels(ComputeShader shader, Vector3 min, int octreeSize) { float gpuStart = Time.realtimeSinceStartup; float[] chunkPos = new float[3] { min.x, min.y, min.z }; shader.SetFloats("chunkPosition", chunkPos); shader.SetInt("resolution", octreeSize); shader.SetInt("octreeSize", octreeSize); float sqRTRC = Mathf.Sqrt(octreeSize * octreeSize * octreeSize); int sqRTRes = (int)sqRTRC; if (sqRTRC > sqRTRes) { sqRTRes++; } ComputeBuffer Perm = new ComputeBuffer(512, sizeof(int)); Perm.SetData(permutations); ComputeBuffer cornCount = new ComputeBuffer(sqRTRes, sizeof(int)); ComputeBuffer finalCount = new ComputeBuffer(1, sizeof(float)); ComputeBuffer voxMatBuffer = new ComputeBuffer(octreeSize * octreeSize * octreeSize, sizeof(uint)); float rD8 = octreeSize / 8.0f; int rD8I = (int)rD8; if (rD8 > rD8I) { rD8I++; } int kernel = shader.FindKernel("ComputeCorners"); shader.SetBuffer(kernel, "Perm", Perm); shader.SetBuffer(kernel, "voxelMaterials", voxMatBuffer); shader.Dispatch(kernel, rD8I, rD8I, rD8I); /*kernel = shader.FindKernel("ComputeLength"); * shader.SetBuffer(kernel, "voxelMaterials", voxMatBuffer); * shader.SetBuffer(kernel, "cornerCount", cornCount); * shader.Dispatch(kernel, 1, 1, 1);*/ kernel = shader.FindKernel("AddLength"); shader.SetBuffer(kernel, "cornerCount", cornCount); shader.SetBuffer(kernel, "finalCount", finalCount); shader.Dispatch(kernel, 1, 1, 1); float[] voxelCount = new float[1]; finalCount.GetData(voxelCount); finalCount.SetData(voxelCount); int count = (int)voxelCount[0]; //Debug.Log (count); if (count <= 0) { voxMatBuffer.Dispose(); cornCount.Dispose(); finalCount.Dispose(); Perm.Dispose(); return(null); } ComputeBuffer cornerIndexes = new ComputeBuffer(count, sizeof(uint)); kernel = shader.FindKernel("ComputePositions"); shader.SetBuffer(kernel, "voxelMaterials", voxMatBuffer); shader.SetBuffer(kernel, "cornerCount", cornCount); shader.SetBuffer(kernel, "cornerIndexes", cornerIndexes); shader.Dispatch(kernel, 1, 1, 1); ComputeBuffer voxBuffer = new ComputeBuffer(count, (sizeof(float) * 6) + sizeof(int)); ComputeBuffer positionBuffer = new ComputeBuffer(count, sizeof(float) * 3); kernel = shader.FindKernel("ComputeVoxels"); shader.SetBuffer(kernel, "Perm", Perm); shader.SetBuffer(kernel, "voxMins", positionBuffer); shader.SetBuffer(kernel, "voxelMaterials", voxMatBuffer); shader.SetBuffer(kernel, "finalCount", finalCount); shader.SetBuffer(kernel, "cornerIndexes", cornerIndexes); shader.SetBuffer(kernel, "voxels", voxBuffer); //int dispatchCount = count / 10; shader.Dispatch(kernel, (count / 128) + 1, 1, 1); List <OctreeNode> computedVoxels = new List <OctreeNode>(); Vector3[] voxelMins = new Vector3[count]; positionBuffer.GetData(voxelMins); positionBuffer.Dispose(); uint[] voxelMaterials = new uint[count]; cornerIndexes.GetData(voxelMaterials); cornerIndexes.Dispose(); GPUVOX[] voxs = new GPUVOX[count]; voxBuffer.GetData(voxs); voxBuffer.Dispose(); voxMatBuffer.Dispose(); cornCount.Dispose(); finalCount.Dispose(); Perm.Dispose(); float gpuEnd = Time.realtimeSinceStartup; //Debug.Log ("GPU time on chunk: " + (gpuEnd - gpuStart)); int HIGHEST_VOXEL_RES = 64; int voxelSize = HIGHEST_VOXEL_RES / octreeSize; for (int i = 0; i < count; i++) { if (voxs[i].numPoints != 0) { OctreeNode leaf = new OctreeNode(); leaf.type = OctreeNodeType.Node_Leaf; leaf.size = voxelSize; OctreeDrawInfo drawInfo = new OctreeDrawInfo(); drawInfo.position = voxs[i].vertPoint; drawInfo.averageNormal = voxs[i].avgNormal; drawInfo.corners = (int)voxelMaterials[i]; leaf.drawInfo = drawInfo; leaf.min = voxelMins[i]; computedVoxels.Add(leaf); } } //Debug.Log ("CPU Leaf generation time on chunk: " + (Time.realtimeSinceStartup - gpuEnd)); //Debug.Log (computedVoxels.Count); return(computedVoxels); }
public static OctreeNode ConstructLeaf(OctreeNode leaf) { if (leaf == null || leaf.size != 1) { return(null); } int corners = 0; for (int i = 0; i < 8; i++) { Vector3 cornerPos = leaf.min + CHILD_MIN_OFFSETS[i]; float density = glm.Density_Func(cornerPos); int material = density < 0.0f ? MATERIAL_SOLID : MATERIAL_AIR; corners |= (material << i); } if (corners == 0 || corners == 255) { // voxel is full inside or outside the volume //delete leaf //setting as null isn't required by the GC in C#... but its in the original, so why not! leaf = null; return(null); } // otherwise the voxel contains the surface, so find the edge intersections const int MAX_CROSSINGS = 6; int edgeCount = 0; Vector3 averageNormal = Vector3.zero; QefSolver qef = new QefSolver(); for (int i = 0; i < 12 && edgeCount < MAX_CROSSINGS; i++) { int c1 = edgevmap[i][0]; int c2 = edgevmap[i][1]; int m1 = (corners >> c1) & 1; int m2 = (corners >> c2) & 1; if ((m1 == MATERIAL_AIR && m2 == MATERIAL_AIR) || (m1 == MATERIAL_SOLID && m2 == MATERIAL_SOLID)) { // no zero crossing on this edge continue; } Vector3 p1 = leaf.min + CHILD_MIN_OFFSETS[c1]; Vector3 p2 = leaf.min + CHILD_MIN_OFFSETS[c2]; Vector3 p = ApproximateZeroCrossingPosition(p1, p2); Vector3 n = CalculateSurfaceNormal(p); qef.add(p.x, p.y, p.z, n.x, n.y, n.z); averageNormal += n; edgeCount++; } Vector3 qefPosition = Vector3.zero; qef.solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); OctreeDrawInfo drawInfo = new OctreeDrawInfo(); drawInfo.corners = 0; drawInfo.index = -1; drawInfo.position = new Vector3(qefPosition.x, qefPosition.y, qefPosition.z); drawInfo.qef = qef.getData(); Vector3 min = leaf.min; Vector3 max = new Vector3(leaf.min.x + leaf.size, leaf.min.y + leaf.size, leaf.min.z + leaf.size); if (drawInfo.position.x < min.x || drawInfo.position.x > max.x || drawInfo.position.y < min.y || drawInfo.position.y > max.y || drawInfo.position.z < min.z || drawInfo.position.z > max.z) { drawInfo.position = qef.getMassPoint(); } drawInfo.averageNormal = Vector3.Normalize(averageNormal / (float)edgeCount); drawInfo.corners = corners; leaf.Type = OctreeNodeType.Node_Leaf; leaf.drawInfo = drawInfo; return(leaf); }
public static OctreeNode SimplifyOctree(OctreeNode node, float threshold) { if (node == null) { return(null); } if (node.Type != OctreeNodeType.Node_Internal) { // can't simplify! return(node); } QefSolver qef = new QefSolver(); int[] signs = new int[8] { -1, -1, -1, -1, -1, -1, -1, -1 }; int midsign = -1; int edgeCount = 0; bool isCollapsible = true; for (int i = 0; i < 8; i++) { node.children[i] = SimplifyOctree(node.children[i], threshold); if (node.children[i] != null) { OctreeNode child = node.children[i]; if (child.Type == OctreeNodeType.Node_Internal) { isCollapsible = false; } else { qef.add(child.drawInfo.qef); midsign = (child.drawInfo.corners >> (7 - i)) & 1; signs[i] = (child.drawInfo.corners >> i) & 1; edgeCount++; } } } if (!isCollapsible) { // at least one child is an internal node, can't collapse return(node); } Vector3 qefPosition = Vector3.zero; qef.solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); float error = qef.getError(); // convert to glm vec3 for ease of use Vector3 position = new Vector3(qefPosition.x, qefPosition.y, qefPosition.z); // at this point the masspoint will actually be a sum, so divide to make it the average if (error > threshold) { // this collapse breaches the threshold return(node); } if (position.x < node.min.x || position.x > (node.min.x + node.size) || position.y < node.min.y || position.y > (node.min.y + node.size) || position.z < node.min.z || position.z > (node.min.z + node.size)) { position = qef.getMassPoint(); } // change the node from an internal node to a 'psuedo leaf' node OctreeDrawInfo drawInfo = new OctreeDrawInfo(); drawInfo.corners = 0; drawInfo.index = -1; for (int i = 0; i < 8; i++) { if (signs[i] == -1) { // Undetermined, use centre sign instead drawInfo.corners |= (midsign << i); } else { drawInfo.corners |= (signs[i] << i); } } drawInfo.averageNormal = Vector3.zero; for (int i = 0; i < 8; i++) { if (node.children[i] != null) { OctreeNode child = node.children[i]; if (child.Type == OctreeNodeType.Node_Psuedo || child.Type == OctreeNodeType.Node_Leaf) { drawInfo.averageNormal += child.drawInfo.averageNormal; } } } drawInfo.averageNormal = drawInfo.averageNormal.normalized; drawInfo.position = position; drawInfo.qef = qef.getData(); for (int i = 0; i < 8; i++) { DestroyOctree(node.children[i]); node.children[i] = null; } node.Type = OctreeNodeType.Node_Psuedo; node.drawInfo = drawInfo; return(node); }
public static OctreeNode SimplifyOctree(OctreeNode node, double threshold) { if (node == null) { return(null); } if (node.Type != OctreeNodeType.Node_Internal) { // can't simplify! return(node); } var qef = new QefSolver(); int[] signs = new int[8] { -1, -1, -1, -1, -1, -1, -1, -1 }; int midsign = -1; int edgeCount = 0; bool isCollapsible = true; for (int i = 0; i < 8; i++) { node.Children[i] = SimplifyOctree(node.Children[i], threshold); if (node.Children[i] != null) { OctreeNode child = node.Children[i]; if (child.Type == OctreeNodeType.Node_Internal) { isCollapsible = false; } else { qef.Add(child.drawInfo.qefData); midsign = (child.drawInfo.corners >> (7 - i)) & 1; signs[i] = (child.drawInfo.corners >> i) & 1; edgeCount++; } } } if (!isCollapsible) { // at least one child is an internal node, can't collapse return(node); } Vector3 position = qef.Solve(QEF_ERROR, QEF_SWEEPS, QEF_ERROR); double error = qef.GetError(); // at this point the masspoint will actually be a sum, so divide to make it the average if (error > threshold) { // this collapse breaches the threshold return(node); } if (position.X < node.Min.X || position.X > (node.Min.X + node.Size.X) || position.Y < node.Min.Y || position.Y > (node.Min.Y + node.Size.Y) || position.Z < node.Min.Z || position.Z > (node.Min.Z + node.Size.Z)) { position = qef.GetMassPoint(); } // change the node from an internal node to a 'psuedo leaf' node var drawInfo = new OctreeDrawInfo(); drawInfo.corners = 0; drawInfo.index = -1; for (int i = 0; i < 8; i++) { if (signs[i] == -1) { // Undetermined, use center sign instead drawInfo.corners |= (midsign << i); } else { drawInfo.corners |= (signs[i] << i); } } drawInfo.averageNormal = Vector3.Zero; for (int i = 0; i < 8; i++) { if (node.Children[i] != null) { OctreeNode child = node.Children[i]; if (child.Type == OctreeNodeType.Node_Psuedo || child.Type == OctreeNodeType.Node_Leaf) { drawInfo.averageNormal += child.drawInfo.averageNormal; } } } drawInfo.averageNormal = drawInfo.averageNormal.GetNormal(); drawInfo.position = position; drawInfo.qefData = qef.QefData; for (int i = 0; i < 8; i++) { DestroyOctree(node.Children[i]); node.Children[i] = null; } node.Type = OctreeNodeType.Node_Psuedo; node.drawInfo = drawInfo; return(node); }
public static OctreeNode ConstructLeaf(Func <Vector3, double> f, OctreeNode leaf) { if (leaf == null || leaf.Level != 1) { return(null); } int corners = 0; for (int i = 0; i < 8; i++) { Vector3 cornerPos = leaf.Min + CHILD_MIN_OFFSETS[i] * leaf.Size; double density = f(cornerPos); int material = density < 0.0f ? MATERIAL_SOLID : MATERIAL_AIR; corners |= (material << i); } if (corners == 0 || corners == 255) { // voxel is full inside or outside the volume //delete leaf return(null); } // otherwise the voxel contains the surface, so find the edge intersections const int MAX_CROSSINGS = 6; int edgeCount = 0; Vector3 averageNormal = Vector3.Zero; var qefSolver = new QefSolver(); var debugError = .1; bool is5Ish = false; for (int i = 0; i < 12 && edgeCount < MAX_CROSSINGS; i++) { int c1 = edgevmap[i][0]; int c2 = edgevmap[i][1]; int m1 = (corners >> c1) & 1; int m2 = (corners >> c2) & 1; if ((m1 == MATERIAL_AIR && m2 == MATERIAL_AIR) || (m1 == MATERIAL_SOLID && m2 == MATERIAL_SOLID)) { // no zero crossing on this edge continue; } Vector3 p1 = leaf.Min + CHILD_MIN_OFFSETS[c1] * leaf.Size; Vector3 p2 = leaf.Min + CHILD_MIN_OFFSETS[c2] * leaf.Size; Vector3 position = ApproximateZeroCrossingPosition(f, p1, p2); is5Ish = Math.Abs(Math.Abs(position[0]) - 5) < debugError; is5Ish |= Math.Abs(Math.Abs(position[1]) - 5) < debugError; is5Ish |= Math.Abs(Math.Abs(position[2]) - 5) < debugError; if (!is5Ish) { int a = 0; } Vector3 normal = CalculateSurfaceNormal(f, position); qefSolver.Add(position, normal); averageNormal += normal; edgeCount++; } Vector3 qefPosition = qefSolver.Solve(QEF_ERROR, QEF_SWEEPS, QEF_ERROR); is5Ish = Math.Abs(Math.Abs(qefPosition[0]) - 5) < debugError; is5Ish |= Math.Abs(Math.Abs(qefPosition[1]) - 5) < debugError; is5Ish |= Math.Abs(Math.Abs(qefPosition[2]) - 5) < debugError; if (!is5Ish) { int a = 0; } var drawInfo = new OctreeDrawInfo(); drawInfo.corners = 0; drawInfo.index = -1; drawInfo.position = new Vector3(qefPosition.X, qefPosition.Y, qefPosition.Z); drawInfo.qefData = qefSolver.QefData; Vector3 min = leaf.Min; var max = min + leaf.Size; if (drawInfo.position.X < min.X || drawInfo.position.X > max.X || drawInfo.position.Y < min.Y || drawInfo.position.Y > max.Y || drawInfo.position.Z < min.Z || drawInfo.position.Z > max.Z) { drawInfo.position = qefSolver.GetMassPoint(); } drawInfo.averageNormal = Vector3.Normalize(averageNormal); drawInfo.corners = corners; leaf.Type = OctreeNodeType.Node_Leaf; leaf.drawInfo = drawInfo; return(leaf); }
protected override void ThreadFunction() { if (i_Tree != null && i_Count > 0 && i_VoxMins.Length > 0 && i_VoxMaterials.Length > 0 && i_Voxs.Length > 0 && i_Size != 0) { List <OctreeNode> computedVoxels = new List <OctreeNode>(); int HIGHEST_VOXEL_RES = 64; int voxelSize = HIGHEST_VOXEL_RES / i_Size; for (int i = 0; i < i_Count; i++) { if (i_Voxs[i].numPoints != 0) { OctreeNode leaf = new OctreeNode(); leaf.type = OctreeNodeType.Node_Leaf; leaf.size = voxelSize; OctreeDrawInfo drawInfo = new OctreeDrawInfo(); drawInfo.position = i_Voxs[i].vertPoint; drawInfo.averageNormal = i_Voxs[i].avgNormal; drawInfo.corners = (int)i_VoxMaterials[i]; leaf.drawInfo = drawInfo; leaf.min = i_VoxMins[i]; computedVoxels.Add(leaf); } } //Debug.Log(computedVoxels.Count); if (computedVoxels.Count > 0) { if (updatingChunk && chunkToUpdate != null) { chunkToUpdate.DestroyOctree(); } m_Root = i_Tree.ConstructUpwards(computedVoxels, i_Min, HIGHEST_VOXEL_RES); if (m_Root != null) { i_Tree.GenerateMeshFromOctree(m_Root, m_Vertices, m_Normals, m_Indices, voxelSize); } } //m_Vertices.TrimExcess(); //m_Normals.TrimExcess(); //m_Indices.TrimExcess(); computedVoxels.Clear(); computedVoxels = null; i_Min = Vector3.zero; i_Size = 0; i_Count = 0; i_Tree = null; //Array.Clear(i_VoxMins, 0, i_VoxMins.Length); i_VoxMins = null; //Array.Clear(i_VoxMaterials, 0, i_VoxMaterials.Length); i_VoxMaterials = null; //Array.Clear(i_Voxs, 0, i_Voxs.Length); i_Voxs = null; //Debug.Log ("Finished loading chunk"); if (!updatingChunk) { m_Test.informGame(); } if (updatingChunk) { m_Test.informGame(chunkToUpdate); chunkToUpdate = null; updatingChunk = false; } } }