//
//  Shader.vsh
//  KREngine
//
//  Copyright 2012 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.
//


attribute highp vec3	vertex_position, vertex_normal;
#if HAS_NORMAL_MAP == 1
    attribute highp vec3    vertex_tangent;
#endif
attribute mediump vec2	vertex_uv;
uniform highp mat4      mvp_matrix; // mvp_matrix is the result of multiplying the model, view, and projection matrices

#if BONE_COUNT > 0
    attribute highp vec4 bone_weights;
    attribute highp vec4 bone_indexes;
    uniform highp mat4 bone_transforms[BONE_COUNT];
#else
    #define vertex_position_skinned vertex_position
    #define vertex_normal_skinned vertex_normal
    #define vertex_tangent_skinned vertex_tangent
#endif



#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
        varying highp vec2 texCoord;
    #endif
    #if HAS_NORMAL_MAP == 1
        #if HAS_NORMAL_MAP_SCALE == 1
            uniform highp vec2 normalTexture_Scale;
        #endif

        #if HAS_NORMAL_MAP_OFFSET == 1
            uniform highp vec2 normalTexture_Offset;
        #endif

        #if HAS_NORMAL_MAP_OFFSET == 1 || HAS_NORMAL_MAP_SCALE == 1
            varying highp vec2 normal_uv;
        #endif
    #else
        varying mediump vec3 normal;
    #endif
#else
    uniform mediump float material_shininess;
    #if HAS_DIFFUSE_MAP == 1
        varying highp vec2 texCoord;
    #endif
#endif

#if GBUFFER_PASS == 1
    #if HAS_NORMAL_MAP == 1
        uniform highp mat4 model_view_inverse_transpose_matrix;
        varying highp mat3 tangent_to_view_matrix;
    #endif
#else

    uniform highp vec3  light_direction_model_space; // Must be normalized before entering shader
    uniform highp vec3  camera_position_model_space;

    #if HAS_LIGHT_MAP == 1
        attribute mediump vec2  vertex_lightmap_uv;
        varying mediump vec2    lightmap_uv;
    #endif

    #if ENABLE_PER_PIXEL == 1
        varying mediump vec3    lightVec;
        varying mediump vec3    halfVec;

        #if HAS_SPEC_MAP_OFFSET == 1 || HAS_SPEC_MAP_SCALE == 1
            varying highp vec2 spec_uv;
        #endif

        #if HAS_SPEC_MAP_SCALE == 1
            uniform highp vec2    specularTexture_Scale;
        #endif

        #if HAS_SPEC_MAP_OFFSET == 1
            uniform highp vec2    specularTexture_Offset;
        #endif

        #if HAS_REFLECTION_MAP_OFFSET == 1 || HAS_REFLECTION_MAP_SCALE == 1
            varying highp vec2 reflection_uv;
        #endif

        #if HAS_REFLECTION_MAP_SCALE == 1
            uniform highp vec2    reflectionTexture_Scale;
        #endif

        #if HAS_REFLECTION_MAP_OFFSET == 1
            uniform highp vec2    reflectionTexture_Offset;
        #endif

        #if SHADOW_QUALITY >= 1
            uniform highp mat4  shadow_mvp1;
            varying highp vec4	shadowMapCoord1;
        #endif

        #if SHADOW_QUALITY >= 2
            uniform highp mat4  shadow_mvp2;
            varying highp vec4	shadowMapCoord2;
        #endif

        #if SHADOW_QUALITY >= 3
            uniform highp mat4  shadow_mvp3;
            varying highp vec4	shadowMapCoord3;
        #endif

    #else
        varying mediump float   lamberFactor;
        varying 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
            uniform highp mat4 model_inverse_transpose_matrix;
            varying highp mat3 tangent_to_world_matrix;
        #else
            uniform highp mat4 model_matrix;
            varying mediump vec3 reflectionVec;
        #endif
    #endif

    #ifdef NEED_EYEVEC
        varying mediump vec3 eyeVec;
    #endif

    #if HAS_DIFFUSE_MAP_SCALE == 1
        uniform highp vec2  diffuseTexture_Scale;
    #endif

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

    #if HAS_DIFFUSE_MAP_OFFSET == 1 || HAS_DIFFUSE_MAP_SCALE == 1
        varying 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 = 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_uv.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 += diffuseTexture_Offset;
            #endif
                
            #if HAS_DIFFUSE_MAP_SCALE == 1
                diffuse_uv *= 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(camera_position_model_space - vertex_position_skinned);
                mediump vec3 incidenceVec = -eyeVec;
                reflectionVec = mat3(model_matrix) * (incidenceVec - 2.0 * dot(vertex_normal_skinned, incidenceVec) * vertex_normal_skinned);
            #endif
        #endif
    
        #ifdef NEED_EYEVEC
            eyeVec = normalize(camera_position_model_space - vertex_position_skinned);
        #endif
    
        #if HAS_LIGHT_MAP == 1
            // Pass shadow UV co-ordinates
            lightmap_uv = vertex_lightmap_uv.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 += specularTexture_Offset;
                #endif
                    
                #if HAS_SPEC_MAP_SCALE == 1
                    spec_uv *= 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 += reflectionTexture_Offset;
                #endif
                    
                #if HAS_REFLECTION_MAP_SCALE == 1
                    reflection_uv *= reflectionTexture_Scale;
                #endif
            #endif
    
    
            #if SHADOW_QUALITY >= 1
                shadowMapCoord1 = shadow_mvp1 * vec4(vertex_position_skinned,1.0);
            #endif
    
            #if SHADOW_QUALITY >= 2
                shadowMapCoord2 = shadow_mvp2 * vec4(vertex_position_skinned,1.0);
            #endif
                
            #if SHADOW_QUALITY >= 3
                shadowMapCoord3 = 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(camera_position_model_space - vertex_position_skinned);
                #else
                    tangent_to_world_matrix[0] = vec3(model_inverse_transpose_matrix * vec4(vertex_tangent_skinned, 1.0));
                    tangent_to_world_matrix[1] = vec3(model_inverse_transpose_matrix * vec4(a_bitangent, 1.0));
                    tangent_to_world_matrix[2] = vec3(model_inverse_transpose_matrix * vec4(vertex_normal_skinned, 1.0));
                #endif
    
                lightVec = normalize(vec3(dot(light_direction_model_space, vertex_tangent_skinned), dot(light_direction_model_space, a_bitangent), dot(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 = light_direction_model_space;
                halfVec = normalize((normalize(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(camera_position_model_space - vertex_position_skinned) + light_direction_model_space)); // Normalizing anyways, no need to divide by 2
            lamberFactor = max(0.0,dot(light_direction_model_space, vertex_normal_skinned));
            specularFactor = max(0.0,pow(dot(halfVec,vertex_normal_skinned), material_shininess));
        #endif
    #endif
}
