#ifdef COMPILEVS
attribute vec4 iPos;
attribute vec3 iNormal;
attribute vec4 iColor;
attribute vec2 iTexCoord;
attribute vec2 iTexCoord2;
attribute vec4 iTangent;
attribute vec4 iBlendWeights;
attribute vec4 iBlendIndices;
attribute vec3 iCubeTexCoord;
attribute vec4 iCubeTexCoord2;
#ifndef GL_ES
    attribute vec4 iInstanceMatrix1;
    attribute vec4 iInstanceMatrix2;
    attribute vec4 iInstanceMatrix3;
#endif

#ifdef SKINNED
mat4 GetSkinMatrix(vec4 blendWeights, vec4 blendIndices)
{
    ivec4 idx = ivec4(blendIndices) * 3;
    const vec4 lastColumn = vec4(0.0, 0.0, 0.0, 1.0);
    return mat4(cSkinMatrices[idx.x], cSkinMatrices[idx.x + 1], cSkinMatrices[idx.x + 2], lastColumn) * blendWeights.x +
        mat4(cSkinMatrices[idx.y], cSkinMatrices[idx.y + 1], cSkinMatrices[idx.y + 2], lastColumn) * blendWeights.y +
        mat4(cSkinMatrices[idx.z], cSkinMatrices[idx.z + 1], cSkinMatrices[idx.z + 2], lastColumn) * blendWeights.z +
        mat4(cSkinMatrices[idx.w], cSkinMatrices[idx.w + 1], cSkinMatrices[idx.w + 2], lastColumn) * blendWeights.w;
}
#endif

#ifdef INSTANCED
mat4 GetInstanceMatrix()
{
    const vec4 lastColumn = vec4(0.0, 0.0, 0.0, 1.0);
    return mat4(iInstanceMatrix1, iInstanceMatrix2, iInstanceMatrix3, lastColumn);
}
#endif

mat3 GetNormalMatrix(mat4 modelMatrix)
{
    return mat3(modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz);
}

vec2 GetTexCoord(vec2 texCoord)
{
    return vec2(dot(texCoord, cUOffset.xy) + cUOffset.w, dot(texCoord, cVOffset.xy) + cVOffset.w);
}

vec4 GetClipPos(vec3 worldPos)
{
    vec4 ret = cViewProj * vec4(worldPos, 1.0);
    // While getting the clip coordinate, also automatically set gl_ClipVertex for user clip planes
    #ifndef GL_ES
    gl_ClipVertex = ret;
    #endif
    return ret;
}

float GetZonePos(vec3 worldPos)
{
    return clamp((cZone * vec4(worldPos, 1.0)).z, 0.0, 1.0);
}

float GetDepth(vec4 clipPos)
{
    return dot(clipPos.zw, cDepthMode.zw);
}

vec3 GetBillboardPos(vec4 iPos, vec2 iSize, mat4 modelMatrix)
{
    return (modelMatrix * iPos).xyz + cBillboardRot * vec3(iSize.x, iSize.y, 0.0);
}

vec3 GetBillboardNormal()
{
    return vec3(-cBillboardRot[2][0], -cBillboardRot[2][1], -cBillboardRot[2][2]);
}

// Note: the skinning/instancing model matrix is a transpose, so the matrix multiply order must be swapped
// (see GetWorldPos(), GetWorldNormal() and GetWorldTangent() below)
#if defined(SKINNED)
    #define iModelMatrix GetSkinMatrix(iBlendWeights, iBlendIndices)
#elif defined(INSTANCED)
    #define iModelMatrix GetInstanceMatrix();
#else
    #define iModelMatrix cModel
#endif

vec3 GetWorldPos(mat4 modelMatrix)
{
    #if defined(SKINNED) || defined(INSTANCED)
        return (iPos * modelMatrix).xyz;
    #elif defined(BILLBOARD)
        return GetBillboardPos(iPos, iTexCoord2, modelMatrix);
    #else
        return (modelMatrix * iPos).xyz;
    #endif
}

vec3 GetWorldNormal(mat4 modelMatrix)
{
    #if defined(SKINNED) || defined(INSTANCED)
        return normalize(iNormal * GetNormalMatrix(modelMatrix));
    #elif defined(BILLBOARD)
        return GetBillboardNormal();
    #else
        return normalize(GetNormalMatrix(modelMatrix) * iNormal);
    #endif
}

vec3 GetWorldTangent(mat4 modelMatrix)
{   
    mat3 normalMatrix = GetNormalMatrix(modelMatrix);
    #if defined(SKINNED) || defined(INSTANCED)
        return normalize(iTangent.xyz * normalMatrix);
    #else
        return normalize(normalMatrix * iTangent.xyz);
    #endif
}
#endif
