using UnityEngine; using System; using System.Collections.Generic; using System.Linq; public class VoxelTreeBuilder { private int maxDepth; private float minNodeSize; public VoxelTreeBuilder(int maxDepth, float minNodeSize) { this.maxDepth = maxDepth; this.minNodeSize = minNodeSize; } public OctreeNode Build(Bounds bounds, Func isSolid) { return BuildNode(bounds, isSolid, 0); } private OctreeNode BuildNode(Bounds bounds, Func isSolid, int depth) { OctreeNode node = new OctreeNode(bounds); IsSolidNodeAnswere answere; // Stop subdivision if we reached the maximum depth or smallest size if (depth >= maxDepth) { // Check the voxel state at the center answere = isSolid(bounds.center, bounds.size.x); node.isOccupied = answere.isSolid; node.penetrationFactor = answere.penetrationFactor; node.reflexionFactor = answere.reflexionFactor; return node; } if( bounds.size.x <= minNodeSize ) { bool allSolid = true; bool allEmpty = true; foreach (Vector3 corner in GetCorners(bounds)) { answere = isSolid(corner, bounds.size.x); bool solid = answere.isSolid; node.penetrationFactor = answere.penetrationFactor; node.reflexionFactor = answere.reflexionFactor; allSolid &= solid; allEmpty &= !solid; } // Check if the entire cube is homogeneous (either fully solid or empty) if (depth >= maxDepth || allSolid) { // If homogeneous, stop subdivision if (allSolid || allEmpty) { if (allSolid) { answere = isSolid(bounds.center, bounds.size.x); node.isOccupied = answere.isSolid; node.penetrationFactor = answere.penetrationFactor; node.reflexionFactor = answere.reflexionFactor; node.isOccupied = allSolid; } return node; } } } // Otherwise, subdivide into 8 child nodes node.isLeaf = false; int arrayIndex = 0; foreach (var (i, subBounds) in GetSubBounds(bounds).Select((b, i) => (i, b))) { OctreeNode childrenNode = BuildNode(subBounds, isSolid, depth + 1); if (childrenNode.isOccupied == true || childrenNode.hasChildrenOccupied == true) { node.hasChildrenOccupied = true; node.children[arrayIndex] = childrenNode; } else { node.children[arrayIndex] = null; } arrayIndex++; } if (arrayIndex == 0) node.isLeaf = true; return node; } private IEnumerable GetSubBounds(Bounds parent) { // Generate 8 sub-bounds for the octree subdivision Vector3 size = parent.size / 2f; Vector3 min = parent.min; for (int x = 0; x < 2; x++) for (int y = 0; y < 2; y++) for (int z = 0; z < 2; z++) { Vector3 center = min + Vector3.Scale(new Vector3(x + 0.5f, y + 0.5f, z + 0.5f), size); yield return new Bounds(center, size); } } private IEnumerable GetCorners(Bounds b) { // Return all 8 corners of the bounding box Vector3 min = b.min; Vector3 max = b.max; for (int x = 0; x <= 1; x++) for (int y = 0; y <= 1; y++) for (int z = 0; z <= 1; z++) yield return new Vector3( x == 0 ? min.x : max.x, y == 0 ? min.y : max.y, z == 0 ? min.z : max.z); } }