////////////////////////////////////////////////
//
// model shaders
//
////////////////////////////////////////////////

// skeletal animation with dual quaternions

qtangentdecode = [
    ? $arg1 [
        vec4 qxyz = mquat.xxyy*mquat.yzyz, qxzw = vec4(mquat.xzw, -mquat.w);
        vec3 mtangent = (qxzw.yzw*mquat.zzy + qxyz.zxy)*vec3(-2.0, 2.0, 2.0) + vec3(1.0, 0.0, 0.0);
        vec3 mnormal = (qxzw.zwx*mquat.yxx + qxyz.ywz)*vec3(2.0, 2.0, -2.0) + vec3(0.0, 0.0, 1.0);
        // vec3 mtangent = cross(mquat.xyz, vec3(mquat.wz, -mquat.y))*2.0 + vec3(1.0, 0.0, 0.0);
        // vec3 mnormal = cross(mquat.xyz, vec3(mquat.y, -mquat.x, mquat.w))*2.0 + vec3(0.0, 0.0, 1.0);
    ] [
        vec3 mnormal = cross(mquat.xyz, vec3(mquat.y, -mquat.x, mquat.w))*2.0 + vec3(0.0, 0.0, 1.0);
    ]
]

skelanimdefs = [
    result [
        in vec4 vboneweight, vboneindex; 
        //:uniform animdata
        uniform vec4 animdata[@@(min $maxvsuniforms $maxskelanimdata)];
    ]
]

skelanim = [
    result [
        int index = int(vboneindex.x);
        @(if (= $arg1 1) [result [
            vec4 dqreal = animdata[index];
            vec4 dqdual = animdata[index+1];
        ]] [result [
            vec4 dqreal = animdata[index] * vboneweight.x;
            vec4 dqdual = animdata[index+1] * vboneweight.x;
            index = int(vboneindex.y);
            dqreal += animdata[index] * vboneweight.y;
            dqdual += animdata[index+1] * vboneweight.y;
            @(if (>= $arg1 3) [result [
                index = int(vboneindex.z);
                dqreal += animdata[index] * vboneweight.z;
                dqdual += animdata[index+1] * vboneweight.z;
            ]])
            @(if (>= $arg1 4) [result [
                index = int(vboneindex.w);
                dqreal += animdata[index] * vboneweight.w;
                dqdual += animdata[index+1] * vboneweight.w;
            ]])
            float len = length(dqreal);
            dqreal /= len;
            dqdual /= len;
        ]])

        vec4 mpos = vec4((cross(dqreal.xyz, cross(dqreal.xyz, vvertex.xyz) + vvertex.xyz*dqreal.w + dqdual.xyz) + dqdual.xyz*dqreal.w - dqreal.xyz*dqdual.w)*2.0 + vvertex.xyz, vvertex.w);

        @(if (>= $numargs 2) [result [
            vec4 mquat = vec4(cross(dqreal.xyz, vtangent.xyz) + dqreal.xyz*vtangent.w + vtangent.xyz*dqreal.w, dqreal.w*vtangent.w - dot(dqreal.xyz, vtangent.xyz));
            @(qtangentdecode $arg2)
        ]])
    ]
]

// mdltype:
//    a -> alpha test
//    b -> dual-quat skeletal animation

mdlopt = [ >= (strstr $modeltype $arg1) 0 ]

shadowmodelvertexshader = [
    local modeltype
    modeltype = $arg1
    result [
        in vec4 vvertex;
        @(if (mdlopt "b") [skelanimdefs $arg2])
        uniform mat4 modelmatrix;
        @(? (mdlopt "a") [
            in vec2 vtexcoord0;
            uniform vec2 texscroll;
            out vec2 texcoord0;
        ])
        void main(void)
        {
            @(if (mdlopt "b") [
                skelanim $arg2
            ] [result [
                #define mpos vvertex
            ]])

            gl_Position = modelmatrix * mpos;

            @(? (mdlopt "a") [
                texcoord0 = vtexcoord0 + texscroll;
            ])
        }
    ]
]

shadowmodelfragmentshader = [
    local modeltype
    modeltype = $arg1
    result [
        @(? (mdlopt "a") [
            uniform sampler2D tex0;
            uniform float alphatest;
            in vec2 texcoord0;
        ])
        void main(void)
        {
            @(? (mdlopt "a") [
                vec4 color = texture2D(tex0, texcoord0);
                if(color.a <= alphatest)    
                    discard;
            ])
        }
    ]
]

shadowmodelshader = [
    defershader 0 $arg1 [
        shader 0 @arg1 (shadowmodelvertexshader @arg2) (shadowmodelfragmentshader @arg2)
        loop+ i 1 4 [
            variantshader 0 @@arg1 0 (shadowmodelvertexshader @@(concatword $arg2 "b") $i) []
        ]
    ]
]

shadowmodelshader "shadowmodel" ""
shadowmodelshader "alphashadowmodel" "a"

// mdltype:
//    a -> alpha test
//    e -> envmap
//    n -> normalmap
//    m -> masks
//    d -> decal
//    D -> alpha decal
//    b -> dual-quat skeletal animation
//    c -> disable cullface
//    t -> transparent 

modelvertexshader = [
    local modeltype
    modeltype = $arg1
    result [
        in vec4 vvertex, vtangent;
        in vec2 vtexcoord0;
        @(if (mdlopt "b") [skelanimdefs $arg2 (mdlopt "n")])
        uniform mat4 modelmatrix;
        uniform mat3 modelworld;
        uniform vec3 modelcamera;
        uniform vec2 texscroll;
        @(? (mdlopt "n") [
            out mat3 world;
        ] [
            out vec3 nvec;
        ])
        @(? (mdlopt "e") [
            out vec3 camvec;
        ])
        @(msaainterpvert)
        out vec2 texcoord0;

        void main(void)
        {
            @(if (mdlopt "b") [
                skelanim $arg2 (mdlopt "n")
            ] [result [
                #define mpos vvertex
                #define mquat vtangent
                @(qtangentdecode (mdlopt "n"))
            ]])

            gl_Position = modelmatrix * mpos;

            texcoord0 = vtexcoord0 + texscroll;

            @(msaapackvert)

            @(? (mdlopt "e") [
                camvec = modelworld * normalize(modelcamera - mpos.xyz);
            ])

            @(? (mdlopt "n") [
                // composition of tangent -> object and object -> world transforms
                //   becomes tangent -> world
                vec3 wnormal = modelworld * mnormal;
                vec3 wtangent = modelworld * mtangent;
                vec3 wbitangent = cross(wnormal, wtangent) * (vtangent.w < 0.0 ? -1.0 : 1.0);
                world = mat3(wtangent, wbitangent, wnormal);
            ] [
                nvec = modelworld * mnormal;
            ])
        }
    ]
]

modelfragmentshader = [
    local modeltype
    modeltype = $arg1
    result [
        @(? (mdlopt "n") [
            in mat3 world; 
        ] [
            in vec3 nvec;
        ])
        @(? (mdlopt "e") [
            uniform vec2 envmapscale;
            in vec3 camvec;
        ])
        uniform vec4 colorscale;
        uniform vec2 fullbright;
        uniform vec3 maskscale;
        @(? (mdlopt "a") [uniform float alphatest;])
        uniform sampler2D tex0;
        @(? (mdlopt "m") [uniform sampler2D tex1;])
        @(? (mdlopt "e") [uniform samplerCube tex2;])
        @(? (mdlopt "n") [uniform sampler2D tex3;])
        @(? (|| (mdlopt "d") [mdlopt "D"]) [uniform sampler2D tex4;])
        @(msaainterpfrag)
        in vec2 texcoord0;
        uniform float aamask;

        void main(void)
        {
            vec4 diffuse = texture2D(tex0, texcoord0);

            @(? (mdlopt "a") [
                if(diffuse.a <= alphatest)
                    discard;
            ])

            gcolor.rgb = diffuse.rgb*colorscale.rgb;

            @(? (|| (mdlopt "d") [mdlopt "D"]) [
                vec4 decal = texture2D(tex4, texcoord0);
                @(? (mdlopt "D") [
                    gcolor.rgb = mix(gcolor.rgb, decal.rgb, decal.a);
                ] [
                    gcolor.rgb += decal.rgb;
                ])
            ])

            @(if (mdlopt "n") [result [
                vec3 normal = texture2D(tex3, texcoord0).rgb - 0.5;
                @(? (mdlopt "c") [
                    if(!gl_FrontFacing) normal.z = -normal.z;
                ])
                normal = normalize(world * normal);
            ]] [result [
                vec3 normal = normalize(nvec);
                @(? (mdlopt "c") [
                    if(!gl_FrontFacing) normal = -normal;
                ])
            ]])

            float spec = maskscale.x;
            @(if (mdlopt "m") [result [
                vec3 masks = texture2D(tex1, texcoord0).rgb;
                spec *= masks.r; // specmap in red channel

                @(? (mdlopt "e") [
                    vec3 camn = normalize(camvec);
                    float invfresnel = dot(camn, normal);
                    vec3 rvec = 2.0*invfresnel*normal - camn;
                    float rmod = envmapscale.x*clamp(invfresnel, 0.0, 1.0) + envmapscale.y;
                    vec3 reflect = textureCube(tex2, rvec).rgb;
                    gcolor.rgb = mix(gcolor.rgb, reflect, rmod*masks.b); // envmap mask in blue channel
                ])

                float glowk = max(maskscale.z*masks.g, fullbright.y); // glow mask in green channel
            ]] [result [
                float glowk = fullbright.y;
            ]])
            @(gspecpack maskscale.y spec)

            float colork = clamp(fullbright.x - glowk, 0.0, 1.0);

            @(if (mdlopt "t") [result [
                gcolor *= colorscale.a;
                gglow.rgb = gcolor.rgb*glowk;
                gcolor.rgb *= colork;
                #define packnorm colorscale.a
            ]] [gglowpack "" packnorm])

            @(gnormpackdef normal packnorm)

            @(msaapackfrag aamask)
        }
    ]
]

modelshader = [
    shadername = (concatword "model" $arg1)
    maxvariants = 9
    shader 0 $shadername (modelvertexshader $arg1) (modelfragmentshader $arg1)
    loop+ i 1 4 [
        variantshader 0 $shadername 0 (modelvertexshader (concatword $arg1 "b") $i) [] $maxvariants
    ]
    variantshader 0 $shadername 1 [] (modelfragmentshader (concatword $arg1 "t")) $maxvariants
    loop i 4 [
        variantshader 0 $shadername 1 [0 , @i] 1 $maxvariants
    ]
]

rsmmodelvertexshader = [
    local modeltype
    modeltype = $arg1
    result [
        in vec4 vvertex, vtangent;
        in vec2 vtexcoord0;
        @(if (mdlopt "b") [skelanimdefs $arg2 (mdlopt "n")])
        uniform mat4 modelmatrix;
        uniform mat3 modelworld;
        uniform vec2 texscroll;
        out vec2 texcoord0;
        out vec3 nvec;
        void main(void)
        {
            @(if (mdlopt "b") [
                skelanim $arg2 (mdlopt "n")
            ] [result [
                #define mpos vvertex
                #define mquat vtangent
                @(qtangentdecode (mdlopt "n"))
            ]])

            gl_Position = modelmatrix * mpos;

            texcoord0 = vtexcoord0 + texscroll;

            nvec = modelworld * mnormal;
        }
    ]
]

rsmmodelfragmentshader = [
    local modeltype
    modeltype = $arg1
    result [
        in vec2 texcoord0;
        in vec3 nvec;
        uniform vec4 colorscale;
        @(? (mdlopt "a") [uniform float alphatest;])
        uniform vec3 rsmdir;
        uniform sampler2D tex0;
        layout(location = 0) out vec4 gcolor;
        layout(location = 1) out vec4 gnormal;
        void main(void)
        {
            vec4 diffuse = texture2D(tex0, texcoord0);
            @(? (mdlopt "a") [
                if(diffuse.a <= alphatest)
                    discard;
            ])
            vec3 normal = normalize(nvec);
            @(? (mdlopt "c") [
                if(!gl_FrontFacing) normal = -normal;
            ])
            gcolor = vec4(dot(normal, rsmdir)*diffuse.rgb*colorscale.rgb, 1.0);
            gnormal = vec4(normal*0.5+0.5, 0.0);
        }
    ]
]

rsmmodelshader = [
    shadername = (concatword "rsmmodel" $arg1)
    shader 0 $shadername (rsmmodelvertexshader $arg1) (rsmmodelfragmentshader $arg1)
    loop+ i 1 4 [
        variantshader 0 $shadername 0 (rsmmodelvertexshader (concatword $arg1 "b") $i) []
    ]
]

