#version 450

layout(vertices = 4) out;
layout(location = 0) in vec3 iPos[];
layout(location = 1) flat in int iChunkID[];

layout(set = 0, binding = 0) uniform uGlobal {
    mat4 view;
    mat4 proj;
    vec4 camPos;
};

layout(set = 0, binding = 1) uniform uEntity {
    mat4 model;
};

layout(location = 0) out vec3 oPos[];
layout(location = 1) flat out int oChunkID[];


const float MIN_TESS_LEVEL = 1.0;
const float MAX_TESS_LEVEL = 64;
const float MIN_DISTANCE = 80;
const float MAX_DISTANCE = 500;

float getLod(vec4 point) {
    float dist = distance(point.xyz, camPos.xyz);
    float factor = clamp((dist - MIN_DISTANCE) / (MAX_DISTANCE - MIN_DISTANCE), 0.0, 1.0);
    return mix(MAX_TESS_LEVEL, MIN_TESS_LEVEL, factor);
}

void main() {
    if (gl_InvocationID == 0) {
        vec4 p0 = model * gl_in[0].gl_Position;
        vec4 p1 = model * gl_in[1].gl_Position;
        vec4 p2 = model * gl_in[2].gl_Position;
        vec4 p3 = model * gl_in[3].gl_Position;
        gl_TessLevelOuter[0] = getLod((p3 + p0) * 0.5);
        gl_TessLevelOuter[1] = getLod((p0 + p1) * 0.5);
        gl_TessLevelOuter[2] = getLod((p1 + p2) * 0.5);
        gl_TessLevelOuter[3] = getLod((p2 + p3) * 0.5);

        gl_TessLevelInner[0] = mix(gl_TessLevelOuter[1], gl_TessLevelOuter[3], 0.5);
        gl_TessLevelInner[1] = mix(gl_TessLevelOuter[0], gl_TessLevelOuter[2], 0.5);
    }
    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
    oPos[gl_InvocationID] = iPos[gl_InvocationID];
    oChunkID[gl_InvocationID] = iChunkID[gl_InvocationID];
}
