////////////////////////////////////////////////
//
// decals
//
////////////////////////////////////////////////

// decaltype:
//    e -> reserve envmap texture slot
//    r -> envmap reflection
//    R -> modulate envmap reflection with spec map
//    s -> spec
//    S -> spec map
//    n -> normal map
//    p -> parallax
//    g -> glow
//    G -> pulse glow
//    b -> keep normals
//    0 -> pass 0
//    1 -> pass 1

dtopt = [ >= (strstr $decaltype $arg1) 0 ]

decalvariantshader = [
    local decaltype
    decaltype = $arg2
    stype = (? (dtopt "e") 3 1)
    srow = (? (dtopt "1") 0 -1)
    if (dtopt "G") [
        stype = (+ $stype 0x10)
        defuniformparam "glowcolor" 1 1 1 // glow color
        defuniformparam "pulseglowspeed" 1     // pulse frequency (Hz)
        defuniformparam "pulseglowcolor" 0 0 0 // pulse glow color
    ] [if (dtopt "g") [
        defuniformparam "glowcolor" 1 1 1  // glow color
    ]]
    if (dtopt "R") [
        defuniformparam "envscale" 1 1 1 // reflectivity map multiplier
    ] [if (dtopt "r") [
        defuniformparam "envscale" 0.2 0.2 0.2 // reflectivity
    ]]
    reuseuniformparam "gloss" 1 // glossiness
    if (dtopt "S") [
        defuniformparam "specscale" 2 2 2 // spec map multiplier
    ] [if (dtopt "s") [
        defuniformparam "specscale" 1 1 1 // spec multiplier
    ]]
    if (dtopt "p") [
        defuniformparam "parallaxscale" 0.06 -0.03 // parallax scaling
    ]
    if (dtopt "n") [
        defuniformparam "bumpblend" 1 // alpha-blend strength for normals
    ]
    variantshader $stype $arg1 $srow [
        attribute vec4 vvertex;
        @(? (dtopt "n") [
            attribute vec4 vtangent;
            varying mat3 world;
        ] [
            varying vec3 nvec;
        ])
        attribute vec4 vnormal;
        attribute vec3 vtexcoord0;
        uniform mat4 camprojmatrix;
        varying vec4 texcoord0;
        @(? (|| (dtopt "p") (dtopt "r")) [
            uniform vec3 camera;
            varying vec3 camvec;
        ])
        @(? (dtopt "G") [uniform float millis; flat varying float pulse;])
        void main(void)
        {
            gl_Position = camprojmatrix * vvertex;
            texcoord0.xyz = vtexcoord0;
            texcoord0.w = 3.0*vnormal.w;

            @(? (dtopt "n") [
                vec3 bitangent = cross(vnormal.xyz, vtangent.xyz) * vtangent.w;
                // calculate tangent -> world transform
                world = mat3(vtangent.xyz, bitangent, vnormal.xyz);
            ] [
                nvec = vnormal.xyz;
            ])

            @(? (|| (dtopt "p") (dtopt "r")) [
                camvec = camera - vvertex.xyz;
            ])

            @(? (dtopt "G") [
                pulse = abs(fract(millis*pulseglowspeed.x)*2.0 - 1.0);
            ])
        }
    ] [
        @(cond [dtopt "0"] [result [
            fragdata(0) vec4 gcolor;
            fragblend(0) vec4 gcolorblend;
        ]] [dtopt "1"] [
            ? $usepacknorm [
                fragdata(0) vec4 gnormal;
            ] [
                fragdata(0) vec4 gnormal;
                fragblend(0) vec4 gnormalblend;
            ]
        ] [result [
            fragdata(0) vec4 gcolor;
            fragdata(1) vec4 gnormal;
        ]])
        uniform sampler2D diffusemap;
        uniform vec4 colorparams;
        varying vec4 texcoord0;
        @(? (dtopt "n") [
            uniform sampler2D normalmap;
            varying mat3 world;
        ] [
            varying vec3 nvec;
            #define bumpblend vec4(1.0)
        ])
        @(? (|| (dtopt "p") (dtopt "r")) [varying vec3 camvec;])
        @(? (|| (dtopt "g") (dtopt "S")) [uniform sampler2D glowmap;])
        @(? (dtopt "G") [flat varying float pulse;])
        @(? (dtopt "r") [uniform samplerCube envmap;])
        void main(void)
        {
            @(if (dtopt "n") [result [
                @(? (dtopt "p") [
                    float height = texture2D(normalmap, texcoord0.xy).a;
                    vec3 camvecn = normalize(camvec);
                    vec2 dtc = texcoord0.xy + (camvecn * world).xy*(height*parallaxscale.x + parallaxscale.y);
                ] [
                    #define dtc texcoord0.xy
                ])

                @(? (|| (! (dtopt "0")) (dtopt "r")) [
                    vec3 bump = texture2D(normalmap, dtc).rgb*2.0 - 1.0;
                    vec3 bumpw = world * bump;
                    #define nvec bumpw
                ])
            ]] [result [
                #define dtc texcoord0.xy
            ]])

            vec4 diffuse = texture2D(diffusemap, dtc);

            @(if (dtopt "g") [result [
                vec4 glowspec = texture2D(glowmap, dtc);
                #define glow glowspec.rgb
                #define spec glowspec.a
                glow *= @(? (dtopt "G") [mix(glowcolor.xyz, pulseglowcolor.xyz, pulse)] [glowcolor.xyz]);
                glow *= diffuse.a;
            ]])

            @(if (dtopt "0") [result [
                @(if (&& (dtopt "S") [! (dtopt "g")]) [result [
                    float spec = @(? (|| (! (dtopt "n")) (dtopt "p")) [texture2D(glowmap, dtc).r] [texture2D(normalmap, dtc).a]);
                ]])
                @(gspecpack [gloss.x] (if (dtopt "s") [? (dtopt "S") [spec * specscale.x] [specscale.x]]))
            ]])

            @(if (dtopt "1") [
                ? (dtopt "g") [
                   vec3 gcolor = diffuse.rgb*colorparams.rgb;
                ]
            ] [result [
                gcolor.rgb = diffuse.rgb*colorparams.rgb;

                @(if (dtopt "r") [result [
                    @(? (|| (! (dtopt "n")) (! (dtopt "p"))) [
                        vec3 camvecn = normalize(camvec);
                    ])
                    float invfresnel = dot(camvecn, nvec);
                    vec3 rvec = 2.0*nvec*invfresnel - camvecn;
                    vec3 reflect = textureCube(envmap, rvec).rgb * diffuse.a;
                    @(? (dtopt "R") [
                        vec3 rmod = envscale.xyz*spec;
                    ] [
                        #define rmod envscale.xyz
                    ])
                    reflect *= diffuse.a;
                    gcolor.rgb = mix(gcolor.rgb, reflect, rmod*clamp(1.0 - invfresnel, 0.0, 1.0));
                ]])
            ]])

            @(if (dtopt "g") [gglowpack glow packnorm])

            @(if (! (dtopt "0")) [result [
                vec3 normal = normalize(nvec);
                @(gnormpackdef normal packnorm)
            ]])

            float inside = clamp(texcoord0.z, 0.0, 1.0) * clamp(texcoord0.w, 0.0, 1.0);
            float alpha = inside * diffuse.a;
            @(cond [dtopt "0"] [result [
                gcolor.rgb *= inside;
                gcolor.a *= alpha;
                gcolorblend = vec4(alpha);
            ]] [dtopt "1"] [
                ? $usepacknorm [
                    gnormal.a = alpha * bumpblend.x;
                ] [
                    gnormalblend = vec4(alpha * bumpblend.x);
                ]
            ] [result [
                gcolor.rgb *= inside;
                gcolor.a = alpha;
                @(? (dtopt "b") [
                    gnormal = vec4(0.0);
                ] [
                    gnormal.rgb *= alpha * bumpblend.x;
                    gnormal.a = alpha * bumpblend.x;
                ])
            ]])
        }
    ]
]

decalshader = [
    decaltype = $arg2
    stype = (? (dtopt "e") 3 1)
    defershader $stype $arg1 [
        if $maxdualdrawbufs [
            decalvariantshader @@arg1 @@(concatword $arg2 "0")
            if @@(! (dtopt "b")) [
                decalvariantshader @@@arg1 @@@(concatword $arg2 "1")
            ]
        ] [
            decalvariantshader @@arg1 @@arg2
        ]
    ]
]

decalshader "stddecal" "b"
decalshader "specdecal" "bs"
decalshader "specmapdecal" "bsS"

decalshader "glowdecal" "g"
decalshader "pulseglowdecal" "gG"

decalshader "envdecal" "ber"
decalshader "envspecdecal" "besr"
decalshader "envspecmapdecal" "besSrR"
decalshader "envglowdecal" "erg"
decalshader "envpulseglowdecal" "ergG"

decalshader "bumpdecal" "n"
decalshader "bumpspecdecal" "ns"
decalshader "bumpspecmapdecal" "nsS"

decalshader "bumpglowdecal" "ng"
decalshader "bumpspecglowdecal" "nsg"
decalshader "bumpspecmapglowdecal" "nsSg"

decalshader "bumppulseglowdecal" "ngG"
decalshader "bumpspecpulseglowdecal" "nsgG"
decalshader "bumpspecmappulseglowdecal" "nsSgG"

decalshader "bumpparallaxdecal" "np"
decalshader "bumpspecparallaxdecal" "nps"
decalshader "bumpspecmapparallaxdecal" "npsS"

decalshader "bumpparallaxglowdecal" "npg"
decalshader "bumpspecparallaxglowdecal" "npsg"
decalshader "bumpspecmapparallaxglowdecal" "npsSg"

decalshader "bumpparallaxpulseglowdecal" "npgG"
decalshader "bumpspecparallaxpulseglowdecal" "npsgG"
decalshader "bumpspecmapparallaxpulseglowdecal" "npsSgG"

decalshader "bumpenvdecal" "ner"
decalshader "bumpenvspecdecal" "nesr"
decalshader "bumpenvspecmapdecal" "nesSrR"

decalshader "bumpenvglowdecal" "nerg"
decalshader "bumpenvspecglowdecal" "neosrg"
decalshader "bumpenvspecmapglowdecal" "nesSrRg"

decalshader "bumpenvpulseglowdecal" "neorgG"
decalshader "bumpenvspecpulseglowdecal" "nesrgG"
decalshader "bumpenvspecmappulseglowdecal" "nesSrRgG"

decalshader "bumpenvparallaxdecal" "nepr"
decalshader "bumpenvspecparallaxdecal" "nepsr"
decalshader "bumpenvspecmapparallaxdecal" "nepsSrR"

decalshader "bumpenvparallaxglowdecal" "neprg"
decalshader "bumpenvspecparallaxglowdecal" "nepsrg"
decalshader "bumpenvspecmapparallaxglowdecal" "nepsSrRg"

decalshader "bumpenvparallaxpulseglowdecal" "neprgG"
decalshader "bumpenvspecparallaxpulseglowdecal" "nepsrgG"
decalshader "bumpenvspecmapparallaxpulseglowdecal" "nepsSrRgG"

