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

<CustomShader version="5">
    <Parameters>
        <Parameter name = "windSnowLeafScale" target = "windSnowLeafScale" type = "float4" group="base"     defaultValue = "1.0 1.0 1.0 0"   minValue = "0.0 0.0 0.0 0"      maxValue = "1.0 1.0 1.0 10000.0"/>
    </Parameters>
    <UvUsages/>
    <Textures>
        <Texture name="mMaskMap" group="seasonal" defaultColorProfile="linearRGB"/>
    </Textures>
    <Variations>
        <Variation name="SEASONAL" groups="base seasonal">
            <UvUsages>
                <UvUsage textureName="baseMap"  uvType="uv0" uvScale="0.5"/>
            </UvUsages>
<![CDATA[
#define SEASONAL
    /*
    Season states:
        cShared3 = 0 = summer
        cShared3 = 1 = autumn
        cShared3 = 2 = winter
        cShared3 = 3 = spring
        cShared3 = 4 = summer
    */
]]>
        </Variation>
    </Variations>
    <LodLevel startDistance="0">
        <CodeInjections>
            <CodeInjection position="CONFIG_DEFINES">
<![CDATA[
#if defined(SEASONAL)
    #define ALPHA_TESTED
#endif
]]>
            </CodeInjection>
            <CodeInjection position="SAMPLERS">
<![CDATA[
#if defined(SEASONAL)
    sampler2D mMaskMap;
#endif
]]>
            </CodeInjection>
            <CodeInjection position="OBJECT_PARAMETERS">
<![CDATA[
/*
    windSnowLeafScale.r - wind intensity
    windSnowLeafScale.g - snow intensity
    windSnowLeafScale.b - leaf alpha
    windSnowLeafScale.a - (treePos.x + treePos.y + treePos.z) should be > 0.0
*/
float4 windSnowLeafScale;
]]>
            </CodeInjection>
            <CodeInjection position="LIB_FUNCTION_VS">
<![CDATA[
float fitRange(float mIn,float mOldMin,float mOldMax ,float mNewMin ,float mNewMax ){
    return (mNewMin + (mIn - mOldMin) * (mNewMax  - mNewMin ) / (mOldMax  - mOldMin));
}
float3 getTrunkBranchAnimationOffset(VS_INPUT In, ObjectParameters& object, float mTime, float3 windDirection,float windStrength){
    // Trunk animation
    float3 treePos = float3(object.modelMatrix[0][3] + invViewMatrix[0][3],
                            object.modelMatrix[1][3] + invViewMatrix[1][3],
                            object.modelMatrix[2][3] + invViewMatrix[2][3]);
    float3 windSideDirection = cross(float3(0,1,0), windDirection);
    // ----
    float treeVarSeed = treePos.x + treePos.y + treePos.z;
    // ----
    float3 trunkMoveDirection = windDirection;
    trunkMoveDirection = normalize(trunkMoveDirection + windSideDirection*sin(0.9 * mTime + treeVarSeed + 0.1)*0.3);
    // ----
    float normalizedTreeY = In.position.y/30;
    float trunkOffsetScale = (1*max(windStrength-0.1,0)/0.9 + sin( 1.5 * mTime + treeVarSeed)*0.7*windStrength)*normalizedTreeY*normalizedTreeY;
    float3 trunkOffset = trunkMoveDirection * trunkOffsetScale;
    // ----
    // Branch animation
    float branchVarSeed = (In.position.x+In.position.y+In.position.z) + treeVarSeed;
    float branchDistanceToTrunk = saturate( In.normal.w*100 / 25.0);
    // ----
    float3 branchMoveDirection = windDirection;
    branchMoveDirection = normalize(branchMoveDirection + windSideDirection*sin(branchVarSeed)*0.3);
    // ----
    float pi = 3.14159265359;
    float x1 = 0.20*mTime - 0.100*branchVarSeed;
    float x2 = 0.30*mTime - 0.025*branchVarSeed;
    float branchMove1 =  cos(x1*pi*0.4)*cos(x1*pi*2)+sin(7*x1*pi)*0.4;
    float branchMove2 =  cos(x2*pi*0.4)*cos(x2*pi*2)+sin(7*x2*pi)*0.4;
    branchMove1 = fitRange(branchMove1,-1,1,0.0,1.0);
    branchMove2 = fitRange(branchMove2,-1,1,0.0,2.0);
    float branchMove = lerp(branchMove1, branchMove2, windStrength);
    // ----
    branchMove *= 0.75*(windStrength * 0.2 + 0.8);
    branchMove *= branchDistanceToTrunk*0.2;
    // ----
    return (branchMoveDirection*branchMove + trunkOffset);
}
]]>
            </CodeInjection>
            <CodeInjection position="VS_OUTPUT">
<![CDATA[
#if defined(SEASONAL)
    float3 randTintBranchDistance : TEXCOORDn;
    float2 texCoordsWinter        : TEXCOORDn;
    float2 texCoordsSpring        : TEXCOORDn;
    float2 texCoordsSummer        : TEXCOORDn;
    float2 texCoordsAutumn        : TEXCOORDn;
#endif
]]>
            </CodeInjection>
            <CodeInjection position="POST_SET_TEXCOORDS_VS">
<![CDATA[
#if defined(SEASONAL)
    {
        float3 treePos = float3(object.modelMatrix[0][3] + invViewMatrix[0][3],
                                object.modelMatrix[1][3] + invViewMatrix[1][3],
                                object.modelMatrix[2][3] + invViewMatrix[2][3]);
        float treePosSum = (object.windSnowLeafScale.w > 0) ? object.windSnowLeafScale.w : (abs(treePos.x) + abs(treePos.y) + abs(treePos.z));
        treePosSum = ceil(10.0 * treePosSum)/10.0;
        float x = 0.10812 * treePosSum;
        float y = 0.21237 * treePosSum;
        Out.randTintBranchDistance.x = abs(frac(x) - 0.5) * 2; // 0-1 range triangle wave
        Out.randTintBranchDistance.y = abs(frac(y) - 0.5) * 2;
        Out.randTintBranchDistance.z = saturate( In.normal.w*100 / 5.0);
    }
    float2 mUV = getDefaultTexCoords(In, object);
    Out.texCoordsWinter = mUV*float2(0.5,0.5) + float2(0.5,0.0);
    Out.texCoordsSpring = mUV*float2(0.5,0.5) + float2(0.0,0.5);
    Out.texCoordsSummer = mUV*float2(0.5,0.5) + float2(0.5,0.5);
    Out.texCoordsAutumn = mUV*float2(0.5,0.5) + float2(0.0,0.0);
#endif
]]>
            </CodeInjection>
            <CodeInjection position="POST_GET_WORLD_POSE_VS">
<![CDATA[
float3 windDirection;
windDirection.xz = normalize(float2(cShared1, cShared2));
windDirection.y = 0;
//float3 windDirection = float3(1, 0, 0);
float windStrength = cShared0*object.windSnowLeafScale.x;
#if !defined(DEPTH_VIS) // avoid shadow artefacts by not animating in the shadow map
    if (windStrength > 0.0001) {
        // current pos
        worldPosition += getTrunkBranchAnimationOffset(In,object,cTime_s,windDirection,windStrength);
        // previous pos
        prevWorldPosition += getTrunkBranchAnimationOffset(In,object,cPrevTime_s,windDirection,windStrength);
    }
#endif
]]>
            </CodeInjection>
            <CodeInjection position = "LIB_FUNCTION_FS">
<![CDATA[
// Season states:
// cShared3 = 0 = summer
// cShared3 = 1 = autumn
// cShared3 = 2 = winter
// cShared3 = 3 = spring
// cShared3 = 4 = summer
// cShared4 = 0 .. 1 = snow control
/*
    0.00 = June      - fully spring green
    0.33 = July      -
    0.66 = August    - fully dark green
    1.00 = September - half green/orange
    1.33 = October   - fully orange
    1.66 = November  - half lost leaves
    2.00 = December  - almost no leaves
    2.33 = January   - no leaves
    2.66 = February  - no leaves
    3.00 = March     - small leaves
    3.33 = April     -
    3.66 = May       - fully spring green
*/
float getSnowMask(ObjectParameters& object, float3 mNormal, float mBackfaceScale){
    float snowLevel = min(cShared4, 1);
    float mTransition = snowLevel*snowLevel*snowLevel; // pow(cShared4,3);
    float NdotY = mNormal.y; // dot(mNormal, float3(0.0, 1.0, 0.0));
    NdotY = max(NdotY * mBackfaceScale, NdotY * (1.0-mBackfaceScale)) + mBackfaceScale;
    float mSnowMask = object.windSnowLeafScale.y*mTransition*saturate(NdotY);
    return mSnowMask;
}
// simplifed smoothstep
// remaps the values of x between a and b to the range [0, 1], and clamps values outside this range
float linearstepFS(float a, float b, float x){
    return saturate((x - a)/(b - a));
}
float fitRangeFS(float mIn,float mOldMin,float mOldMax ,float mNewMin ,float mNewMax ){
    return (mNewMin + (mIn - mOldMin) * (mNewMax  - mNewMin ) / (mOldMax  - mOldMin));
}
]]>
            </CodeInjection>
            <CodeInjection position="FS_GLOBALS">
<![CDATA[
    float  gSnowMask;
    bool   gIsSnowing;
    float3 gSnowDiffuse;
    float3 gSnowSpecular;
#if defined(SEASONAL)
    float gSeason;              // current season 0,1,2,3,4
    float gBranchesMask;        // 0..1   stencil-style mask only for branches
    float gLeavesMask;          // 0..1   stencil-style mask only for leaves
    float gLeavesGrow;          // 0.5..1 radial growing gradient mask only for leaves
    float gLeavesRand;          // 0..1   random value per leaf
    float gAlpha;               // alpha channel
#endif
]]>
            </CodeInjection>
            <CodeInjection position="START_FS">
<![CDATA[
#if defined(SEASONAL)
    float3 mMask   = tex2D(mMaskMap, ALBEDO_MAP_TEXCOORDS).xyz;
    // ----
    globals.gAlpha      = 1.0;
    {
        float scale1    = saturate(cShared3); // 0..1
        float scale2    = 1-saturate(cShared3-2.0); // 2..3
        float offset    = 0.4*In.vs.randTintBranchDistance.x * scale1*scale2; // time offset
        #if defined(DEBUG_SHOW_CUSTOM1)
            // NEED FOR LOD BAKING
            offset = 0.0;
        #endif
        globals.gSeason = max(cShared3 - offset,0.0); // current season
    }
    // ----
    globals.gBranchesMask = mMask.x;
    globals.gLeavesGrow   = mMask.y;
    // bring grow from 0.5..1.0 to 1.0..1.0 range (stencil-style)
    globals.gLeavesMask    = clamp(2*globals.gLeavesGrow,0.0,1.0);
    globals.gLeavesRand    = mMask.z + 0.0001;
#endif
    globals.gSnowDiffuse  = float3(0.7300,0.7668,0.8356);
    globals.gSnowSpecular = float3(0.1922,0.8706,0.0000);
    globals.gSnowMask  = 0.0;
    globals.gIsSnowing = cShared4 > 0.001 && object.windSnowLeafScale.y > 0.0;
]]>
            </CodeInjection>
            <CodeInjection position="POST_DIFFUSE_COLOR_FS">
<![CDATA[
#if defined(SEASONAL)
    // compute transition between seasons
    float mColorLeavesTransition = 0.0;
    float mTransition = 0.0;
    // ----
    #if defined(ALBEDO_MAP)
        float3 mColorBranches = tex2D(baseMap, In.vs.texCoordsWinter).rgb;
        float3 mColorSpring   = tex2D(baseMap, In.vs.texCoordsSpring).rgb;
        float3 mColorSummer   = tex2D(baseMap, In.vs.texCoordsSummer).rgb;
        float3 mColorAutumn   = tex2D(baseMap, In.vs.texCoordsAutumn).rgb;
    #else
        float3 mColorBranches = float3(0,0,0);
        float3 mColorSpring   = float3(0,0,0);
        float3 mColorSummer   = float3(0,0,0);
        float3 mColorAutumn   = float3(0,0,0);
    #endif
    #if defined(DEBUG_SHOW_CUSTOM1)
        // NEED FOR LOD BAKING
        diffuseColor = lerp(mColorBranches,mColorSummer,globals.gLeavesMask);
        globals.gAlpha = globals.gBranchesMask + globals.gLeavesGrow;
        if (globals.gSeason >= 1.0 && globals.gSeason < 2.0){
            diffuseColor = lerp(mColorBranches,mColorAutumn,globals.gLeavesMask);
        } else if (globals.gSeason >= 2.0 && globals.gSeason < 3.0) {
            diffuseColor = mColorBranches;
            globals.gAlpha = globals.gBranchesMask;
        } else if (globals.gSeason >= 3.0 && globals.gSeason < 4.0){
            diffuseColor = lerp(mColorBranches,mColorSpring,globals.gLeavesMask);
        }
    #else
        mColorSpring   = lerp( mColorSpring, float3(0.335,0.620,0.049), 0.30*In.vs.randTintBranchDistance.y );
        mColorSummer   = lerp( mColorSummer, float3(0.026,0.097,0.029), 0.30*In.vs.randTintBranchDistance.y );
        mColorAutumn   = lerp( mColorAutumn, float3(0.981,0.000,0.000), 0.40*In.vs.randTintBranchDistance.y );
        // ----
        float3 mColorLeaves = mColorSummer;
        float  mLeavesMaskTansition  = globals.gLeavesRand*saturate((2.0*globals.gLeavesGrow));
        float  mLeavesMask  = globals.gLeavesMask * float(globals.gLeavesRand <= object.windSnowLeafScale.z);
        /*
            0.0..0.7 Spring to Summer (only color)
            0.7..1.5 Summer to Autumn (only color)
            1.5..2.1 Autumn to Winter (losing leaves)
            2.1..2.6 Winter (no leaves)
            2.6..4.0 Winter to Spring (growing leaves)
        */
        {
            // 0.00 = June      - fully spring green
            // 0.33 = July      -
            // 0.66 = August    - fully dark green
            mTransition = fitRangeFS(globals.gSeason,0.0,0.70,0.00,1.00);
            mColorLeavesTransition = mTransition*linearstepFS(mTransition+0.1,mTransition,mLeavesMaskTansition);
            // color of the leaves
            mColorLeaves = lerp(mColorSpring, mColorSummer, mColorLeavesTransition);
            // combine it with branches
            diffuseColor = lerp(mColorBranches,mColorLeaves,mLeavesMask);
            globals.gAlpha = globals.gBranchesMask + globals.gLeavesGrow * float(globals.gLeavesRand <= object.windSnowLeafScale.z);
        }
        if ( globals.gSeason >= 0.70 && globals.gSeason < 1.50 ) {
            // 0.66 = August    - fully dark green
            // 1.00 = September - half green/orange
            // 1.33 = October   - fully orange
            mTransition = fitRangeFS(globals.gSeason,0.7,1.50,0.00,1.00);
            mColorLeavesTransition = mTransition*linearstepFS(mTransition+0.1,mTransition,mLeavesMaskTansition);
            // color of the leaves
            mColorLeaves = lerp(mColorSummer, mColorAutumn, mColorLeavesTransition);
            // combine it with branches
            diffuseColor = lerp(mColorBranches,mColorLeaves,mLeavesMask);
        } else if ( globals.gSeason >= 1.50  && globals.gSeason < 2.1 ) {
            // 1.66 = Novemner  - half lost leaves
            // 2.00 = December  - almost no leaves
            // 2.33 = January   - no leaves
            mTransition = fitRangeFS(globals.gSeason,1.5,2.1,0.00,1.00);
            // color of the leaves
            mColorLeaves = mColorAutumn;
            float autumnToWinter = (1-mTransition)*object.windSnowLeafScale.z <= 0.95*globals.gLeavesRand;
            globals.gAlpha = lerp(globals.gBranchesMask + globals.gLeavesGrow, globals.gBranchesMask, autumnToWinter);
            autumnToWinter = (1 - autumnToWinter)*globals.gLeavesMask;
            diffuseColor = lerp(mColorBranches,mColorLeaves,autumnToWinter);
        } else if ( globals.gSeason >= 2.1 && globals.gSeason < 2.6 ) {
            // 2.33 = January   - no leaves
            // 2.66 = February  - no leaves
            // 3.00 = March     - small leaves
            diffuseColor   = mColorBranches;
            globals.gAlpha = globals.gBranchesMask;
        } else if ( globals.gSeason >= 2.6 && globals.gSeason <= 4.0 ) {
            // 3.00 = March     - small leaves
            // 3.33 = April     -
            // 3.66 = May       - fully spring green
            mTransition    = fitRangeFS(globals.gSeason,2.6,4.0,0.00,1.00);
            // color of the leaves
            mColorLeaves = mColorSpring;
            float winterToSpring = mTransition*object.windSnowLeafScale.z <= 1-globals.gLeavesGrow + 0.45*globals.gLeavesRand;
            globals.gAlpha = lerp(globals.gBranchesMask + globals.gLeavesGrow, globals.gBranchesMask, winterToSpring);
            winterToSpring = (1 - winterToSpring)*globals.gLeavesMask;
            // combine it with branches
            diffuseColor = lerp(mColorBranches,mColorLeaves,winterToSpring);
        }
    #endif
#endif
    { // Snow mixing code
        if (globals.gIsSnowing){
            // pixelShader normal computed before this point, right after PARALLAX_MAPPING_FS
            globals.gSnowMask = getSnowMask(object,normal,-0.05);
            diffuseColor.rgb = lerp( diffuseColor.rgb, globals.gSnowDiffuse.rgb, globals.gSnowMask );
        }
    }
]]>
            </CodeInjection>
            <CodeInjection position = "POST_GLOSS_COLOR_FS">
<![CDATA[
    { // Snow mixing code
        if (globals.gIsSnowing){
            float3 mPbrMapValues = lerp( float3(1-roughness,bakedAO,metalness), globals.gSnowSpecular, globals.gSnowMask );
            roughness = 1-mPbrMapValues.x;
            bakedAO   = mPbrMapValues.y;
            metalness = mPbrMapValues.z;
        }
    }
]]>
            </CodeInjection>
            <CodeInjection position="ALPHA_FS">
<![CDATA[
#if defined(SEASONAL)
    alpha = globals.gAlpha;
#endif
]]>
            </CodeInjection>
            <CodeInjection position="POST_GET_LIGHTING_FS">
<![CDATA[
{
    float NdotLNegative = dot(normal, lightDir);
    if (NdotLNegative < 0) {
        NdotLNegative = -NdotLNegative*0.35;
    }
    NdotLNegative = min(NdotLNegative, 1);
    NdotL = saturate(dot(normal, lightDir));
    float3 lighting = ((atten*NdotLNegative) * diffuseShading + (atten*NdotL)* specularShading )* lightColor;
    return lighting;
}
]]>
            </CodeInjection>
            <CodeInjection position="FINAL_POS_FS">
<![CDATA[
{
#if defined(DEBUG_SHOW_ALPHA)
    #undef ALPHA_TESTED
    oColor.xyz *= 0.0001;
    // NEED FOR LOD BAKING
    #if defined(SEASONAL)
        oColor.xyz += srgb_to_linear(float3(globals.gLeavesRand,globals.gLeavesGrow,0));
    #endif
#endif
#if defined(DEBUG_SHOW_CUSTOM1)
    // NEED FOR LOD BAKING
    oColor.xyz *= 0.0001;
    #if defined(SEASONAL)
        oColor.xyz += diffuseColor;
    #endif
#endif
#if defined(DEBUG_SHOW_CUSTOM2)
    oColor.xyz *= 0.0001;
    #if defined(SEASONAL)
        float transition = 1-mTransition;
        oColor.xyz += 1-smoothstep( (transition + 0.5), transition , saturate(globals.gLeavesRand*In.vs.randTintBranchDistance.z) );
    #endif
#endif
}
]]>
            </CodeInjection>
        </CodeInjections>
    </LodLevel>
</CustomShader>
