#version 430

layout(location = 0) in vec3 vertexIn;            // Each vertex supplied
layout(location = 1) in vec3 normalIn;            // Each normal supplied
layout(location = 2) in vec2 textureCoordinateIn; // Each texture coordinate supplied

out VsData
{
    vec3 normalOut;            // Transformed normal based on the normal matrix transform
    vec2 textureCoordinateOut; // Passthrough
    vec3 positionOut;          // Passthrough for deferred shadow rendering
    vec4 projPositionOut;
    vec4 prevProjPositionOut;
    vec3 modelPosition;
}
vsData;

uniform mat4 prevModel;  // Previous Model transformation matrix
uniform mat4 prevView;   // Previous View/Camera transformation matrix
uniform mat4 model;      // Model and World transformation matrix
uniform mat4 view;       // View/Camera transformation matrix
uniform mat4 projection; // Projection transformation matrix
uniform mat4 normal;     // Normal matrix

// For heightmap terrains 200x200 blocks
uniform sampler2D heightmaptex0; // texture indicating elevation
uniform int       isLayeredTexture;
uniform int       heightmapWidth;
uniform int       heightmapHeight;

void main()
{

    // transform based on heightmap if the vertex is terrain based
    // each terrain tile is 200 by 200 and the resolution can be dynamic of the sample points based
    // on the granurity of the vertices of the tile

    float heightOffset   = 0.0f;
    vec3  normalCoord    = normalIn;
    vsData.modelPosition = (model * vec4(vertexIn.xyz, 1.0)).xyz;
    if (isLayeredTexture == 1)
    {
        // Normalized texture indexing between 0.0 and 1.0
        float halfWidth  = heightmapWidth / 2;
        float halfHeight = heightmapHeight / 2;

        vec2 xzIndex = (vsData.modelPosition.xz + vec2(halfWidth, halfHeight)) /
                       vec2(float(heightmapWidth), float(heightmapHeight));

        heightOffset = (texture2D(heightmaptex0, xzIndex).x) - 0.5;

        // sample at a lower frequency and relative to the actually translation of where the tile is
        // placed The frequency will be scaled down by every 5x5 tile grid so the space will be
        // 1000.0f
        // vec2 lowFreqIndex = abs((model * vec4(vsData.modelPosition.x, vsData.modelPosition.y,
        // vsData.modelPosition.z, 1.0)).xz); lowFreqIndex /= 1000.0f; heightOffset +=
        // (texture2D(heightmaptex0, lowFreqIndex).x) - 0.5;

        // Scale up or down by 50 which indicates a 100 swing in height
        heightOffset *= 100.0f;

        vec2  offset      = 1.0 / vec2(heightmapWidth, heightmapHeight);
        vec2  center      = xzIndex;
        float heightLeft  = texture2D(heightmaptex0, center + vec2(-offset.x, 0.0)).x;
        float heightRight = texture2D(heightmaptex0, center + vec2(offset.x, 0.0)).x;
        float heightUp    = texture2D(heightmaptex0, center + vec2(0.0, offset.y)).x;

        // Only need 3 points to find the normal using the crossproduct
        vec3 pointA = vec3(center.x - offset.x, heightLeft, center.y);
        vec3 pointB = vec3(center.x + offset.x, heightRight, center.y);
        vec3 pointC = vec3(center.x, heightUp, center.y + offset.y);

        normalCoord = cross((pointC - pointA), (pointB - pointA));
        normalCoord = normalize(normalCoord);

        vsData.modelPosition = vec3(vsData.modelPosition + vec3(0.0, heightOffset, 0.0));
    }

    // The vertex is first transformed by the model and world, then
    // the view/camera and finally the projection matrix
    // The order in which transformation matrices affect the vertex
    // is in the order from right to left
    vec4 transformedVert =
        projection * view * model * vec4(vertexIn.x, vertexIn.y + heightOffset, vertexIn.z, 1.0);
    // store only in model space so deferred shadow rendering is done properly
    vsData.positionOut = vec3((view * model * vec4(vertexIn.xyz, 1.0)).xyz);

    // Transform normal coordinate in with the normal matrix
    vsData.normalOut = vec3((normal * vec4(normalCoord, 0.0)).xyz);

    vsData.textureCoordinateOut = textureCoordinateIn; // Passthrough

    // vertex buffer previous
    vsData.prevProjPositionOut = projection * prevView * prevModel * vec4(vertexIn.xyz, 1.0);
    vsData.projPositionOut     = transformedVert; // vertex buffer current
    // Pass the transformed vertex to the fragment shader
    gl_Position = transformedVert;
}