//semplifica l'octree public OctreeNode SimplifyOctree(OctreeNode node, float threshold) { if (node == null) { return(null); } if (node.type != NodeType.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 == NodeType.INTERNAL) { isCollapsible = false; } else { qef.Add(child.nodeInfo.qef); midsign = (child.nodeInfo.corners >> (7 - i)) & 1; signs[i] = (child.nodeInfo.corners >> i) & 1; edgeCount++; } } } if (!isCollapsible) { // at least one child is an internal node, can't collapse return(node); } Vector3 qefPosition = new Vector3(); float error = qef.Solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); 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 'pseudo leaf' node NodeInfo info = new NodeInfo(); for (int i = 0; i < 8; i++) { if (signs[i] == -1) { // Undetermined, use centre sign instead info.corners |= (midsign << i); } else { info.corners |= (signs[i] << i); } } info.avgNormal = new Vector3(0, 0, 0); for (int i = 0; i < 8; i++) { if (node.children[i] != null) { if (node.children[i].type == NodeType.PSEUDO || node.children[i].type == NodeType.LEAF) { info.avgNormal += node.children[i].nodeInfo.avgNormal; } } } info.avgNormal = info.avgNormal.normalized; info.position = position; info.qef = qef.GetData(); for (int i = 0; i < 8; i++) { DestroyOctree(node.children[i]); node.children[i] = null; } node.type = NodeType.PSEUDO; node.nodeInfo.Set(info); return(node); }
//costruisce il nodo terminale (foglia) calcolando le NodeInfo per la generazione della mesh public OctreeNode BuildLeaf(OctreeNode leaf, int size) { if (leaf == null || leaf.size != size) { return(null); } int corners = 0; for (int i = 0; i < 8; i++) { Vector3 cornerPos = leaf.min + (CHILD_MIN_OFFSETS[i] * size); float density = Density.DensityFunc(cornerPos); int material = density < 0.0f ? SOLID : AIR; corners |= (material << i); } if (corners == 0 || corners == 255) { leaf = null; return(null); } int edgeCount = 0; Vector3 averageNormal = new Vector3(); 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 == AIR && m2 == AIR) || (m1 == SOLID && m2 == SOLID)) { continue; } Vector3 p1 = leaf.min + (CHILD_MIN_OFFSETS[c1] * size); Vector3 p2 = leaf.min + (CHILD_MIN_OFFSETS[c2] * size); Vector3 p = ApproximateZeroCrossingPosition(p1, p2, 8); Vector3 n = CalculateSurfaceNormal(p); qef.Add(p.x, p.y, p.z, n.x, n.y, n.z); averageNormal += n; edgeCount++; } Vector3 qefPosition = new Vector3(); qef.Solve(qefPosition, QEF_ERROR, QEF_SWEEPS, QEF_ERROR); NodeInfo info = new NodeInfo(); info.index = -1; info.corners = 0; info.position = qefPosition; info.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 (info.position.x < min.x || info.position.x > max.x || info.position.y < min.y || info.position.y > max.y || info.position.z < min.z || info.position.z > max.z) { info.position = qef.GetMassPoint(); } info.avgNormal = (averageNormal / (float)edgeCount).normalized; info.corners = corners; leaf.type = NodeType.LEAF; leaf.nodeInfo = info; return(leaf); }