101 lines
3.1 KiB
C#
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;
|
|
}
|
|
} |