/***********************************************/
/*           Copyright (c) 2025 Belmu          */
/*             All Rights Reserved             */
/***********************************************/

/*
    [Credits]:
        Xirreal - providing base code for voxelization, thanks to them!
*/

#include "/settings.glsl"
#include "/include/common.glsl"

#if defined STAGE_VERTEX

    #define attribute in
    attribute vec2 mc_midTexCoord;
    attribute vec3 at_midBlock;
    attribute vec3 mc_Entity;

    out VertexData {
        int  blockId;
        vec2 textureCoords;
        vec2 midCoords;
        vec3 midBlock;
        vec3 normal;
        vec3 scenePosition;
        vec4 tint;
    } vs;

    uniform mat4 shadowModelViewInverse;

    void main() {
        vs.blockId = int(mc_Entity.x);

        vs.midCoords     = mc_midTexCoord;
        vs.textureCoords = gl_MultiTexCoord0.xy;

        vs.midBlock      = at_midBlock / 64.0;
        vs.normal        = gl_Normal;
        vs.scenePosition = (shadowModelViewInverse * gl_ModelViewMatrix * gl_Vertex).xyz;

        vs.tint = gl_Color;
    }

#elif defined STAGE_GEOMETRY

    layout (triangles) in;

    in VertexData {
        int  blockId;
        vec2 textureCoords;
        vec2 midCoords;
        vec3 midBlock;
        vec3 normal;
        vec3 scenePosition;
        vec4 tint;
    } vs[3];

    layout (points, max_vertices = 1) out;

    uniform sampler2D gtexture;

    uniform int renderStage;

    #if SPHERES_MODE == 1
        const vec3 glassColors[] = vec3[](
            vec3(0.090, 0.541, 0.910),
            vec3(0.090, 0.949, 0.027),
            vec3(0.949, 0.118, 0.075),
            vec3(0.949, 0.600, 0.075),
            vec3(0.929, 0.047, 0.886),
            vec3(0.969, 0.875, 0.027)
        );
    #endif

    #include "/include/voxels/voxelization.glsl"

    void main() {
        if (renderStage != MC_RENDER_STAGE_TERRAIN_SOLID         &&
            renderStage != MC_RENDER_STAGE_TERRAIN_CUTOUT        &&
            renderStage != MC_RENDER_STAGE_TERRAIN_CUTOUT_MIPPED &&
            renderStage != MC_RENDER_STAGE_TERRAIN_TRANSLUCENT)  return;

        vec3  voxelCenter   = (vs[0].midBlock + vs[1].midBlock + vs[2].midBlock + vs[0].scenePosition + vs[1].scenePosition + vs[2].scenePosition) / 3.0;
        ivec3 voxelPosition = ivec3(floor(voxelCenter + cameraPosition));

        if (!isInVoxelBounds(voxelPosition)) return;

        vec2  atlasAspectCorrect = vec2(1.0, atlasSize.x / atlasSize.y);

        // Thanks Zombye for the help with fixing maximum edge calculation for versions above 1.16
        float maxEdge        = max(maxOf(abs(vs[0].scenePosition - vs[1].scenePosition)), maxOf(abs(vs[0].scenePosition - vs[2].scenePosition)));
        float tileSize       = max(maxOf(abs(vs[0].textureCoords - vs[0].midCoords) / atlasAspectCorrect), maxOf(abs(vs[1].textureCoords - vs[1].midCoords) / atlasAspectCorrect)) / maxEdge;
        float tileResolution = 2.0 * tileSize * maxOf(atlasSize);
        ivec2 tileIndex      = ivec2(round((vs[0].midCoords - tileSize * atlasAspectCorrect) * atlasSize / round(tileResolution)));

        uint facePriority = uint(dot(max0(vs[0].normal), vec3(4, 5, 3)) + dot(max0(-vs[0].normal), vec3(2, 0, 1)));

        vec3 tint = vs[0].tint.rgb;
        #if SPHERES_MODE == 1
            if (vs[0].blockId == SPHERE_1) {
                tint = glassColors[int(rand(abs((voxelCenter + cameraPosition).xz * voxelCenter.y)) * 5.0)];
            }
        #endif

        writeVoxel(voxelPosition, tint, vs[0].blockId, facePriority, int(ceil(tileResolution)), tileIndex);

        EmitVertex();
        EndPrimitive();
    }

#elif defined STAGE_FRAGMENT

    void main() {
        discard; return;
    }
    
#endif
