Skip to content

Instantly share code, notes, and snippets.

@enbugger
Last active August 3, 2025 18:49
Show Gist options
  • Save enbugger/d4ddb9c267f5a1a7e17a0617c269e396 to your computer and use it in GitHub Desktop.
Save enbugger/d4ddb9c267f5a1a7e17a0617c269e396 to your computer and use it in GitHub Desktop.

Revisions

  1. enbugger revised this gist Aug 3, 2025. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions reference.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    https://github.com/fenomas/fast-voxel-raycast
  2. enbugger revised this gist Aug 3, 2025. No changes.
  3. enbugger revised this gist Aug 3, 2025. No changes.
  4. enbugger created this gist Aug 3, 2025.
    96 changes: 96 additions & 0 deletions RayCasting.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,96 @@
    using static Unity.Mathematics.math;
    using Unity.Mathematics;

    namespace Game
    {
    public interface IVoxelStore<T> where T : struct
    {
    // Should return null if the guess value is not in boundaries of any voxel
    // Otherwise return arbitrary voxel data
    T? GetVoxel(float3 guess);
    }

    public struct RayCasting
    {
    public static T? Cast<T, TR>(TR store,
    float3 origin, float3 direction, float maxDistance,
    out float3 hitPos, out float3 hitNorm)
    where T : struct
    where TR: struct, IVoxelStore<T>
    {
    if (distance(origin, direction) == 0)
    {
    hitPos = 0;
    hitNorm = 0;
    return null;
    }

    direction = normalize(direction);
    var t = 0f;
    var i = floor(origin);
    var step = sign(direction);
    var tDelta = abs(1 / direction);
    float3 dist;
    dist.x = step.x > 0 ? i.x + 1 - origin.x : origin.x - i.x;
    dist.y = step.y > 0 ? i.y + 1 - origin.y : origin.y - i.y;
    dist.z = step.z > 0 ? i.z + 1 - origin.z : origin.z - i.z;
    float3 tMax;
    tMax.x = tDelta.x < float.PositiveInfinity ? tDelta.x * dist.x : float.PositiveInfinity;
    tMax.y = tDelta.y < float.PositiveInfinity ? tDelta.y * dist.y : float.PositiveInfinity;
    tMax.z = tDelta.z < float.PositiveInfinity ? tDelta.z * dist.z : float.PositiveInfinity;
    var steppedIndex = -1;
    while (t <= maxDistance)
    {
    var b = store.GetVoxel(i);
    if (b != null)
    {
    hitPos = origin + t * direction;
    hitNorm = 0;
    if (steppedIndex >= 0)
    hitNorm[steppedIndex] = -step[steppedIndex];
    return b;
    }

    // Advance t to next nearest voxel boundary
    if (tMax.x < tMax.y)
    {
    if (tMax.x < tMax.z)
    {
    i.x += step.x;
    t = tMax.x;
    tMax.x += tDelta.x;
    steppedIndex = 0;
    }
    else
    {
    i.z += step.z;
    t = tMax.z;
    tMax.z += tDelta.z;
    steppedIndex = 2;
    }
    }
    else
    {
    if (tMax.y < tMax.z)
    {
    i.y += step.y;
    t = tMax.y;
    tMax.y += tDelta.y;
    steppedIndex = 1;
    }
    else
    {
    i.z += step.z;
    t = tMax.z;
    tMax.z += tDelta.z;
    steppedIndex = 2;
    }
    }
    }
    // No voxel hit found
    hitPos = origin + t * direction;
    hitNorm = 0;
    return null;
    }
    }
    }