#include "common.inc"
#include "materialPreprocess.inc"

struct GlobalConsts
{
  float4x4 matView;
  float4x4 matInvView;
  float4x4 matViewProj;
  float4x4 matInvViewProj;
  float4   taragetSizeInvSize;
  float4   cameraPosition;
  float    time;
  float    timeDelta;
  float2   clipPlanes;
  float4   depthEncodeConsts;

  float4   fogParameters;
  float4   fogParameters2;
  float4   distanceFog;
  float4   heightFog;
};

struct MatConsts
{
  float4 samplerTiling[ 2 ];
  float4 samplerOffset[ 2 ];
  float4 samplerSpeed[ 2 ];
  float4 samplerUV;
  float4 samplers;
#if GBUFFERPASS
  float parallaxAmt;
  float specPower;
  float2 paddingms;
#else
  float4 specularColour;
  float3 tintColour;
  float parallaxAmt;
  float3 selfIlluminationColour;
  float specFactor;
  float3 tintColour2;
  float specPower;
  float4 rimLightColour;
#endif
};

#ifdef VS

  /*
    vertex input attributes
  */
  #if POSITION
    layout ( location = attrPosLoc ) in float3 aPosition;
  #elif POSITION4
    layout ( location = attrPosLoc ) in float4 aPosition;
  #endif
  #if NORMAL
    layout ( location = attrNormLoc ) in float3 aNormal;
  #endif
  #if TANGENT
    layout ( location = attrTangLoc ) in float4 aTangent;
  #endif
  #if COLOUR
    layout ( location = attrColLoc ) in float4 aColour;
  #endif
  #if BONEINFO
    layout ( location = attrBoneIndicesLoc ) in float4 aBoneIndices;
    layout ( location = attrBoneWeightsLoc ) in float4 aBoneWeights;
  #endif
  #if TEXCOORD==4
    layout ( location = attrUV0Loc ) in float4 aTexCoord0;
    layout ( location = attrUV1Loc ) in float4 aTexCoord1;
  #elif TEXCOORD==3
    layout ( location = attrUV0Loc ) in float4 aTexCoord0;
    layout ( location = attrUV1Loc ) in float2 aTexCoord1;
  #elif TEXCOORD==2
    layout ( location = attrUV0Loc ) in float4 aTexCoord0;
  #elif TEXCOORD==1
    layout ( location = attrUV0Loc ) in float2 aTexCoord0;
  #endif

  /*
    VS Constants
  */
  #if defined(USE_BINDING)
  layout( binding = VSCB0Index, std140 ) uniform VSCB0
  #else
  layout (std140) uniform VSCB0
  #endif
  {
    GlobalConsts vsgc;
  };

  #if defined(USE_BINDING)
  layout( binding = VSCB1Index, std140 ) uniform VSCB1
  #else
  layout (std140) uniform VSCB1
  #endif
  {
    float4x4 worldMatrix;
  #if GBUFFERPASS
    float4x4 lastFrameWorldMatrix;
  #endif
  };

  #if defined(USE_BINDING)
  layout( binding = VSCB2Index, std140 ) uniform VSCB2
  #else
  layout (std140) uniform VSCB2
  #endif
  {
    MatConsts vsmc;
  };

  struct BoneXForm
  {
    float4 xform0;
    float4 xform1;
    float4 xform2;
  };

  #if defined(USE_BINDING)
  layout( binding = VSCB3Index, std140 ) uniform VSCB3
  #else
  layout (std140) uniform VSCB3
  #endif
  {
    BoneXForm boneXform[ 32 ];
  };

  #if INSTANCED
	#if defined(USE_BINDING)
    layout( binding = VSCB7Index, std140 ) uniform VSCB7
	#else
	layout (std140) uniform VSCB7
	#endif
    {
      BoneXForm instXform[ 42 ]; 
    };
  #endif

  #if INSTANCED
    #define BONE_DATA instXform
  #else
    #define BONE_DATA boneXform
  #endif

  /*
    interpolants
  */
  #if COLOUR
    out float4 vColour;
  #endif
  #if NORMAL && !OUTLINEMODE
    out float3 vNormal;
  #endif
  #if TANGENT
    out float4 vTangent;
  #endif
  #if TEXCOORD!=0
    out float4 vUv0;
    out float4 vUv1;
  #endif
  #if ( GBUFFERPASS == GBUFFER_PACKED_LPP ) || ( GBUFFERPASS == GBUFFER_DEPTH ) || ( PARALLAXMAP ) || ( DEPTHONLY )
      out float4 vDepthViewDirTS;
  #endif
  #ifdef SAMPLE_LIGHTBUFFER
    out float4 vCsPos;
    out float4 vWsPos;
  #elif FORWARDLIGHT
    out float4 vWsPos;
  #endif

  float3 skinPosition( float3 pos, ivec4 boneIndices, float4 blendWeights )
  {
    float4 posLS = float4( pos, 1.f );
    float3 p0 = float3( dot( posLS, BONE_DATA[ boneIndices.x ].xform0 ), 
                        dot( posLS, BONE_DATA[ boneIndices.x ].xform1 ),
                        dot( posLS, BONE_DATA[ boneIndices.x ].xform2 ) ) * blendWeights.x;  

    float3 p1 = float3( dot( posLS, BONE_DATA[ boneIndices.y ].xform0 ), 
                        dot( posLS, BONE_DATA[ boneIndices.y ].xform1 ),
                        dot( posLS, BONE_DATA[ boneIndices.y ].xform2 ) ) * blendWeights.y;

    float3 p2 = float3( dot( posLS, BONE_DATA[ boneIndices.z ].xform0 ), 
                        dot( posLS, BONE_DATA[ boneIndices.z ].xform1 ),
                        dot( posLS, BONE_DATA[ boneIndices.z ].xform2 ) ) * blendWeights.z;

    float3 p3 = float3( dot( posLS, BONE_DATA[ boneIndices.w ].xform0 ), 
                        dot( posLS, BONE_DATA[ boneIndices.w ].xform1 ),
                        dot( posLS, BONE_DATA[ boneIndices.w ].xform2 ) ) * blendWeights.w;

    return ( p0 + p1 + p2 + p3 );
  }

  float3 skinDirection( float3 dir, ivec4 boneIndices, float4 blendWeights )
  {
    float3 p0 = float3( dot( dir, BONE_DATA[ boneIndices.x ].xform0.xyz ), 
                        dot( dir, BONE_DATA[ boneIndices.x ].xform1.xyz ),
                        dot( dir, BONE_DATA[ boneIndices.x ].xform2.xyz ) ) * blendWeights.x;  

    float3 p1 = float3( dot( dir, BONE_DATA[ boneIndices.y ].xform0.xyz ), 
                        dot( dir, BONE_DATA[ boneIndices.y ].xform1.xyz ),
                        dot( dir, BONE_DATA[ boneIndices.y ].xform2.xyz ) ) * blendWeights.y;

    float3 p2 = float3( dot( dir, BONE_DATA[ boneIndices.z ].xform0.xyz ), 
                        dot( dir, BONE_DATA[ boneIndices.z ].xform1.xyz ),
                        dot( dir, BONE_DATA[ boneIndices.z ].xform2.xyz ) ) * blendWeights.z;

    float3 p3 = float3( dot( dir, BONE_DATA[ boneIndices.w ].xform0.xyz ), 
                        dot( dir, BONE_DATA[ boneIndices.w ].xform1.xyz ),
                        dot( dir, BONE_DATA[ boneIndices.w ].xform2.xyz ) ) * blendWeights.w;

    return ( p0 + p1 + p2 + p3 );
  }

  void main()
  {
    float4 posLS = float4( aPosition.xyz, 1.f );
    
#if INSTANCED
  #if BONEINFO
    int xformInstIdx = gl_InstanceID * 2;
    int boneInstIdx = gl_InstanceID * 2 + 1;
    float4 boneIndices = float4(boneInstIdx);
  #else
    int xformInstIdx = gl_InstanceID;
  #endif
#elif BONEINFO
  float4 boneIndices = aBoneIndices; 
#endif

  #if OUTLINEMODE && NORMAL
    posLS.xyz += aNormal.xyz * 0.05f;
  #endif

  #if BONEINFO
    posLS.xyz = skinPosition( posLS.xyz, ivec4( boneIndices ), aBoneWeights );
  #endif

  #if INSTANCED
    float4 p = float4( dot( posLS, instXform[ xformInstIdx ].xform0 ), 
                        dot( posLS, instXform[ xformInstIdx ].xform1 ),
                        dot( posLS, instXform[ xformInstIdx ].xform2 ), 1.f );
  #else
    float4 p = posLS * worldMatrix;
  #endif
  float4 opos = p * vsgc.matViewProj;
    
  gl_Position = opos;
    
  #ifdef SAMPLE_LIGHTBUFFER
    vCsPos = opos;
  #endif

  #if FORWARDLIGHT || defined(SAMPLE_LIGHTBUFFER)
    vWsPos = p;
  #endif

  #if TEXCOORD!=0
    float2 uvs[ TEXCOORD ];
    #if TEXCOORD==4
      uvs[ 0 ] = aTexCoord0.xy;
      uvs[ 1 ] = aTexCoord0.zw;
      uvs[ 2 ] = aTexCoord1.xy;
      uvs[ 3 ] = aTexCoord1.zw;
    #elif TEXCOORD==3
      uvs[ 0 ] = aTexCoord0.xy;
      uvs[ 1 ] = aTexCoord0.zw;
      uvs[ 2 ] = aTexCoord1.xy;
    #elif TEXCOORD==2
      uvs[ 0 ] = aTexCoord0.xy;
      uvs[ 1 ] = aTexCoord0.zw;
    #elif TEXCOORD==1
      uvs[ 0 ] = aTexCoord0.xy;
    #endif

    vUv0.xy = ( uvs[ int( vsmc.samplerUV.x ) ] * vsmc.samplerTiling[ 0 ].xy + vsmc.samplerOffset[ 0 ].xy ) + ( vsmc.samplerSpeed[ 0 ].xy * vsgc.time );
    vUv0.zw = ( uvs[ int( vsmc.samplerUV.y ) ] * vsmc.samplerTiling[ 0 ].zw + vsmc.samplerOffset[ 0 ].zw ) + ( vsmc.samplerSpeed[ 0 ].zw * vsgc.time );
    vUv1.xy = ( uvs[ int( vsmc.samplerUV.z ) ] * vsmc.samplerTiling[ 1 ].xy + vsmc.samplerOffset[ 1 ].xy ) + ( vsmc.samplerSpeed[ 1 ].xy * vsgc.time );
    vUv1.zw = ( uvs[ int( vsmc.samplerUV.w ) ] * vsmc.samplerTiling[ 1 ].zw + vsmc.samplerOffset[ 1 ].zw ) + ( vsmc.samplerSpeed[ 1 ].zw * vsgc.time );

  #endif
  #if COLOUR
    #if VERTEXALPHA
      #if ADDITIVE
       vColour = float4( aColour.xyz * aColour.w, aColour.w );
      #else
        vColour = aColour.xyzw;
      #endif
    #else
      vColour = float4( aColour.xyz, 1.f );
    #endif
  #endif
   
  #if NORMAL && !OUTLINEMODE
    float4 n = float4( aNormal.xyz, 0.f );
    #if BONEINFO
      n.xyz = skinDirection( n.xyz, ivec4( boneIndices ), aBoneWeights );
    #endif
    #if INSTANCED
      vNormal = float3( dot( n, instXform[ xformInstIdx ].xform0 ), 
                          dot( n, instXform[ xformInstIdx ].xform1 ),
                          dot( n, instXform[ xformInstIdx ].xform2 ) );
    #else
      vNormal = float4( n * worldMatrix ).xyz;
    #endif
    
    #if GBUFFERPASS == GBUFFER_PACKED_LPP
      vNormal = float4( float4( vNormal.xyz, 0.f ) * vsgc.matView ).xyz;
    #endif
  #endif
  #if TANGENT
    float4 t = float4( aTangent.xyz, 0.f );
    #if BONEINFO
      t.xyz = skinDirection( t.xyz, ivec4( boneIndices ), aBoneWeights );
    #endif
    #if INSTANCED
      vTangent = float4( dot( t, instXform[ xformInstIdx ].xform0 ), 
                          dot( t, instXform[ xformInstIdx ].xform1 ),
                          dot( t, instXform[ xformInstIdx ].xform2 ), aTangent.w );
    #else
      vTangent = float4( float4( t * worldMatrix ).xyz, aTangent.w );
    #endif
    #if GBUFFERPASS == GBUFFER_PACKED_LPP
      vTangent = float4( float4( vTangent.xyz, 0.f ) * vsgc.matView ).xyz;
    #endif
  #endif

  #if ( GBUFFERPASS == GBUFFER_PACKED_LPP ) || ( GBUFFERPASS == GBUFFER_DEPTH ) || ( PARALLAXMAP ) || ( DEPTHONLY )
    float3 viewDirTS = float3( 0.f, 0.f, 0.f );
    #if PARALLAXMAP
      float3 binormal = cross( vNormal.xyz, vTangent.xyz );
      float3 viewDir = normalize( vsgc.cameraPosition.xyz - p.xyz );
      viewDirTS = float3( dot( vTangent.xyz, viewDir.xyz ), dot( binormal.xyz, viewDir.xyz ), dot( vNormal.xyz, viewDir.xyz ) );
    #endif
    
    float depth = 0.f;
    #if ( GBUFFERPASS == GBUFFER_PACKED_LPP ) || ( GBUFFERPASS == GBUFFER_DEPTH )
      depth = dot( p.xyzw, float4( vsgc.matView[ 0 ].z, vsgc.matView[ 1 ].z, vsgc.matView[ 2 ].z, vsgc.matView[ 3 ].z ) );
    #elif DEPTHONLY
      depth = opos.z;
    #endif

    vDepthViewDirTS = float4( depth, viewDirTS.xyz );
  #endif
  }

#endif //VS


#ifdef PS

  layout ( location = 0 ) out float4 FragColor;
  
  /*
    constant buffers
  */
  #if defined(USE_BINDING)
  layout( binding = PSCB0Index, std140 ) uniform PSCB0
  #else
  layout (std140) uniform PSCB0
  #endif
  {
    GlobalConsts psgc;
  };
  
  #if defined(USE_BINDING)
  layout( binding = PSCB1Index, std140 ) uniform PSCB1
  #else
  layout (std140) uniform PSCB1
  #endif
  { 
    float3 colorOverride;
    float alphaFactor;
    float4x4 colXfm;
  };

  #if defined(USE_BINDING)
  layout( binding = PSCB2Index, std140 ) uniform PSCB2
  #else
  layout (std140) uniform PSCB2
  #endif
  {
    MatConsts psmc;
  };

  #if defined(USE_BINDING)
  layout( binding = PSCB3Index, std140 ) uniform PSCB3
  #else
  layout (std140) uniform PSCB3
  #endif
  {
    float4 sunDir;
    float4 sunCol;
    float4 shAmbientR;
    float4 shAmbientG;
    float4 shAmbientB;
  };
  
  /*
    interpolants
  */
  #if COLOUR
    in float4 vColour;
  #endif
  #if NORMAL && !OUTLINEMODE
    in float3 vNormal;
  #endif
  #if TANGENT
    in float4 vTangent;
  #endif
  #if TEXCOORD!=0
    in float4 vUv0;
    in float4 vUv1;
  #endif
  #if ( GBUFFERPASS == GBUFFER_PACKED_LPP ) || ( GBUFFERPASS == GBUFFER_DEPTH ) || ( PARALLAXMAP ) || ( DEPTHONLY )
      in float4 vDepthViewDirTS;
  #endif
  #ifdef SAMPLE_LIGHTBUFFER
    in float4 vCsPos;
    in float4 vWsPos;
  #elif FORWARDLIGHT
    in float4 vWsPos;
  #endif
  
  /*
    textures
  */
  #if defined(USE_BINDING)
  layout( binding = 0 ) uniform sampler2D sDiffuseMap;
  layout( binding = 1 ) uniform sampler2D sSpecMap;
  layout( binding = 2 ) uniform sampler2D sNormalMap;
  #else
  uniform sampler2D sDiffuseMap;
  uniform sampler2D sSpecMap;
  uniform sampler2D sNormalMap;
  #endif

  #if DIFFUSE2MAP
	#if defined(USE_BINDING)
    layout( binding = 3 ) uniform sampler2D sEnvOrDiff2Map;
	#else
	uniform sampler2D sEnvOrDiff2Map;
	#endif
  #elif ENVIRONMENTMAP
    #if defined(USE_BINDING)
    layout( binding = 3 ) uniform sampler2D sEnvOrDiff2Map;
	#else
	uniform sampler2D sEnvOrDiff2Map;
	#endif
  #endif

  #if defined(USE_BINDING)
  layout( binding = 4 ) uniform sampler2D sParallaxMap;
  #else
  uniform sampler2D sParallaxMap;
  #endif

  #if MASK2MAP
    #if defined(USE_BINDING)
    layout( binding = 5 ) uniform sampler2D sSelfIllumOrMaskMap;
	#else
	uniform sampler2D sSelfIllumOrMaskMap;
	#endif
  #elif SELFILLUMINATIONMAP
	#if defined(USE_BINDING)
    layout( binding = 5 ) uniform sampler2D sSelfIllumOrMaskMap;
	#else
	uniform sampler2D sSelfIllumOrMaskMap;
	#endif
  #endif

  #ifdef SAMPLE_GBUFFER
    #if defined(USE_BINDING)
    layout( binding = 6 ) uniform sampler2D sGBuffer;
	#else
	uniform sampler2D sGBuffer;
	#endif
  #endif

  #ifdef SAMPLE_LIGHTBUFFER
    #if defined(USE_BINDING)
    layout( binding = 7 ) uniform sampler2D sLightBuffer;
	#else
	uniform sampler2D sLightBuffer;
	#endif
  #endif


  #if ENVIRONMENTMAP
    float3 CalculateEnvironment( float3 viewDirection, float3 normalVector, float fresnelAmt )
    {
      float3 reflected = reflect( -viewDirection, normalVector );
      float absZ = abs( reflected.z );
      float sign = reflected.z / absZ;

      float2 uv = ( reflected.xy / ( 2.0f * ( 1.0f + absZ ) ) ) + 0.5f;
      uv.x *= 0.5f * sign;
      uv.y = 1.0f - uv.y;

      float fresnel = 1.0 - saturate( dot( normalVector, viewDirection ) * fresnelAmt );
      float3 envColour = texture( sEnvOrDiff2Map, uv ).xyz; 

      return envColour * fresnel * fresnel;
    }
  #endif

  float2 GetFogAmounts( float distance, float height, float4 fogParams )
  {
    float2 amounts;

    amounts.x = saturate( height * fogParams.z + fogParams.w );
    amounts.y = saturate( distance * fogParams.x + fogParams.y );
    return amounts;
  }

  float3 CalculateFog( float3 diffuse, float4 heightFogParams, float heightFogAmount, float4 distanceFogParams, float distanceFogAmount )
  {
    float3 colour = lerp( diffuse, heightFogParams.xyz, heightFogAmount * heightFogParams.w );
    float fogIntensity = distanceFogAmount * distanceFogParams.w;
    return lerp( colour.xyz, distanceFogParams.xyz, fogIntensity );
  }

  void main()
  {
  #if OUTLINEMODE
    FragColor = float4( colorOverride, 1.f );
  #else

    #if GBUFFERPASS || DEPTHONLY
      float4 colour = float4( 1.0f, 1.0f, 1.0f, 1.0f );
    #else
      float4 colour = float4( psmc.tintColour, 1.0f );
    #endif

    #if TEXCOORD!=0
      float2 uvs[ 4 ];
      uvs[ 0 ] = vUv0.xy;
      uvs[ 1 ] = vUv0.zw;
      uvs[ 2 ] = vUv1.xy;
      uvs[ 3 ] = vUv1.zw;
    #endif
      
    #if PARALLAXMAP
      float parallaxUvSlot = frac( psmc.samplers[ PARALLAX_SLOT ] ) * 10.f;
      float h = texture( sParallaxMap, uvs[ int( parallaxUvSlot ) ] ).x;
      float3 ViewDirInTanSpace = normalize( vDepthViewDirTS.yzw );
      float2 parallaxOffset = psmc.parallaxAmt * ( h * 0.04f - 0.02f ) * ViewDirInTanSpace.xy;

      uvs[ 0 ] += parallaxOffset;
      uvs[ 1 ] += parallaxOffset;
      uvs[ 2 ] += parallaxOffset;
      uvs[ 3 ] += parallaxOffset;
    #endif

    #if DIFFUSEMAP
      float diffuseUvSlot = floor( psmc.samplers[ DIFFUSE_SLOT ] );
      colour *= texture( sDiffuseMap, uvs[ int( diffuseUvSlot ) ] );
      #if ALPHATEST
        clip( colour.a - 0.1f );
      #endif
    #endif

    #if DIFFUSE2MAP
      float diffuse2UvSlot = frac( psmc.samplers[ DIFFUSE2_SLOT ] ) * 10.f;
      float4 diffuse2 = texture( sEnvOrDiff2Map, uvs[ int( diffuse2UvSlot ) ] );
      float mask = diffuse2.w;
      
      #if MASK2MAP
        float mask2UvSlot = frac( psmc.samplers[ MASK2_SLOT ] ) * 10.f;
        mask *= texture( sSelfIllumOrMaskMap, uvs[ int( mask2UvSlot ) ] ).x;
      #endif

        diffuse2.xyz *= psmc.tintColour2.xyz;
      
      #if BLEND2MODE == BLENDOP_ADD
        colour.xyz += diffuse2.xyz * mask;
      #elif BLEND2MODE == BLENDOP_MUL
        colour.xyz = lerp( colour.xyz, colour.xyz * diffuse2.xyz, mask );
      #else
        colour.xyz = lerp( colour.xyz, diffuse2.xyz, mask );
      #endif
    #endif

    #if COLOUR && ( VERTEXCOLOUR && VERTEXALPHA )
      colour *= vColour.zyxw;
    #elif COLOUR && VERTEXCOLOUR
      colour.xyz *= vColour.zyx;
    #elif COLOUR && VERTEXALPHA
      colour.w *= vColour.w;
    #endif

    #if GBUFFERPASS == 0
      colour.w *= alphaFactor;
      #if ADDITIVE
        colour.xyz *= colour.w;
      #endif
    #endif

    #if NORMAL
      float3 n = normalize( vNormal.xyz );
      #if NORMALMAP
        float3 t = normalize( vTangent.xyz );
        float3 b = normalize( cross( ( vTangent.w * 2.f - 1.f ) * n, t ) );
        
        float normalUvSlot = floor( psmc.samplers[ NORMAL_SLOT ] );
        float3 nm = texture( sNormalMap, uvs[ int( normalUvSlot ) ] ).xyz * 2.f - 1.f;

        n = normalize( nm.x * t.xyz + nm.y * b + nm.z * n.xyz);
      #endif
    #elif GBUFFERPASS
      float3 n = float3( 0.f, 1.f, 0.f );
    #endif

    #if defined( SAMPLE_LIGHTBUFFER ) || FORWARDLIGHT==1 || NOLIGHTING==1
      #if ( defined( SAMPLE_LIGHTBUFFER ) && !NOLIGHTING ) || ( defined( SAMPLE_GBUFFER ) && ( ENVIRONMENTMAP || RIMLIGHT ) && !NORMAL )
        float2 projUV = ( vCsPos.xy / vCsPos.ww ) * float2( 0.5, 0.5 ) + float2( 0.5, 0.5 );
      #endif

      #if defined( SAMPLE_LIGHTBUFFER ) && !NOLIGHTING
        float4 l = texture( sLightBuffer, projUV ) * 2.f;
        #define L_PRESENT
      #endif

      #if FORWARDLIGHT 
        #if NORMAL && !NOLIGHTING

          float nDotL = saturate( dot( n.xyz, sunDir.xyz ) );
          #ifndef SAMPLE_LIGHTBUFFER
            float4 l = float4( 0.f, 0.f, 0.f, 0.f );
          #endif
          
          l.xyz += nDotL * sunCol.xyz;

          l.r += dot( shAmbientR, float4( n.xyz, 1 ) );
          l.g += dot( shAmbientG, float4( n.xyz, 1 ) );
          l.b += dot( shAmbientB, float4( n.xyz, 1 ) );

          float3 V = normalize( psgc.cameraPosition.xyz - vWsPos.xyz );
          
          #define CHEETAH_SPECULAR_MODEL
          #ifdef CHEETAH_SPECULAR_MODEL
            float3 R = reflect( sunDir.xyz, n.xyz );
            l.w += pow( saturate( dot( -V, R ) ), max( psmc.specPower, 1.f ) );  
          #else
            float3 H = normalize( sunDir.xyz + V );
            l.w += pow( saturate( dot( normalWS.xyz, H ) ), max( psmc.specPower, 1.f ) );  
          #endif

        #else
          float4 l = float4( 1.f, 1.f, 1.f, 0.f );
        #endif
        #define L_PRESENT
      #endif

      #ifndef L_PRESENT
        float4 l = float4( 1.f, 1.f, 1.f, 0.f );
      #endif

      #if SELFILLUMINATION
        float3 selfIllum = psmc.selfIlluminationColour;
        #if SELFILLUMINATIONMAP
          float selfIllumUvSlot = floor( psmc.samplers[ SELFILLUMINATION_SLOT ] );
          selfIllum *= texture( sSelfIllumOrMaskMap, uvs[ int( selfIllumUvSlot ) ] ).xyz;
        #endif
        l.xyz += selfIllum;
      #endif

      colour.xyz *= l.xyz;

      float3 glossMap = psmc.specularColour.xyz;
      #if GLOSSMAP
        float glossUvSlot = floor( psmc.samplers[ GLOSS_SLOT ] );
        #if GLOSSMAP == 1
          glossMap *= texture( sSpecMap, uvs[ int( glossUvSlot ) ] ).xyz;
        #elif GLOSSMAP == 2
          glossMap *= texture( sSpecMap, uvs[ int( glossUvSlot ) ] ).www;
        #endif
      #endif

      float3 specular = glossMap * psmc.specFactor * l.w;


      #if ( RIMLIGHT || ENVIRONMENTMAP ) && ( defined(SAMPLE_GBUFFER) || NORMAL )
        #if !NORMAL && defined(SAMPLE_GBUFFER)
          float4 gbuffer0 = texture( sGBuffer, projUV );
          #if GBUFFERTYPE == GBUFFER_PACKED_LPP
            float3 normalES = DecodeNormalSphereMap( gbuffer0.xy );
            float3 n = normalize( (float3x3)matInvView * normalES.xyz );
          #else
            float3 n = normalize( gbuffer0.xyz * 2.0f - 1.0f );
          #endif
        #endif
        float3 viewDir = normalize( psgc.cameraPosition.xyz - vWsPos.xyz );
        #if ENVIRONMENTMAP
          float3 env = CalculateEnvironment( viewDir, n, psmc.specularColour.w );
          specular *= env;
        #endif
        #if RIMLIGHT
          float rimLightFresnel = saturate( 1.0f - abs( dot( n, viewDir ) ) * psmc.rimLightColour.w );
          colour.xyz += psmc.rimLightColour.xyz * rimLightFresnel * rimLightFresnel * alphaFactor;
        #endif
      #endif

      colour.xyz += specular;

      float distanceToCamera = distance( psgc.cameraPosition, vWsPos );
      #if ADDITIVE==1
          float alpha0 = dot( colour.xyz, float3( 0.33334f, 0.33334f, 0.33334f ) );
      #endif
      #if FOG
          float2 amounts = GetFogAmounts( distanceToCamera, vWsPos.y, psgc.fogParameters );
          colour.xyz = CalculateFog( colour.xyz, psgc.heightFog, amounts.x, psgc.distanceFog, amounts.y );
      #endif
      #if !GBUFFERPASS && COLOUR_XFORM
          colour.xyz = ( float4( colour.xyz, 1.f ) * colXfm ).xyz;
      #endif
      #if ADDITIVE==1
          float alpha1 = dot( colour.xyz, float3( 0.33334f, 0.33334f, 0.33334f ) );
          colour.xyz *= alpha0 / max( alpha1, 0.0001f );
      #endif
    #endif

    #if DEPTHONLY

      FragColor = vDepthViewDirTS.xxxx;

    #else

      #if GBUFFERPASS
        #if GBUFFERPASS == GBUFFER_PACKED_LPP 
          #error
        #elif GBUFFERPASS == GBUFFER_DEPTH
          #error
        #elif GBUFFERPASS == GBUFFER_NORMALSPEC
          FragColor = float4( n.xyz * 0.5f + 0.5f, psmc.specPower / 255.f ); 
        #else
          FragColor = float4( 1.f, 0.f, 1.f, 1.f );
        #endif
      #else
        FragColor = colour;
      #endif

    #endif 

  #endif //OUTLINEMODE
  }

#endif //PS