Files
SoundTool/Assets/Scripts/VoxelOctree/CPU/VoxelTreeRaycaster.cs
2025-10-19 22:56:25 +02:00

101 lines
3.1 KiB
C#

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;
}
}