#if COLORED_LIGHTING < 512
    const ivec3 sceneVoxelVolumeSize = ivec3(COLORED_LIGHTING_INTERNAL, 64, COLORED_LIGHTING_INTERNAL);
#else
    const ivec3 sceneVoxelVolumeSize = ivec3(512, 64, 512);
#endif

const ivec3 sceneVoxelLodVolumeSize = sceneVoxelVolumeSize / 4;

vec3 playerToSceneVoxel(vec3 playerPos) {
    return playerPos + cameraPositionBestFract + 0.5 * vec3(sceneVoxelVolumeSize);
}

vec3 playerToPreviousSceneVoxel(vec3 previousPlayerPos) {
    return previousPlayerPos + previousCameraPositionBestFract + 0.5 * vec3(sceneVoxelVolumeSize);
}

bool CheckInsideSceneVoxelVolume(vec3 voxelPos) {
    #ifndef SHADOW
        voxelPos -= 0.5 * sceneVoxelVolumeSize;
        voxelPos += sign(voxelPos) * 0.95;
        voxelPos += 0.5 * sceneVoxelVolumeSize;
    #endif
    voxelPos /= vec3(sceneVoxelVolumeSize);
    return clamp01(voxelPos) == voxelPos;
}

#include "/lib/voxelization/reflectionVoxelData.glsl"

#if defined SHADOW && defined VERTEX_SHADER
    void UpdateSceneVoxelMap(int mat, vec3 normal, vec3 position) {
        ivec3 eligibleStages = ivec3(
            MC_RENDER_STAGE_TERRAIN_SOLID,
            MC_RENDER_STAGE_TERRAIN_CUTOUT,
            MC_RENDER_STAGE_TERRAIN_CUTOUT_MIPPED
        );

        if (!any(equal(ivec3(renderStage), eligibleStages))) return;

        vec3 viewPos  = mat3(gl_ModelViewMatrix) * (gl_Vertex.xyz + at_midBlock.xyz / 64.0) + gl_ModelViewMatrix[3].xyz;
        vec3 scenePos = mat3(shadowModelViewInverse) * viewPos + shadowModelViewInverse[3].xyz;
        vec3 voxelPos = playerToSceneVoxel(scenePos);

        if (CheckInsideSceneVoxelVolume(voxelPos)) {
            bool doSolidBlockCheck = true;
            bool storeToAllFaces = false;
            bool storeToAllFacesExceptTop = false;
            uint matM = mat > 10 ? uint(mat) : 1u;
            vec2 textureRad = abs(texCoord - mc_midTexCoord.xy);
            vec2 origin = mc_midTexCoord.xy - textureRad;

            if (mat == 10132) { // Grass Block Regular
                if (texture2D(tex, mc_midTexCoord.xy).a < 0.5) return; // Grass Block Side Overlay
            }

            if (abs(mat - 10009) <= 2) { // Leaves (10007, 10009, 10011)
                doSolidBlockCheck = false;
            }

            if (mat == 10129 // Farmland:Dry
                || mat == 10137 // Farmland:Wet
                || mat == 10493 // Dirt Path
            ) {
                doSolidBlockCheck = false;
                textureRad *= 0.5;
                origin.y += 2.0 / atlasSize.y;
            }

            if (abs(mat - 10069) <= 1 // Lava (10068, 10070)
            ) {
                if (abs(dot(textureRad, vec2(atlasSize.x, -atlasSize.y))) < 6.5)
                    storeToAllFaces = true;
                else return;
            }

            // Half blocks that we want to display as full blocks in reflections
            if (mat == 10035 // Stone Bricks, Mossy Stone Bricks
                || abs(mat - 10095) <= 12 && mat % 4 == 3 // Stone, Smooth Stone, Granite, Diorite, Andesite, Bricks, Mud Bricks
                || mat == 10155 // Cobblestone, Mossy Cobblestone
                || abs(mat - 10191) <= 32 && mat % 8 == 7 // Oak, Spruce, Birch, Jungle, Acacia, DarkOak, Mangrove, Crimson, Warped
                || mat == 10111 // Cobbled Deepslate
                || mat == 10115 // Polished Deepslate, Deepslate Bricks, Deepslate Tiles
                || mat == 10243 // Sandstone
                || mat == 10247 // Red Sandstone
                || mat == 10295 // Copper
                || mat == 10367 // Quartz
                || mat == 10379 // Purpur
                || mat == 10381 // Powder Snow
                || mat == 10419 // Nether Bricks
                || mat == 10423 // Red Nether Bricks
                || mat == 10431 // End Stone Bricks
                || mat == 10443 // Prismarine, Prismarine Bricks
                || mat == 10447 // Dark Prismarine
                || mat == 10483 // Blackstone
                || mat == 10715 // Tuff
                || mat == 10759 // Bamboo, Bamboo Mosaic
                || mat == 10763 // Cherry
                || mat == 10931 // Pale Oak
            ) {
                if (textureRad.y < 5.0 / atlasSize.y) {
                    // Discarding if textureRad is too small to fix (somewhat rare) flickering on stairs
                    if (textureRad.x < 5.0 / atlasSize.x) return;

                    // Half textureRad for stairs and slabs to not overshoot their textures
                    textureRad *= 0.5;

                    // P.S: Don't ask me how any of these checks make sense because I have absolutely no idea either
                }

                doSolidBlockCheck = false;
                if (normal.y < 0.5) storeToAllFacesExceptTop = true; // Not overriding top face or else carpets look broken on top of slabs
            }

            if (mat == 10669 || mat == 10843|| mat == 10845 || mat == 10925 || mat == 10953) { // Wool Carpets, Moss Carpet, Snow Layers < 8
                if (normal.y > 0.5) {
                    voxelPos.y -= 1.0;
                    doSolidBlockCheck = false;
                } else return;
            }

            if (mat == 10072 // Fire
                || mat == 10076 // Soul Fire
                || mat == 10332 // Amethyst Clusters
                || mat == 10544 // Glow Lichen
            ) {
                doSolidBlockCheck = false;
                storeToAllFaces = true;
            }

            if (mat == 10652 || mat == 10656) { // Campfire:Lit, Soul Campfire:Lit
                if (abs(abs(normal.x) - 0.5) < 0.25) {
                    doSolidBlockCheck = false;
                    storeToAllFaces = true;
                } else return;
            }

            // Blocks to remove from reflections
            if (mat == 10056 // Lava Cauldron
                || mat == 10404 // Sea Pickle
                || mat == 10496 // Torch
                || mat == 10500 // End Rod
                || mat == 10508 // Chorus Flower:Alive
                || mat == 10512 // Chorus Flower:Dead
                || mat == 10528 // Soul Torch
                || mat == 10556 // End Portal Frame:Active
                || mat == 10572 // Dragon Egg
                || mat == 10604 || mat == 10605 // Redstone Torch
                || mat == 10632 // Cave Vines
                || mat == 10644 // Powered Repeater, Powered Comparator
                || mat == 10776 // Crimson Fungus, Warped Fungus
                || mat == 10780 // Potted Crimson Fungus, Potted Warped Fungus
                || mat == 10836 // Brewing Stand
                || mat == 10884 // Weeping Vines
                || mat == 10972 // Firefly Bush
                || mat == 10976 // Open Eyeblossom
                || mat == 10980 // Potted Open Eyeblossom
                || abs(mat - 10562) <= 2 // Lantern & Soul Lantern
                || abs(mat - 10599) <= 3 // Redstone Wire
                || abs(mat - 10701) <= 3 // Non-Solid Sculk Stuff
                || abs(mat - 10786) <= 2 // Calibrated Sculk Sensor
                || abs(mat - 10911) <= 11 // Lit Candles & Candle Cakes
                || mat == 10984 // Copper Torch
                || mat == 10988 // Copper Lantern
            ) {
                return;
            }

            if (doSolidBlockCheck) {
                if (
                    mat % 2 == 1 // Non-solids
                    || abs(mat - 5000) <= 4999 // Block entities that we treat as non-solid
                )
                return;
            }

            imageStore(wsr_img, ivec3(voxelPos), uvec4(matM, 0u, 0u, 0u));
            storeFaceData(ivec3(voxelPos), round(normal), origin, textureRad.x, storeToAllFaces, storeToAllFacesExceptTop, scenePos);

            updateWsrBitmask(ivec3(voxelPos));
            updateWsrLodBitmask(ivec3(voxelPos / 4.0));
        }
    }

    #if WORLD_SPACE_PLAYER_REF == 1
        uniform writeonly image2D playerAtlas_img;

        #define updateAABB(aabb_min, aabb_max, pos) \
                      atomicMin(aabb_min.x, pos.x); \
                      atomicMin(aabb_min.y, pos.y); \
                      atomicMin(aabb_min.z, pos.z); \
                      atomicMax(aabb_max.x, pos.x); \
                      atomicMax(aabb_max.y, pos.y); \
                      atomicMax(aabb_max.z, pos.z);


        void UpdatePlayerVertexList(vec3 position) {
            if (entityId == 50017 && textureSize(tex, 0) == ivec2(64) && gl_VertexID < 288) { // Current Player

                // The atlas takes 4 frames to fully generate, reload every 600 frames
                if (framemod600 > 5 && framemod600 <= 9 && gl_VertexID < 256) {
                    int i = gl_VertexID * 4 + int(framemod4) * 1024;
                    for (int j = 0; j < 4; j++) {
                        ivec2 coord = ivec2((i + j) % 64, (i + j) / 64);
                        imageStore(playerAtlas_img, coord, texelFetch(tex, coord, 0));
                    }
                }

                ivec3 aabbPos = ivec3(position * 1000.0);

                if (gl_VertexID < 48) { // Head
                    updateAABB(playerVerticesSSBO.bounds.headMin, playerVerticesSSBO.bounds.headMax, aabbPos);
                } else if (gl_VertexID < 96) { // Right Hand
                    updateAABB(playerVerticesSSBO.bounds.rightHandMin, playerVerticesSSBO.bounds.rightHandMax, aabbPos);
                } else if (gl_VertexID < 144) { // Left Leg
                    updateAABB(playerVerticesSSBO.bounds.leftLegMin, playerVerticesSSBO.bounds.leftLegMax, aabbPos);
                } else if (gl_VertexID < 192) { // Left Hand
                    updateAABB(playerVerticesSSBO.bounds.leftHandMin, playerVerticesSSBO.bounds.leftHandMax, aabbPos);
                } else if (gl_VertexID < 240) { // Right leg
                    updateAABB(playerVerticesSSBO.bounds.rightLegMin, playerVerticesSSBO.bounds.rightLegMax, aabbPos);
                } else { // Torso
                    updateAABB(playerVerticesSSBO.bounds.torsoMin, playerVerticesSSBO.bounds.torsoMax, aabbPos);
                }

                if (gl_VertexID % 4 != 3) {
                    int ssboIndex = gl_VertexID - gl_VertexID / 4;
                    playerVerticesSSBO.vertexPositions[ssboIndex] = position;
                    playerVerticesSSBO.vertexData[ssboIndex] = texCoord;
                }
            }
        }
    #endif
#endif
