#if !defined VOXEL_RAYTRACER_LIB
#define VOXEL_RAYTRACER_LIB
    bool FindPotentialHit(
        vec3 pos, vec3 dir,
        vec3 deltaT,
        ivec3 deltaSign,
        ivec3 positiveDir,

        inout ivec3 idx, inout int level,
        inout vec3 steps, inout int iteration,

        out float minT, out bvec3 isMinComp
    ) {
        const int maxLevel = OCTREE_LEVELS - 1;

        bool potentialHit = false;
        do {
            // Find distances to next potential voxels
            ivec3 plane = ((idx >> level) + positiveDir) << level;
            vec3 planeT = abs(plane - pos) * deltaT;

            minT = minof(planeT);
            #if RAYTRACE_MAX_T > 0
            // Check that we're still within the raytrace range
            if (minT > RAYTRACE_MAX_T) { break; }
            #endif
            isMinComp = bvec3(planeT.x == minT, planeT.y == minT, planeT.z == minT);

            ivec3 prevIdx = idx;
            // Adding 0.5*isMinComp here is needed to avoid incorrect rounding
            // Mathemathically anything >= 0 and < 1 would be valid so 0.5 is probably safest
            ivec3 stepsToNext = ivec3(minT * abs(dir) - steps + 0.5 * vec3(isMinComp));
            steps += stepsToNext;
            idx += stepsToNext * deltaSign;

            // Check that we haven't stepped beyond the bounds
            if (OutsideVoxelmap(idx)) { break; }

            // Step up a level if we've moved to another "node" in the higher level
            int levelAbove = level + 1;
            bool shouldStepUp = (prevIdx >> levelAbove) != (idx >> levelAbove);
            level = min(shouldStepUp ? levelAbove : level, maxLevel);

            // Step down until either at lowest level or an empty node is found
            bool occupied = VoxelIsOccupied(idx, level);
            while (occupied && level > 0) {
                occupied = VoxelIsOccupied(idx, --level);
            }

            potentialHit = occupied && level == 0;
        } while (++iteration < 4096 && !potentialHit);

        return potentialHit;
    }

    bool RaytraceVoxels(vec3 pos, ivec3 idx, vec3 dir) {
        // In theory, using ints for idx should be faster on Volta & Turing, as well as AMD.

        // Early-exit when outside voxelized volume
        if (OutsideVoxelmap(idx)) { return false; }

        #ifdef INTERSECT_PARTIAL_BLOCKS
            // Check for intersection with starting voxel
            // TODO: It might be possible to optimize this
            float voxel0Alpha = texture(shadowcolor0, CalculateVoxelmapCoord(idx)).a;
            if (voxel0Alpha < (254.5 - OCTREE_LEVELS) / 255.0) {
                float hitDist; vec3 hitNormal;

                if (IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - voxel0Alpha * 255.0), hitDist, hitNormal)) {
                    return true;
                }
            }
        #endif

        #if OCTREE_LEVELS > 1
        ivec3 deltaSign = ivec3(sign(dir));
        if (deltaSign.x == 0 && deltaSign.y == 0 && deltaSign.z == 0) { return false; }
        ivec3 positiveDir = max0(deltaSign);
        ivec3 negativeDir = min(deltaSign, 0);

        vec3 deltaT = 1.0 / abs(dir);

        const int maxLevel = OCTREE_LEVELS - 1;
        int level = 0;
        while (level++ < maxLevel && !VoxelIsOccupied(idx, level)); --level;

        // there's probably a better name for this
        vec3 steps = (idx - pos) * deltaSign + positiveDir - 1;

        bool hit = false;
        int i = 0;
        do {
            float minT; bvec3 isMinComp;
            bool potentialHit = FindPotentialHit(
                pos, dir,
                deltaT, deltaSign, positiveDir,
                idx, level,
                steps, i,
                minT, isMinComp
            );

            // If a potential hit was found, try to intersect
            if (potentialHit) {
                #ifdef INTERSECT_PARTIAL_BLOCKS
                float voxel0Alpha = texture(shadowcolor0, CalculateVoxelmapCoord(idx)).a;
                float hitDist; vec3 hitNormal;
                hit = IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - voxel0Alpha * 255.0), hitDist, hitNormal);
                #else
                hit = true;
                #endif
            } else { // If no potential hit was found, break as we didn't find anything.
                break;
            }
        } while (++i < 4096 && !hit);
        #else
        vec3 nextT;
        vec3 deltaDist = 1.0 / abs(dir);
        ivec3 deltaSign;
        for (int axis = 0; axis < 3; ++axis) {
            if (dir[axis] < 0.0) {
                deltaSign[axis] = -1;
                nextT[axis] = (pos[axis] - idx[axis]) * deltaDist[axis];
            } else if (dir[axis] > 0.0) {
                deltaSign[axis] = 1;
                nextT[axis] = (idx[axis] - pos[axis]) * deltaDist[axis] + deltaDist[axis];
            } else {
                deltaSign[axis] = 0;
                nextT[axis] = uintBitsToFloat(0x7f800000u);// = infinity
            }
        }

        if (deltaSign.x == 0 && deltaSign.y == 0 && deltaSign.z == 0) { return false; }

        bool hit = false;
        do {
            float minComp = minof(nextT);
            if (nextT.x == minComp) {
                idx.x   += deltaSign.x;
                nextT.x += deltaDist.x;
            }
            if (nextT.y == minComp) {
                idx.y   += deltaSign.y;
                nextT.y += deltaDist.y;
            }
            if (nextT.z == minComp) {
                idx.z   += deltaSign.z;
                nextT.z += deltaDist.z;
            }

            #if RAYTRACE_MAX_T >= 0
            if (minof(nextT) > RAYTRACE_MAX_T) { break; }
            #endif
            if (OutsideVoxelmap(idx)) { break; }

            float voxel0Alpha = texture(shadowcolor0, CalculateVoxelmapCoord(idx)).a;
            #ifdef DEV_VIS
            ++reads[0];
            #endif
            #ifdef INTERSECT_PARTIAL_BLOCKS
            if (voxel0Alpha < (255.5 - OCTREE_LEVELS) / 255.0) {
                float hitDist; vec3 hitNormal;
                hit = IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - voxel0Alpha * 255.0), hitDist, hitNormal);
            }
            #else
            hit = voxel0Alpha < (255.5 - OCTREE_LEVELS) / 255.0;
            #endif
        } while (!hit);
        #endif

        return hit;
    }

    bool RaytraceVoxels(
        vec3 pos, ivec3 idx, vec3 dir,
        out vec3 hitPos, out ivec3 hitIdx,
        out vec2 voxelmapCoord, out vec3 hitNormal
    ) {
        // In theory, using ints for idx should be faster on Volta & Turing, as well as AMD.

        // Early-exit when outside voxelized volume
        if (OutsideVoxelmap(idx)) { return false; }

        #ifdef INTERSECT_PARTIAL_BLOCKS
            // Check for intersection with starting voxel
            // TODO: It might be possible to optimize this
            voxelmapCoord = CalculateVoxelmapCoord(idx);
            float voxel0Alpha = texture(shadowcolor0, voxelmapCoord).a;
            if (voxel0Alpha < (254.5 - OCTREE_LEVELS) / 255.0) {
                float hitDist;

                if (IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - voxel0Alpha * 255.0), hitDist, hitNormal)) {
                    hitPos = pos + dir * hitDist;
                    hitIdx = idx;
                    return true;
                }
            }
        #endif

        #if OCTREE_LEVELS > 1
        ivec3 deltaSign = ivec3(sign(dir));
        if (deltaSign.x == 0 && deltaSign.y == 0 && deltaSign.z == 0) { return false; }
        ivec3 positiveDir = max0(deltaSign);
        ivec3 negativeDir = min(deltaSign, 0);

        vec3 deltaT = 1.0 / abs(dir);

        const int maxLevel = OCTREE_LEVELS - 1;
        int level = 0;
        while (level++ < maxLevel && !VoxelIsOccupied(idx, level)); --level;

        // there's probably a better name for this
        vec3 steps = (idx - pos) * deltaSign + positiveDir - 1;

        bool hit = false;
        int i = 0;
        do {
            float minT; bvec3 isMinComp;
            bool potentialHit = FindPotentialHit(
                pos, dir,
                deltaT, deltaSign, positiveDir,
                idx, level,
                steps, i,
                minT, isMinComp
            );

            // If a potential hit was found, try to intersect
            if (potentialHit) {
                hitIdx = idx;

                hitNormal = vec3(isMinComp) * -deltaSign;

                float hitDist = minT;
                #ifdef INTERSECT_PARTIAL_BLOCKS
                voxelmapCoord = CalculateVoxelmapCoord(idx);
                float voxel0Alpha = texture(shadowcolor0, voxelmapCoord).a;
                hit = IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - voxel0Alpha * 255.0), hitDist, hitNormal);
                #else
                hit = true;
                #endif

                hitPos = pos + hitDist * dir;
            } else {
                break;
            }
        } while (++i < 4096 && !hit);
        #else
        vec3 nextT;
        vec3 deltaDist = 1.0 / abs(dir);
        ivec3 deltaSign;
        for (int axis = 0; axis < 3; ++axis) {
            if (dir[axis] < 0.0) {
                deltaSign[axis] = -1;
                nextT[axis] = (pos[axis] - idx[axis]) * deltaDist[axis];
            } else if (dir[axis] > 0.0) {
                deltaSign[axis] = 1;
                nextT[axis] = (idx[axis] - pos[axis]) * deltaDist[axis] + deltaDist[axis];
            } else {
                deltaSign[axis] = 0;
                nextT[axis] = uintBitsToFloat(0x7f800000u);// = infinity
            }
        }

        if (deltaSign.x == 0 && deltaSign.y == 0 && deltaSign.z == 0) { return false; }

        bool hit = false;
        while (!hit) {
            float hitDist;

            float minComp = minof(nextT);
            if (nextT.x == minComp) {
                hitDist   = nextT.x;
                hitNormal = vec3(-deltaSign.x, 0.0, 0.0);

                idx.x   += deltaSign.x;
                nextT.x += deltaDist.x;
            }
            if (nextT.y == minComp) {
                hitDist   = nextT.y;
                hitNormal = vec3(0.0, -deltaSign.y, 0.0);

                idx.y   += deltaSign.y;
                nextT.y += deltaDist.y;
            }
            if (nextT.z == minComp) {
                hitDist   = nextT.z;
                hitNormal = vec3(0.0, 0.0, -deltaSign.z);

                idx.z   += deltaSign.z;
                nextT.z += deltaDist.z;
            }

            #if RAYTRACE_MAX_T >= 0
            if (minof(nextT) > RAYTRACE_MAX_T) { break; }
            #endif
            if (OutsideVoxelmap(idx)) { break; }

            voxelmapCoord = CalculateVoxelmapCoord(idx);
            float voxel0Alpha = texture(shadowcolor0, voxelmapCoord).a;
            if (voxel0Alpha < (255.5 - OCTREE_LEVELS) / 255.0) {
                #ifdef INTERSECT_PARTIAL_BLOCKS
                    hit = IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - voxel0Alpha * 255.0), hitDist, hitNormal);
                #else
                    hit = true;
                #endif
                hitPos = pos + hitDist * dir; hitIdx = idx;
            }
        }
        #endif

        return hit;
    }

    vec2 GetUv(vec3 hitPos, vec3 hitNormal, vec4[2] voxel, out mat3 tbnMatrix) {
        vec2 uv;
        if (abs(hitNormal.y) > abs(hitNormal.x) && abs(hitNormal.y) > abs(hitNormal.z)) {
            uv = vec2(fract(hitPos.x), fract(hitPos.z * sign(hitNormal.y)));
            tbnMatrix = mat3(
                vec3(1.0, 0.0, 0.0),
                vec3(0.0, 0.0, sign(hitNormal.y)),
                hitNormal
            );
        } else {
            uv = vec2(fract(hitPos.x * sign(hitNormal.z) - hitPos.z * sign(hitNormal.x)), fract(-hitPos.y));
            tbnMatrix = mat3(
                vec3(sign(hitNormal.z), 0.0, -sign(hitNormal.x)),
                vec3(0.0, -1.0, 0.0),
                hitNormal
            );
        }

        uv += 256.0 * 15.0 * UnpackUnorm2x4(voxel[1].y) + 255.0 * voxel[1].zw;
        uv *= exp2(voxel[1].x * 15.0) / textureSize(atlas, 0);

        return uv;
    }
    vec2 GetUv(vec3 hitPos, vec3 hitNormal, vec4[2] voxel) {
        vec2 uv;
        if (abs(hitNormal.y) > abs(hitNormal.x) && abs(hitNormal.y) > abs(hitNormal.z)) {
            uv = vec2(fract(hitPos.x), fract(hitPos.z * sign(hitNormal.y)));
        } else {
            uv = vec2(fract(hitPos.x * sign(hitNormal.z) - hitPos.z * sign(hitNormal.x)), fract(-hitPos.y));
        }

        uv += 256.0 * 15.0 * UnpackUnorm2x4(voxel[1].y) + 255.0 * voxel[1].zw;
        uv *= exp2(voxel[1].x * 15.0) / textureSize(atlas, 0);

        return uv;
    }
    bool AlphaTest(vec3 hitPos, vec4[2] voxel, vec3 hitNormal, out vec2 uv, out mat3 tbnMatrix) {
        uv = GetUv(hitPos, hitNormal, voxel, tbnMatrix);

        // TODO: Ray differentials to be able to safely use lod > 0?
        return textureLod(atlas, uv, 0).a > max(randNextF(), 26.5 / 255.0);
        //return textureLod(atlas, uv, 0).a > randNextF();
    }
    bool AlphaTest(vec3 hitPos, vec4[2] voxel, vec3 hitNormal, out vec2 uv) {
        uv = GetUv(hitPos, hitNormal, voxel);

        // TODO: Ray differentials to be able to safely use lod > 0?
        return textureLod(atlas, uv, 0).a > max(randNextF(), 26.5 / 255.0);
        //return textureLod(atlas, uv, 0).a > randNextF();
    }

    bool RaytraceVoxelsAlphatest(
        vec3 pos, ivec3 idx, vec3 dir,
        out vec3 hitPos, out ivec3 hitIdx, out vec3 hitNormal,
        out vec4[2] hitVoxel, out vec2 hitUv
    ) {
        // In theory, using ints for idx should be faster on Volta & Turing, as well as AMD.

        // Early-exit when outside voxelized volume
        if (OutsideVoxelmap(idx)) { return false; }

        #ifdef INTERSECT_PARTIAL_BLOCKS
            // Check for intersection with starting voxel
            // TODO: It might be possible to optimize this
            vec2 voxelmapCoord = CalculateVoxelmapCoord(idx);
            hitVoxel[0] = texture(shadowcolor0, voxelmapCoord);
            if (hitVoxel[0].a > 0.5 / 255.0 && hitVoxel[0].a < (254.5 - OCTREE_LEVELS) / 255.0) {
                float hitDist;

                if (IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - hitVoxel[0].a * 255.0), hitDist, hitNormal)) {
                    hitPos = pos + dir * hitDist;
                    hitVoxel[1] = texture(shadowcolor1, voxelmapCoord);

                    if (AlphaTest(hitPos, hitVoxel, hitNormal, hitUv)) {
                        hitIdx = idx;
                        return true;
                    }
                }
            }
        #endif

        #if OCTREE_LEVELS > 1
        ivec3 deltaSign = ivec3(sign(dir));
        if (deltaSign.x == 0 && deltaSign.y == 0 && deltaSign.z == 0) { return false; }
        ivec3 positiveDir = max0(deltaSign);
        ivec3 negativeDir = min(deltaSign, 0);

        vec3 deltaT = 1.0 / abs(dir);

        const int maxLevel = OCTREE_LEVELS - 1;
        int level = 0;
        while (level++ < maxLevel && !VoxelIsOccupied(idx, level)); --level;

        // there's probably a better name for this
        vec3 steps = (idx - pos) * deltaSign + positiveDir - 1;

        bool hit = false;
        int i = 0;
        do {
            float minT; bvec3 isMinComp;
            bool potentialHit = FindPotentialHit(
                pos, dir,
                deltaT, deltaSign, positiveDir,
                idx, level,
                steps, i,
                minT, isMinComp
            );

            // If a potential hit was found, try to intersect
            if (potentialHit) {
                vec2 voxelmapCoord = CalculateVoxelmapCoord(idx);
                hitVoxel[0] = texture(shadowcolor0, voxelmapCoord);
                hitVoxel[1] = texture(shadowcolor1, voxelmapCoord);

                hitIdx = idx;

                float hitDist = minT;
                hitNormal = vec3(isMinComp) * -deltaSign;

                #ifdef INTERSECT_PARTIAL_BLOCKS
                hit = IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - hitVoxel[0].a * 255.0), hitDist, hitNormal);
                #else
                hit = true;
                #endif

                hitPos = pos + hitDist * dir;

                hit = hit && AlphaTest(hitPos, hitVoxel, hitNormal, hitUv);
            } else {
                break;
            }
        } while (++i < 4096 && !hit);
        #else
        vec3 nextT;
        vec3 deltaDist = 1.0 / abs(dir);
        ivec3 deltaSign;
        for (int axis = 0; axis < 3; ++axis) {
            if (dir[axis] < 0.0) {
                deltaSign[axis] = -1;
                nextT[axis] = (pos[axis] - idx[axis]) * deltaDist[axis];
            } else if (dir[axis] > 0.0) {
                deltaSign[axis] = 1;
                nextT[axis] = (idx[axis] - pos[axis]) * deltaDist[axis] + deltaDist[axis];
            } else {
                deltaSign[axis] = 0;
                nextT[axis] = uintBitsToFloat(0x7f800000u);// = infinity
            }
        }

        if (deltaSign.x == 0 && deltaSign.y == 0 && deltaSign.z == 0) { return false; }

        bool hit = false;
        do {
            float hitDist;

            float minComp = minof(nextT);
            if (nextT.x == minComp) {
                hitDist   = nextT.x;
                hitNormal = vec3(-deltaSign.x, 0.0, 0.0);

                idx.x   += deltaSign.x;
                nextT.x += deltaDist.x;
            }
            if (nextT.y == minComp) {
                hitDist   = nextT.y;
                hitNormal = vec3(0.0, -deltaSign.y, 0.0);

                idx.y   += deltaSign.y;
                nextT.y += deltaDist.y;
            }
            if (nextT.z == minComp) {
                hitDist   = nextT.z;
                hitNormal = vec3(0.0, 0.0, -deltaSign.z);

                idx.z   += deltaSign.z;
                nextT.z += deltaDist.z;
            }

            #if RAYTRACE_MAX_T >= 0
            if (minof(nextT) > RAYTRACE_MAX_T) { break; }
            #endif
            if (OutsideVoxelmap(idx)) { break; }

            vec2 voxelmapCoord = CalculateVoxelmapCoord(idx);
            hitVoxel[0] = texture(shadowcolor0, voxelmapCoord);
            if (hitVoxel[0].a < (255.5 - OCTREE_LEVELS) / 255.0) {
                hitVoxel[1] = texture(shadowcolor1, voxelmapCoord);

                #ifdef INTERSECT_PARTIAL_BLOCKS
                hit = IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - hitVoxel[0].a * 255.0), hitDist, hitNormal);
                #else
                hit = true;
                #endif

                hitPos = pos + hitDist * dir; hitIdx = idx;
                hit = hit && AlphaTest(hitPos, hitVoxel, hitNormal, hitUv);
            }
        } while (!hit);
        #endif

        return hit;
    }
    bool RaytraceVoxelsAlphatest(
        vec3 pos, ivec3 idx, vec3 dir,
        out vec3 hitPos, out ivec3 hitIdx, out vec3 hitNormal,
        out vec4[2] hitVoxel, out vec2 hitUv, out mat3 tbnMatrix
    ) {
        // In theory, using ints for idx should be faster on Volta & Turing, as well as AMD.

        // Early-exit when outside voxelized volume
        if (OutsideVoxelmap(idx)) { return false; }

        #ifdef INTERSECT_PARTIAL_BLOCKS
            // Check for intersection with starting voxel
            // TODO: It might be possible to optimize this
            vec2 voxelmapCoord = CalculateVoxelmapCoord(idx);
            hitVoxel[0] = texture(shadowcolor0, voxelmapCoord);
            if (hitVoxel[0].a > 0.5 / 255.0 && hitVoxel[0].a < (254.5 - OCTREE_LEVELS) / 255.0) {
                float hitDist;

                if (IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - hitVoxel[0].a * 255.0), hitDist, hitNormal)) {
                    hitPos = pos + dir * hitDist;
                    hitVoxel[1] = texture(shadowcolor1, voxelmapCoord);

                    if (AlphaTest(hitPos, hitVoxel, hitNormal, hitUv, tbnMatrix)) {
                        hitIdx = idx;
                        return true;
                    }
                }
            }
        #endif

        #if OCTREE_LEVELS > 1
        ivec3 deltaSign = ivec3(sign(dir));
        if (deltaSign.x == 0 && deltaSign.y == 0 && deltaSign.z == 0) { return false; }
        ivec3 positiveDir = max0(deltaSign);
        ivec3 negativeDir = min(deltaSign, 0);

        vec3 deltaT = 1.0 / abs(dir);

        const int maxLevel = OCTREE_LEVELS - 1;
        int level = 0;
        while (level++ < maxLevel && !VoxelIsOccupied(idx, level)); --level;

        // there's probably a better name for this
        vec3 steps = (idx - pos) * deltaSign + positiveDir - 1;

        bool hit = false;
        int i = 0;
        do {
            float minT; bvec3 isMinComp;
            bool potentialHit = FindPotentialHit(
                pos, dir,
                deltaT, deltaSign, positiveDir,
                idx, level,
                steps, i,
                minT, isMinComp
            );

            // If a potential hit was found, try to intersect
            if (potentialHit) {
                vec2 voxelmapCoord = CalculateVoxelmapCoord(idx);
                hitVoxel[0] = texture(shadowcolor0, voxelmapCoord);
                hitVoxel[1] = texture(shadowcolor1, voxelmapCoord);

                hitIdx = idx;

                float hitDist = minT;
                hitNormal = vec3(isMinComp) * -deltaSign;

                #ifdef INTERSECT_PARTIAL_BLOCKS
                hit = IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - hitVoxel[0].a * 255.0), hitDist, hitNormal);
                #else
                hit = true;
                #endif

                hitPos = pos + hitDist * dir;

                hit = hit && AlphaTest(hitPos, hitVoxel, hitNormal, hitUv, tbnMatrix);
            } else {
                break;
            }
        } while (++i < 4096 && !hit);
        #else
        vec3 nextT;
        vec3 deltaDist = 1.0 / abs(dir);
        ivec3 deltaSign;
        for (int axis = 0; axis < 3; ++axis) {
            if (dir[axis] < 0.0) {
                deltaSign[axis] = -1;
                nextT[axis] = (pos[axis] - idx[axis]) * deltaDist[axis];
            } else if (dir[axis] > 0.0) {
                deltaSign[axis] = 1;
                nextT[axis] = (idx[axis] - pos[axis]) * deltaDist[axis] + deltaDist[axis];
            } else {
                deltaSign[axis] = 0;
                nextT[axis] = uintBitsToFloat(0x7f800000u);// = infinity
            }
        }

        if (deltaSign.x == 0 && deltaSign.y == 0 && deltaSign.z == 0) { return false; }

        bool hit = false;
        do {
            float hitDist;

            float minComp = minof(nextT);
            if (nextT.x == minComp) {
                hitDist   = nextT.x;
                hitNormal = vec3(-deltaSign.x, 0.0, 0.0);

                idx.x   += deltaSign.x;
                nextT.x += deltaDist.x;
            }
            if (nextT.y == minComp) {
                hitDist   = nextT.y;
                hitNormal = vec3(0.0, -deltaSign.y, 0.0);

                idx.y   += deltaSign.y;
                nextT.y += deltaDist.y;
            }
            if (nextT.z == minComp) {
                hitDist   = nextT.z;
                hitNormal = vec3(0.0, 0.0, -deltaSign.z);

                idx.z   += deltaSign.z;
                nextT.z += deltaDist.z;
            }

            #if RAYTRACE_MAX_T >= 0
            if (minof(nextT) > RAYTRACE_MAX_T) { break; }
            #endif
            if (OutsideVoxelmap(idx)) { break; }

            vec2 voxelmapCoord = CalculateVoxelmapCoord(idx);
            hitVoxel[0] = texture(shadowcolor0, voxelmapCoord);
            if (hitVoxel[0].a < (255.5 - OCTREE_LEVELS) / 255.0) {
                hitVoxel[1] = texture(shadowcolor1, voxelmapCoord);

                #ifdef INTERSECT_PARTIAL_BLOCKS
                hit = IntersectVoxel(pos - idx, dir, int((255.5 - OCTREE_LEVELS) - hitVoxel[0].a * 255.0), hitDist, hitNormal);
                #else
                hit = true;
                #endif

                hitPos = pos + hitDist * dir; hitIdx = idx;
                hit = hit && AlphaTest(hitPos, hitVoxel, hitNormal, hitUv, tbnMatrix);
            }
        } while (!hit);
        #endif

        return hit;
    }

    bool RaytraceVoxelsAlphatest(vec3 pos, ivec3 idx, vec3 dir) {
        vec3 hitPos, hitNormal; ivec3 hitIdx; vec4[2] hitVoxel; vec2 hitUv;
        return RaytraceVoxelsAlphatest(pos, idx, dir, hitPos, hitIdx, hitNormal, hitVoxel, hitUv);
    }
#endif