<?xml version="1.0" encoding="utf-8"?>

<CustomShader version="5">
    <Parameters>
        <Parameter name="colorAlpha"        target="colorAlpha"        type="float4" group="base"         defaultValue = "1.0 1.0 1.0 1.0" minValue = "0.0 0.0 0.0 0.0" maxValue = "1.0 1.0 1.0 1.0"/>
        <Parameter name="playScale"         target="playScale"         type="float4" group="subuv"        defaultValue = "30 60 1 0"/>
        <Parameter name="fillTypeId"        target="fillTypeId"        type="float"  group="fillType"     defaultValue = "0"/>
        <Parameter name="alphaErosionScale" target="alphaErosionScale" type="float"  group="alphaErosion" defaultValue = "1" minValue = "0.0" maxValue = "1.0"/>
    </Parameters>
    <UvUsages/>
    <Textures>
        <Texture name = "fillTypeColorMap" group="fillType" defaultColorProfile = "sRGB"      type="2dArray" />
        <Texture name = "lightMap"         group="lightmap" defaultColorProfile = "linearRGB" />
        <Texture name = "alphaMap"         group="alphaMap" defaultColorProfile = "linearRGB" defaultFilename = "$data/shared/default_alpha.png"/>
    </Textures>
    <VertexAttributes>
        <VertexAttribute name="uv1" group="uv1"/>
    </VertexAttributes>
    <Variations>
        <Variation name = "lightmap" groups="base lightmap">
<![CDATA[
    /*
    LIGHTMAP - fake light made out of pre-rendered shaded parts of the particle 
               requeres extra texture == lightMap
               lightMap.rg - right/left
               lightMap.ba - top/bottom
               baseMap.r   - albedo
               baseMap.gb  - front/back
               baseMap.a   - alpha
    */
    #define LIGHTMAP
]]>
        </Variation>
        <Variation name = "subUV" groups="base subuv">
<![CDATA[
    /*
    SUBUV_MOVIE - plays atlas animation with speed set in range [ playScale.x, playScale.y ],
                  random selected start position,
                  plays over whole atlas.
    */
    #define SUBUV_MOVIE
]]>
        </Variation>
        <Variation name = "subUV_alphaErosion" groups="base subuv alphaErosion">
<![CDATA[
    #define SUBUV_MOVIE
    #define ALPHA_EROSION
]]>
        </Variation>
        <Variation name = "subUV_velocityAligned" groups="base subuv">
<![CDATA[
    #define SUBUV_MOVIE
    #define VELOCITY_ALIGNED
]]>
        </Variation>
        <Variation name = "subUV_velocityAligned_alphaErosion" groups="base subuv alphaErosion">
<![CDATA[
    #define SUBUV_MOVIE
    #define VELOCITY_ALIGNED
    #define ALPHA_EROSION
]]>
        </Variation>
        <Variation name = "subUV_lightmap" groups="base subuv lightmap">
<![CDATA[
    #define SUBUV_MOVIE
    #define LIGHTMAP
]]>
        </Variation>
        <Variation name = "subUV_fillTypeArray" groups="base subuv fillType">
<![CDATA[
    #define SUBUV_MOVIE
    #define FILLTYPE_ARRAY
]]>
        </Variation>
        <Variation name = "subUVHorizontal" groups="base subuv">
<![CDATA[
    /*
    SUBUV_MOVIE_HORIZONTAL - plays atlas animation with speed set in range [ playScale.x, playScale.y ],
                             random selected start position,
                             plays over random selected horizontal line.
    */
    #define SUBUV_MOVIE_HORIZONTAL
]]>
        </Variation>
        <Variation name = "subUVByLife" groups="base subuv">
<![CDATA[
    /*
    SUBUV_BY_LIFE - plays atlas animation during lifetime of the particle,
                    start position at top left corner, end position at bottom right corner.
                    plays over whole atlas.
    */
    #define SUBUV_BY_LIFE
    #define SUBUV_MOVIE
]]>
        </Variation>
        <Variation name = "subUVByLifeVelocityAligned" groups="base subuv">
<![CDATA[
    #define SUBUV_BY_LIFE
    #define SUBUV_MOVIE
    #define VELOCITY_ALIGNED
]]>
        </Variation>
        <Variation name = "subUVHorizontalByLife" groups="base subuv">
<![CDATA[
    #define SUBUV_BY_LIFE
    #define SUBUV_MOVIE_HORIZONTAL
]]>
        </Variation>
        <Variation name = "subUVHorizontalByLife_alphaErosion" groups="base subuv alphaErosion">
<![CDATA[
    #define SUBUV_BY_LIFE
    #define SUBUV_MOVIE_HORIZONTAL
    #define ALPHA_EROSION
]]>
        </Variation>
        <Variation name = "subUVHorizontalByLife_alphaMap" groups="base subuv alphaMap">
<![CDATA[
    #define SUBUV_BY_LIFE
    #define SUBUV_MOVIE_HORIZONTAL
    #define ALPHA_MAP
]]>
        </Variation>
        <Variation name = "subUVHorizontalByLife_alphaMap_alphaErosion" groups="base subuv alphaMap alphaErosion">
<![CDATA[
    #define SUBUV_BY_LIFE
    #define SUBUV_MOVIE_HORIZONTAL
    #define ALPHA_EROSION
    #define ALPHA_MAP
]]>
        </Variation>
        <Variation name = "subUVHorizontalByLifeVelocityAligned" groups="base subuv">
<![CDATA[
    #define SUBUV_BY_LIFE
    #define SUBUV_MOVIE_HORIZONTAL
    #define VELOCITY_ALIGNED
]]>
        </Variation>
        <Variation name = "subUVHorizontalByLifeVelocityAligned_alphaErosion" groups="base subuv alphaErosion">
<![CDATA[
    #define SUBUV_BY_LIFE
    #define SUBUV_MOVIE_HORIZONTAL
    #define VELOCITY_ALIGNED
    #define ALPHA_EROSION
]]>
        </Variation>
    </Variations>
    <LodLevel startDistance="0">
        <CodeInjections>
            <CodeInjection position = "CONFIG_DEFINES">
<![CDATA[
/*
    This shader meant to be used with particle systems only
*/
#define PARTICLE_SYSTEM
#if defined( VELOCITY_ALIGNED )
    #define PARTICLE_SYSTEM_VELOCITY_ALIGNED
#endif
#if defined( DISABLE_BILLBOARD )
    #undef PARTICLE_SYSTEM_BILLBOARD
#endif
#if defined(ALPHA_MAP)
    #if !defined(ALPHA_BLENDED)
        #define ALPHA_TESTED
    #endif
#endif
]]>
            </CodeInjection>
            <CodeInjection position="OBJECT_PARAMETERS">
<![CDATA[
#if defined( SUBUV_MOVIE_HORIZONTAL ) || defined( SUBUV_MOVIE )
    /*
    [ playScale.x, playScale.y ] - animation playSpeed Range
    playScale.z                  - enables UV_FLIPPING
                    1 - use flipping, 0 - without flipping.
    */
    float4 playScale;
#endif
    float4 colorAlpha;
#if defined(FILLTYPE_ARRAY)
    float  fillTypeId;
#endif
#if defined(ALPHA_EROSION)
    float  alphaErosionScale;
#endif

]]>
            </CodeInjection>
            <CodeInjection position="LIB_FUNCTION_FS">
<![CDATA[
float getDepthFade(FS_INPUT In, FS_GLOBALS globals, ObjectParameters& object, float fadeDistance)
{
    float screenDepth = In.vs.screenPosZ / In.vs.screenPosW;
    float screenDepthLinear = convertDepthToEyeZ(screenDepth);
    float sceneDepthLinear = getLinearSceneDepth(In, globals,object);
    return saturate((sceneDepthLinear - screenDepthLinear)/fadeDistance);
}
void doAlphaMaskCustom(FS_INPUT In, ObjectParameters& object, float alphaValue)
{
    int2 pixelPos = int2(floor(viewportSize.xy * In.vs.screenPos.xy/In.vs.screenPosW)) & 1;
    int N = (pixelPos.x + 2 * pixelPos.y + 1) & 3;  // 1 2
                                                    // 3 0
    if (round(4*(1-alphaValue)) > (float)N) {
        clip(-1);
    }
}
]]>
            </CodeInjection>
            <CodeInjection position = "FS_GLOBALS">
<![CDATA[
#if defined(LIGHTMAP)
    float4   gLightMap1;
    float4   gLightMap2;
    float3x3 gWorldToTangentSpaceMat;
    float    gAlpha;
#endif
]]>
            </CodeInjection>
            <CodeInjection position = "SAMPLERS">
<![CDATA[
#if defined(FILLTYPE_ARRAY)
    sampler2DArray fillTypeColorMap;
#endif
#if defined(LIGHTMAP)
    sampler2D lightMap;
#endif
#if defined(ALPHA_MAP)
    sampler2D alphaMap;
#endif
]]>
            </CodeInjection>
            <CodeInjection position="VS_OUTPUT">
<![CDATA[
#if defined(FILLTYPE_ARRAY)
    float2 fillTypeTexCoords : TEXCOORDn;
#endif
    float perParticleBlend : TEXCOORDn;
]]>
            </CodeInjection>
            <CodeInjection position="VS_END">
<![CDATA[
#if defined(FILLTYPE_ARRAY)
    Out.fillTypeTexCoords = convertDefaultTexCoords( In, object, In.texCoords0.xy );
#endif
    Out.perParticleBlend = getParticleBlend(In, object);
]]>
            </CodeInjection>
            <CodeInjection position="GET_DEFAULT_TEXCOORDS_VS">
<![CDATA[
#if defined( SUBUV_MOVIE_HORIZONTAL ) || defined( SUBUV_MOVIE )
    // In.color.w;   blendFactor="1" blendInFactor="0" blendOutFactor="0" => 0 .. 1
    float  mAlphaOverLife   = In.texCoords1.x;  // iterated value by lifetime of the particle from 0 .. 1
    float  mRandomValue     = In.texCoords1.y;  // random per particle value from 0 .. 1
    int  mXsize           = (int)(1 / object.particleInvTextureAtlasSize.x + 0.5);
    int  mYsize           = (int)(1 / object.particleInvTextureAtlasSize.y + 0.5);
    int  mXYsize          = mXsize * mYsize;
    
    float  mTime          = cTime_s * lerp( object.playScale.x, object.playScale.y, mRandomValue );
    #if defined( SUBUV_BY_LIFE )
        mTime = mAlphaOverLife * (float)mXYsize;
    #endif
    float2 mIncomingUV      = convertDefaultTexCoords( In, object, In.texCoords0.xy );
    float2 mOutgoingUV      = mIncomingUV;

    // UV_FLIPPING
    float mUVflipping       = object.playScale.z; 
#if defined(LIGHTMAP)
    mUVflipping = 0; // todo: uv flip support for LIGHTMAP
#endif
    float mRandomSwitch1    = mUVflipping * ceil( mRandomValue - 0.5 );  //  0 or 1
    float mRandomSwitch2    = 2 * mRandomSwitch1 - 1;                    // -1 or 1
    mIncomingUV.x = mRandomSwitch1 - mRandomSwitch2*mIncomingUV.x;
    //  end
    #if defined( SUBUV_MOVIE_HORIZONTAL )
        #if defined( SUBUV_BY_LIFE )
            float mSUBuvHorTime = mAlphaOverLife * (float)mXsize;
        #else
            float mSUBuvHorTime = mTime + mRandomValue * (float)mXsize;
        #endif
        int mFrame        = (int)mSUBuvHorTime % mXsize;
        int mYpos         = (int)((float)mYsize*mRandomValue); // converted to float and than to int 
        int2 mSlotPos     = int2(mFrame, mYpos);
        mOutgoingUV       = ((float2)mSlotPos + mIncomingUV ) * object.particleInvTextureAtlasSize.xy;
    #endif
    #if defined( SUBUV_MOVIE )
        float mSUBuvTime    = mTime + mRandomValue * (float)mXYsize;
        int mFrame        = (int)mSUBuvTime % mXYsize;
        #if defined( SUBUV_BY_LIFE )
            mSUBuvTime      = mTime;
            mFrame = (int)mSUBuvTime;
        #endif
        int mYpos         = mFrame / mXsize;
        int2 mSlotPos     = int2( mFrame - mYpos * mXsize, mYsize - mYpos - 1 );
        mOutgoingUV         = ((float2)mSlotPos + mIncomingUV ) * object.particleInvTextureAtlasSize.xy;
    #endif
    return mOutgoingUV;
#endif
]]>
            </CodeInjection>
            <CodeInjection position = "SHARED_VARIABLES_FS">
<![CDATA[
#if defined(LIGHTMAP)
/*
    lightMap.rg - right/left
    lightMap.ba - top/bottom
    baseMap.r   - emissive/albedo
    baseMap.gb  - front/back
    baseMap.a   - alpha
*/
    globals.gLightMap1  = tex2D(lightMap, In.vs.defaultTexCoords).rgba;
    globals.gLightMap2  = tex2D(baseMap,  In.vs.defaultTexCoords).rgba;
    globals.gWorldToTangentSpaceMat = worldToTangentSpaceMat;
    // alpha erosion 
    // In.vs.perParticleBlend - per particle alpha value 
    globals.gAlpha = object.colorAlpha.a * saturate(globals.gLightMap2.a - 1 + In.vs.perParticleBlend);
#endif
]]>
            </CodeInjection>
            <CodeInjection position="POST_DIFFUSE_COLOR_FS">
<![CDATA[

#if defined(FILLTYPE_ARRAY)
    float3 blurredDiffuse = float3(1.0,1.0,1.0);
    if ( (1.0 > object.colorAlpha.r) || (1.0 > object.colorAlpha.g) || (1.0 > object.colorAlpha.b) ){
        blurredDiffuse = object.colorAlpha.rgb;
    } else{
        float  numMips = (float)tex2DGetNumMipLevels(fillTypeColorMap);
        blurredDiffuse = tex2Dlod( fillTypeColorMap, float3(In.vs.fillTypeTexCoords,object.fillTypeId), numMips-1 ).rgba;
    }
    diffuseColor *= blurredDiffuse;
#elif defined(LIGHTMAP)
    diffuseColor = globals.gLightMap2.r*object.colorAlpha.rgb;
#else
    diffuseColor *= object.colorAlpha.rgb;
#endif
]]>
            </CodeInjection>
            <CodeInjection position="ALPHA_FS">
<![CDATA[

#if defined(ALPHA_BLENDED) || defined(ALPHA_TESTED)
    // Read alpha from the texture 
    #if defined(ALPHA_MAP)
        alpha = tex2D(alphaMap, float3(ALBEDO_MAP_TEXCOORDS, ALBEDO_MAP_LAYER)).r;
    #elif defined(ALBEDO_MAP)
        alpha = tex2D(baseMap, float3(ALBEDO_MAP_TEXCOORDS, ALBEDO_MAP_LAYER)).a;
    #elif defined(EMISSIVE_MAP)
        alpha = tex2D(emissiveMap, float3(EMISSIVE_MAP_TEXCOORDS, EMISSIVE_MAP_LAYER)).a;
    #endif
    // modify alpha over life of the particles 
    #if defined(ALPHA_EROSION)
        // In.vs.perParticleBlend - per particle alpha value 
        // object.alphaErosionScale - alpha erosion intensity
        float mAlpha = object.alphaErosionScale * (1 - In.vs.perParticleBlend);
        alpha = saturate(alpha - mAlpha);
        //clip(alpha - 0.5);
    #else
        alpha *= In.vs.perParticleBlend;
    #endif
    alpha *= object.colorAlpha.a;
    #if defined(ALPHA_BLENDED)
        reflectingLightingScale = alpha;
    #endif
    // use custom calculated alpha
    #if defined(LIGHTMAP)
        alpha = globals.gAlpha;
    #endif
#endif
#if defined(PARTICLE_SYSTEM_BILLBOARD) && defined(ALPHA_BLENDED)
    #if GPU_PROFILE >= GPU_PROFILE_HIGH
        alpha *= getDepthFade(In, globals, object, 0.05);
    #endif
#endif
]]>
            </CodeInjection>
            <CodeInjection position="POST_GET_LIGHTING_FS">
<![CDATA[
#if defined(LIGHTMAP)
{
    float3 tangentSpaceLightDir = normalize(mul(globals.gWorldToTangentSpaceMat, lightDir.xyz)).xyz;
    float horizontal = (tangentSpaceLightDir.x > 0.0) ? (globals.gLightMap1.r) : (globals.gLightMap1.g);
    float vertical   = (tangentSpaceLightDir.y > 0.0) ? (globals.gLightMap1.b) : (globals.gLightMap1.a);
    float frontal    = (tangentSpaceLightDir.z > 0.0) ? (globals.gLightMap2.g) : (globals.gLightMap2.b);
    float customShading = horizontal*tangentSpaceLightDir.x*tangentSpaceLightDir.x + 
                            vertical*tangentSpaceLightDir.y*tangentSpaceLightDir.y + 
                             frontal*tangentSpaceLightDir.z*tangentSpaceLightDir.z;
    lighting = (atten) * customShading * lightColor * diffuseColor * globals.gAlpha;
}
#else
    float NdotLNegative = dot(normal, lightDir);
    if (NdotLNegative < 0) {
        NdotLNegative = -NdotLNegative*0.25;
    }
    NdotLNegative = min(NdotLNegative, 1);
    NdotL = saturate(dot(normal, lightDir));
    lighting = ((atten*NdotLNegative) * diffuseShading + (atten*NdotL)* specularShading )* lightColor;
#endif
]]>
            </CodeInjection>
        </CodeInjections>
    </LodLevel>
</CustomShader>
