using UnityEngine; public class VoxelTreeRaycaster { public struct HitInfo { public Vector3 point; public Vector3 normal; public float distance; public OctreeNode node; } // Public API: perform a raycast through the voxel tree public bool Raycast(OctreeNode root, Vector3 origin, Vector3 direction, float maxDistance, out HitInfo hit) { hit = new HitInfo(); if (root == null || direction == Vector3.zero) return false; direction.Normalize(); Bounds treeBounds = root.bounds; // First, check if ray starts inside or intersects the tree bounds if (!treeBounds.IntersectRay(new Ray(origin, direction), out float entryDist)) { // If outside, skip rays that miss the whole tree return false; } // Clamp to max distance float dist = Mathf.Max(0f, entryDist); float endDist = Mathf.Min(maxDistance, entryDist + maxDistance); return Traverse(root, origin, direction, ref dist, endDist, out hit); } // Recursive traversal private bool Traverse(OctreeNode node, Vector3 origin, Vector3 dir, ref float dist, float maxDist, out HitInfo hit) { hit = new HitInfo(); // Stop if beyond range or node is outside of ray segment if (dist > maxDist) return false; if (!node.bounds.IntersectRay(new Ray(origin, dir), out float entry)) return false; // Adjust distance to this node's entry point dist = Mathf.Max(dist, entry); // Leaf node if (node.isLeaf) { if (node.isOccupied) { // Approximate hit point (entry point) hit.point = origin + dir * dist; hit.distance = dist; hit.normal = GetApproximateNormal(node, origin, dir); hit.node = node; return true; } return false; } // Otherwise, traverse children if (node.children != null) { // Check all child nodes that intersect the ray foreach (var child in node.children) { if (child == null) continue; if (Traverse(child, origin, dir, ref dist, maxDist, out hit)) return true; } } return false; } // Approximate normal from cube face hit private Vector3 GetApproximateNormal(OctreeNode node, Vector3 origin, Vector3 dir) { Vector3 p = origin; Bounds b = node.bounds; Vector3 center = b.center; Vector3 extents = b.extents; Vector3 local = p - center; Vector3 absDir = new Vector3(Mathf.Abs(dir.x), Mathf.Abs(dir.y), Mathf.Abs(dir.z)); Vector3 normal = Vector3.zero; if (absDir.x > absDir.y && absDir.x > absDir.z) normal = new Vector3(Mathf.Sign(-dir.x), 0, 0); else if (absDir.y > absDir.z) normal = new Vector3(0, Mathf.Sign(-dir.y), 0); else normal = new Vector3(0, 0, Mathf.Sign(-dir.z)); return normal; } }