#if !defined VOXELIZATION_LIB
#define VOXELIZATION_LIB
    vec3 VoxelSpaceFromSceneSpace(vec3 position) {
        const float radius = VOXELMAP_RESOLUTION / 32;

        position.xz += fract(cameraPosition.xz) + radius;
        position.y  += cameraPosition.y;

        return position;
    }
    vec3 SceneSpaceFromVoxelSpace(vec3 position) {
        const float radius = VOXELMAP_RESOLUTION / 32;

        position.xz -= fract(cameraPosition.xz) + radius;
        position.y  -= cameraPosition.y;

        return position;
    }

    // Bounds check
    bool InsideVoxelmap(uvec3 idx) {
        const uvec2 maxBounds = uvec2(VOXELMAP_RESOLUTION / 16, 256) - 1u;

        return all(lessThanEqual(idx, maxBounds.xyx));
    }
    bool InsideVoxelmap(ivec3 idx) {
        // Converting to a uvec3 should be free as it preserves the bit pattern.
        return InsideVoxelmap(uvec3(idx));
    }
    bool InsideVoxelmap(vec3 idx) {
        const vec2 maxBounds = vec2(VOXELMAP_RESOLUTION / 16, 256) - 1.0;
        /*
        return clamp(idx, vec3(0.0), maxBounds.xyx) == idx;
        /*/
        return all(greaterThanEqual(idx, vec3(0.0))) && all(lessThanEqual(idx, maxBounds.xyx));
        //*/
    }

    bool OutsideVoxelmap(uvec3 idx) {
        const uvec2 maxBounds = uvec2(VOXELMAP_RESOLUTION / 16, 256) - 1u;

        return any(greaterThan(idx, maxBounds.xyx));
    }
    bool OutsideVoxelmap(ivec3 idx) {
        // Converting to a uvec3 should be free as it preserves the bit pattern.
        return OutsideVoxelmap(uvec3(idx));
    }
    bool OutsideVoxelmap(vec3 idx) {
        const vec2 maxBounds = vec2(VOXELMAP_RESOLUTION / 16, 256) - 1.0;
        /*
        return clamp(idx, vec3(0.0), maxBounds.xyx) != idx;
        /*/
        return any(lessThan(idx, vec3(0.0))) || any(greaterThan(idx, maxBounds.xyx));
        //*/
    }

    // Lookup
    uvec2 CalculateVoxelmapIdx(uvec3 idx) {
        const uint range = uint(VOXELMAP_RESOLUTION / 16);

        uint linear = idx.x + range * (idx.y + 256u * idx.z);
        uvec2 pos = uvec2(linear % uint(VOXELMAP_RESOLUTION), linear / uint(VOXELMAP_RESOLUTION));

        return pos;
    }
    ivec2 CalculateVoxelmapIdx(ivec3 idx) {
        const int range = VOXELMAP_RESOLUTION / 16;

        int linear = idx.x + range * (idx.y + 256 * idx.z);
        ivec2 pos = ivec2(linear % VOXELMAP_RESOLUTION, linear / VOXELMAP_RESOLUTION);

        return pos;
    }
    vec2 CalculateVoxelmapCoord(vec3 idx) {
        const float range = float(VOXELMAP_RESOLUTION / 16);

        float linear = idx.x + range * (idx.y + 256.0 * idx.z);
        vec2 pos; pos.x = modf(linear / float(VOXELMAP_RESOLUTION), pos.y) * float(VOXELMAP_RESOLUTION);

        pos /= SHADOWTEX_RESOLUTION;
        pos += 0.5 / SHADOWTEX_RESOLUTION;

        return pos;
    }

    #ifdef DEV_VIS
    uint[8] reads = uint[8](0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u);
    #endif
    #if OCTREE_LEVELS > 1 && defined STAGE_FRAGMENT
    bool VoxelIsOccupied(uvec3 idx, uint level) {
        #ifdef DEV_VIS
        ++reads[level];
        #endif

        float cmp = (255.5 - OCTREE_LEVELS) / 255.0 + float(level) / 255.0;
        uvec2 voxelmapIdx = CalculateVoxelmapIdx((idx >> level) << level);
        return texelFetch(shadowcolor0, ivec2(voxelmapIdx), 0).a < cmp;
    }
    bool VoxelIsOccupied(ivec3 idx, int level) {
        #ifdef DEV_VIS
        ++reads[level];
        #endif

        float cmp = (255.5 - OCTREE_LEVELS) / 255.0 + float(level) / 255.0;
        ivec2 voxelmapIdx = CalculateVoxelmapIdx((idx >> level) << level);
        return texelFetch(shadowcolor0, voxelmapIdx, 0).a < cmp;
    }
    bool VoxelIsOccupied(vec3 idx, int level) {
        #ifdef DEV_VIS
        ++reads[level];
        #endif

        float cmp = (255.5 - OCTREE_LEVELS) / 255.0 + float(level) / 255.0;
        //vec2 voxelmapCoord = CalculateVoxelmapCoord(floor(idx / float(1 << level)) * float(1 << level));
        //vec2 voxelmapCoord = CalculateVoxelmapCoord((ivec3(idx) >> level) << level);
        //return texture(shadowcolor0, voxelmapCoord).a < cmp;
        ivec2 voxelmapIdx = CalculateVoxelmapIdx((ivec3(idx) >> level) << level);
        return texelFetch(shadowcolor0, voxelmapIdx, 0).a < cmp;
    }
    #endif
#endif