//
//  object.vert
//  Kraken Engine
//
//  Copyright 2026 Kearwood Gilbert. All rights reserved.
//  
//  Redistribution and use in source and binary forms, with or without modification, are
//  permitted provided that the following conditions are met:
//  
//  1. Redistributions of source code must retain the above copyright notice, this list of
//  conditions and the following disclaimer.
//  
//  2. Redistributions in binary form must reproduce the above copyright notice, this list
//  of conditions and the following disclaimer in the documentation and/or other materials
//  provided with the distribution.
//  
//  THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
//  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
//  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//  
//  The views and conclusions contained in the software and documentation are those of the
//  authors and should not be interpreted as representing official policies, either expressed
//  or implied, of Kearwood Gilbert.
//

#version 450
#extension GL_GOOGLE_include_directive : enable

// TODO - HACK! Need to dynamically set these defines...
#define ENABLE_DIFFUSE 1
#define ENABLE_PER_PIXEL 1

layout(location = 0) in vec3 vertex_position;
layout(location = 1) in vec3 vertex_normal;

#if HAS_NORMAL_MAP == 1
layout(location = 2) in vec3 vertex_tangent;
#endif
layout(location = 3) in lowp vec3 vertex_texcoord0;

#if BONE_COUNT > 0
    layout(location = 4) in lowp vec3 vertex_texcoord0;
    layout(location = 5) in highp vec4 bone_weights;
    layout(location = 6) in highp vec4 bone_indexes;
#else
    #define vertex_position_skinned vertex_position
    #define vertex_normal_skinned vertex_normal
    #define vertex_tangent_skinned vertex_tangent
#endif

#if GBUFFER_PASS == 1
    #if HAS_NORMAL_MAP == 1
        highp mat3 tangent_to_view_matrix;
    #endif
#else
    #if HAS_LIGHT_MAP == 1
        layout(loction = 7) in mediump vec2  vertex_texcoord1;
    #endif //HAS_LIGHT_MAP
#endif // GBUFFER_PASS


layout( push_constant ) uniform constants
{
  highp mat4 mvp_matrix; // mvp_matrix is the result of multiplying the model, view, and projection matrices
#if BONE_COUNT > 0
  highp mat4 bone_transforms[BONE_COUNT];
#endif
#if ENABLE_PER_PIXEL == 1 || GBUFFER_PASS == 1
  #if HAS_NORMAL_MAP == 1
    #if HAS_NORMAL_MAP_SCALE == 1
      highp vec2 normalTexture_Scale;
    #endif
    #if HAS_NORMAL_MAP_OFFSET == 1
      highp vec2 normalTexture_Offset;
    #endif
  #endif
#else
  mediump float material_roughness_factor;
#endif
#if GBUFFER_PASS == 1
    #if HAS_NORMAL_MAP == 1
        highp mat4 model_view_inverse_transpose_matrix;
    #endif
#else
  highp vec3 light_direction_model_space; // Must be normalized before entering shader
  highp vec3 camera_position_model_space;
  #if ENABLE_PER_PIXEL == 1
      #if HAS_SPEC_MAP_SCALE == 1
          highp vec2 specularTexture_Scale;
      #endif

      #if HAS_SPEC_MAP_OFFSET == 1
          highp vec2 specularTexture_Offset;
      #endif
      
      #if HAS_REFLECTION_MAP_SCALE == 1
          highp vec2 reflectionTexture_Scale;
      #endif

      #if HAS_REFLECTION_MAP_OFFSET == 1
          highp vec2 reflectionTexture_Offset;
      #endif
      
      #if SHADOW_QUALITY >= 1
          highp mat4 shadow_mvp1;
      #endif

      #if SHADOW_QUALITY >= 2
          highp mat4 shadow_mvp2;
      #endif

      #if SHADOW_QUALITY >= 3
          highp mat4 shadow_mvp3;
      #endif
  #endif // ENABLE_PER_PIXEL
  
  #if HAS_REFLECTION_CUBE_MAP == 1
      #if HAS_NORMAL_MAP == 1
          #define NEED_EYEVEC
          highp mat4 model_inverse_transpose_matrix;
      #else
          highp mat4 model_matrix;
      #endif
  #endif
  
  #if HAS_DIFFUSE_MAP_SCALE == 1
      highp vec2  diffuseTexture_Scale;
  #endif

  #if HAS_DIFFUSE_MAP_OFFSET == 1
      highp vec2  diffuseTexture_Offset;
  #endif
#endif


#if ENABLE_RIM_COLOR == 1
    lowp vec3 rim_color;
    mediump float rim_power;
#endif

#if FOG_TYPE > 0
    // FOG_TYPE 1 - Linear
    // FOG_TYPE 2 - Exponential
    // FOG_TYPE 3 - Exponential squared
    lowp vec3 fog_color;
    mediump float fog_near;
    #if FOG_TYPE == 1
        mediump float fog_far;
        mediump float fog_scale;
    #endif

    #if FOG_TYPE > 1
        mediump float fog_density;
    #endif

    #if FOG_TYPE == 2
        mediump float fog_density_premultiplied_exponential;
    #endif
    #if FOG_TYPE == 3
        mediump float fog_density_premultiplied_squared;
    #endif
#endif


#if ENABLE_PER_PIXEL == 1 || GBUFFER_PASS == 1
    mediump float material_roughness_factor;
    #if HAS_NORMAL_MAP == 1
        sampler2D normalTexture;
    #endif
#endif


#if GBUFFER_PASS == 3
    sampler2D gbuffer_frame;
    sampler2D gbuffer_depth;
#endif

#if GBUFFER_PASS == 1
    #if HAS_NORMAL_MAP == 1

    #else
        highp mat4 model_view_inverse_transpose_matrix;
    #endif

    #if HAS_DIFFUSE_MAP == 1 && ALPHA_TEST == 1
        sampler2D     diffuseTexture;
    #endif
#else
    lowp vec3 material_ambient;
    lowp vec4 material_baseColor_factor;
    lowp vec3 material_specularColor_factor;

    #if HAS_DIFFUSE_MAP == 1
        sampler2D     diffuseTexture;
    #endif

    #if HAS_SPEC_MAP == 1
        sampler2D     specularTexture;
    #endif

    #if HAS_REFLECTION_MAP == 1
        sampler2D     reflectionTexture;
    #endif

    #if ENABLE_RIM_COLOR == 1
        #define NEED_EYEVEC
    #endif

    #if HAS_REFLECTION_CUBE_MAP == 1
        lowp vec3       material_reflection;
        samplerCube     reflectionCubeTexture;
        #if HAS_NORMAL_MAP == 1
            highp mat4 model_matrix;
        #endif
    #endif

    #if SHADOW_QUALITY >= 1
        #ifdef GL_EXT_shadow_samplers
            sampler2DShadow   shadowTexture1;
        #else
            sampler2D   shadowTexture1;
        #endif
    #endif

    #if HAS_LIGHT_MAP == 1
        sampler2D     lightmapTexture;
    #endif

    #if SHADOW_QUALITY >= 2
        ampler2D   shadowTexture2;
    #endif

    #if SHADOW_QUALITY >= 3
        sampler2D   shadowTexture3;
    #endif

#endif

#if GBUFFER_PASS == 1 || GBUFFER_PASS == 3
    mediump vec4 viewport;
#endif
} PushConstants;

#if ENABLE_PER_PIXEL == 1 || GBUFFER_PASS == 1
    #if HAS_DIFFUSE_MAP == 1 || HAS_NORMAL_MAP == 1 || HAS_SPEC_MAP == 1 || HAS_REFLECTION_MAP == 1
        layout(location=0) out highp vec2 texCoord;
    #endif
    #if HAS_NORMAL_MAP == 1
        #if HAS_NORMAL_MAP_OFFSET == 1 || HAS_NORMAL_MAP_SCALE == 1
        layout(location=1) out highp vec2 normal_uv;
        #endif
    #else
      layout(location=2) out mediump vec3 normal;
    #endif
#else
    #if HAS_DIFFUSE_MAP == 1
      layout(location=3) out highp vec2 texCoord;
    #endif
#endif

#if GBUFFER_PASS == 1
    #if HAS_NORMAL_MAP == 1
      layout(location=4) out highp mat3 tangent_to_view_matrix;
    #endif
#else
    #if HAS_LIGHT_MAP == 1
      layout(location=5) out mediump vec2    lightmap_uv;
    #endif

    #if ENABLE_PER_PIXEL == 1
        layout(location=6) out mediump vec3    lightVec;
        layout(location=7) out mediump vec3    halfVec;

        #if HAS_SPEC_MAP_OFFSET == 1 || HAS_SPEC_MAP_SCALE == 1
          layout(location = 8) out highp vec2 spec_uv;
        #endif

        #if HAS_REFLECTION_MAP_OFFSET == 1 || HAS_REFLECTION_MAP_SCALE == 1
          layout(location = 9) out highp vec2 reflection_uv;
        #endif

        #if SHADOW_QUALITY >= 1
          layout(location = 10) out highp vec4	shadowMapCoord1;
        #endif

        #if SHADOW_QUALITY >= 2
          layout(location = 11) out highp vec4	shadowMapCoord2;
        #endif

        #if SHADOW_QUALITY >= 3
          layout(location = 12) out highp vec4	shadowMapCoord3;
        #endif

    #else
      layout(location = 13) out mediump float   lamberFactor;
      layout(location = 14) out mediump float   specularFactor;
    #endif

    #if ENABLE_RIM_COLOR == 1
        #define NEED_EYEVEC
    #endif

    #if HAS_REFLECTION_CUBE_MAP == 1
        #if HAS_NORMAL_MAP == 1
            #define NEED_EYEVEC
          layout(location = 15) out highp mat3 tangent_to_world_matrix;
        #else
          layout(location = 16) out mediump vec3 reflectionVec;
        #endif
    #endif

    #ifdef NEED_EYEVEC
      layout(location = 17) out mediump vec3 eyeVec;
    #endif

    #if HAS_DIFFUSE_MAP_OFFSET == 1 || HAS_DIFFUSE_MAP_SCALE == 1
      layout(location = 18) out highp vec2  diffuse_uv;
    #endif

#endif


void main()
{
#if BONE_COUNT > 0
    mediump vec4 scaled_bone_indexes = bone_indexes;
    mediump vec4 scaled_bone_weights = bone_weights;
    
    //scaled_bone_indexes = vec4(0.0, 0.0, 0.0, 0.0);
    //scaled_bone_weights = vec4(1.0, 0.0, 0.0, 0.0);
    
    highp mat4 skin_matrix =
        bone_transforms[ int(scaled_bone_indexes.x) ] * scaled_bone_weights.x +
        bone_transforms[ int(scaled_bone_indexes.y) ] * scaled_bone_weights.y +
        bone_transforms[ int(scaled_bone_indexes.z) ] * scaled_bone_weights.z +
        bone_transforms[ int(scaled_bone_indexes.w) ] * scaled_bone_weights.w;
    //skin_matrix = bone_transforms[0];
    highp vec3 vertex_position_skinned = (skin_matrix * vec4(vertex_position, 1)).xyz;

    highp vec3 vertex_normal_skinned = normalize(mat3(skin_matrix) * vertex_normal);
    #if HAS_NORMAL_MAP == 1
        highp vec3 vertex_tangent_skinned = normalize(mat3(skin_matrix) * vertex_tangent);
    #endif
    
#endif
    
    // Transform position
    gl_Position = PushConstants.mvp_matrix * vec4(vertex_position_skinned,1.0);


    
    #if HAS_DIFFUSE_MAP == 1 || (HAS_NORMAL_MAP == 1 && ENABLE_PER_PIXEL == 1) || (HAS_SPEC_MAP == 1 && ENABLE_PER_PIXEL == 1) || (HAS_REFLECTION_MAP == 1 && ENABLE_PER_PIXEL == 1)
        // Pass UV co-ordinates
        texCoord = vertex_texcoord0.st;
    #endif
    

    

    // Scaled and translated normal map UV's
    #if (HAS_NORMAL_MAP_OFFSET == 1 || HAS_NORMAL_MAP_SCALE == 1) && ENABLE_PER_PIXEL == 1
        normal_uv = texCoord;
        
        #if HAS_NORMAL_MAP_OFFSET == 1
            normal_uv += normalTexture_Offset;
        #endif
            
        #if HAS_NORMAL_MAP_SCALE == 1
            normal_uv *= normalTexture_Scale;
        #endif
        
    #endif

    #if GBUFFER_PASS != 1 || ALPHA_TEST == 1
        // Scaled and translated diffuse map UV's
        #if HAS_DIFFUSE_MAP_OFFSET == 1 || HAS_DIFFUSE_MAP_SCALE == 1
            diffuse_uv = texCoord;
                
            #if HAS_DIFFUSE_MAP_OFFSET == 1
                diffuse_uv += PushConstants.diffuseTexture_Offset;
            #endif
                
            #if HAS_DIFFUSE_MAP_SCALE == 1
                diffuse_uv *= PushConstants.diffuseTexture_Scale;
            #endif
        #endif
    #endif
    
    
    #if GBUFFER_PASS == 1
        #if HAS_NORMAL_MAP == 1
            mediump vec3 a_bitangent = cross(vertex_normal_skinned, vertex_tangent_skinned);
            tangent_to_view_matrix[0] = vec3(model_view_inverse_transpose_matrix * vec4(vertex_tangent_skinned, 1.0));
            tangent_to_view_matrix[1] = vec3(model_view_inverse_transpose_matrix * vec4(a_bitangent, 1.0));
            tangent_to_view_matrix[2] = vec3(model_view_inverse_transpose_matrix * vec4(vertex_normal_skinned, 1.0));
        #else
            normal = vertex_normal_skinned;
        #endif
    #else

        #if HAS_REFLECTION_CUBE_MAP == 1
            #if HAS_NORMAL_MAP == 1
    
            #else
                // Calculate reflection vector as I - 2.0 * dot(N, I) * N
                mediump vec3 eyeVec = normalize(PushConstants.camera_position_model_space - vertex_position_skinned);
                mediump vec3 incidenceVec = -eyeVec;
                reflectionVec = mat3(PushConstants.model_matrix) * (incidenceVec - 2.0 * dot(vertex_normal_skinned, incidenceVec) * vertex_normal_skinned);
            #endif
        #endif
    
        #ifdef NEED_EYEVEC
            eyeVec = normalize(PushConstants.camera_position_model_space - vertex_position_skinned);
        #endif
    
        #if HAS_LIGHT_MAP == 1
            // Pass shadow UV co-ordinates
            lightmap_uv = vertex_texcoord1.st;
        #endif
    


        #if ENABLE_PER_PIXEL == 1
            // Scaled and translated specular map UV's
            #if HAS_SPEC_MAP_OFFSET == 1 || HAS_SPEC_MAP_SCALE == 1
                spec_uv = texCoord;
                #if HAS_SPEC_MAP_OFFSET == 1
                    spec_uv += PushConstants.specularTexture_Offset;
                #endif
                    
                #if HAS_SPEC_MAP_SCALE == 1
                    spec_uv *= PushConstants.specularTexture_Scale;
                #endif
            #endif
    
            // Scaled and translated reflection map UV's
            #if HAS_REFLECTION_MAP_OFFSET == 1 || HAS_REFLECTION_MAP_SCALE == 1
                reflection_uv = texCoord;
                #if HAS_REFLECTION_MAP_OFFSET == 1
                    reflection_uv += PushConstants.reflectionTexture_Offset;
                #endif
                    
                #if HAS_REFLECTION_MAP_SCALE == 1
                    reflection_uv *= PushConstants.reflectionTexture_Scale;
                #endif
            #endif
    
    
            #if SHADOW_QUALITY >= 1
                shadowMapCoord1 = PushConstants.shadow_mvp1 * vec4(vertex_position_skinned,1.0);
            #endif
    
            #if SHADOW_QUALITY >= 2
                shadowMapCoord2 = PushConstants.shadow_mvp2 * vec4(vertex_position_skinned,1.0);
            #endif
                
            #if SHADOW_QUALITY >= 3
                shadowMapCoord3 = PushConstants.shadow_mvp3 * vec4(vertex_position_skinned,1.0);
            #endif

            // ----------- Directional Light (Sun) -----------
            #if HAS_NORMAL_MAP == 1
                // ----- Calculate per-pixel lighting in tangent space, for normal mapping ------
                mediump vec3 a_bitangent = cross(vertex_normal_skinned, vertex_tangent_skinned);
                #if HAS_REFLECTION_CUBE_MAP == 0
                    // The cube map reflections also require an eyeVec as a varying attribute when normal mapping, so only re-calculate here when needed
                    mediump vec3 eyeVec = normalize(PushConstants.camera_position_model_space - vertex_position_skinned);
                #else
                    tangent_to_world_matrix[0] = vec3(PushConstants.model_inverse_transpose_matrix * vec4(vertex_tangent_skinned, 1.0));
                    tangent_to_world_matrix[1] = vec3(PushConstants.model_inverse_transpose_matrix * vec4(a_bitangent, 1.0));
                    tangent_to_world_matrix[2] = vec3(PushConstants.model_inverse_transpose_matrix * vec4(vertex_normal_skinned, 1.0));
                #endif
    
                lightVec = normalize(vec3(dot(PushConstants.light_direction_model_space, vertex_tangent_skinned), dot(PushConstants.light_direction_model_space, a_bitangent), dot(PushConstants.light_direction_model_space, vertex_normal_skinned)));
                halfVec = normalize(vec3(dot(eyeVec, vertex_tangent_skinned), dot(eyeVec, a_bitangent), dot(eyeVec, vertex_normal_skinned)));
                halfVec = normalize(halfVec + lightVec); // Normalizing anyways, no need to divide by 2
            #else
                // ------ Calculate per-pixel lighting without normal mapping ------
                normal = vertex_normal_skinned;
                lightVec = PushConstants.light_direction_model_space;
                halfVec = normalize((normalize(PushConstants.camera_position_model_space - vertex_position_skinned) + lightVec)); // Normalizing anyways, no need to divide by 2
            #endif
        #else
    
            // ------ Calculate per-vertex lighting ------
            mediump vec3 halfVec = normalize((normalize(PushConstants.camera_position_model_space - vertex_position_skinned) + PushConstants.light_direction_model_space)); // Normalizing anyways, no need to divide by 2
            lamberFactor = max(0.0,dot(PushConstants.light_direction_model_space, vertex_normal_skinned));
            specularFactor = max(0.0,pow(dot(halfVec,vertex_normal_skinned), 1.0 - PushConstants.material_roughness_factor));
        #endif
    #endif
}
