uniform float4x4 modelView;
uniform float4x4 modelViewProj;
uniform float4x4 billboard;
uniform float4 flipTCoordsAndFogDensity;
uniform float4 fogColor;
uniform float4 billboardColor;
uniform float4 fade;
uniform float4x4 basis;

#ifdef DX9

TEXTURE gDiffuseMap;
sampler gDiffuseSampler = sampler_state
{
	Texture = (gDiffuseMap);
	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;
	AddressU = Clamp;
	AddressV = Wrap;
};

#else

Texture2D gDiffuseMap;

SamplerState gTriLinearSamClamp
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = CLAMP;
    AddressV = WRAP;
};

#endif

void VS(  float4 position : POSITION,
          float4 color : COLOR,
          float4 texCoord : TEXCOORD,

          out float4 oPosition : SV_POSITION,
          out float4 oColor : COLOR,
          out float4 oTexCoord : TEXCOORD
          )
{
    float3 eyePosition = mul(modelView, position).xyz;
    float fogDistance = length(eyePosition);
    float fogExponent = clamp(fogDistance * flipTCoordsAndFogDensity.y, 0.0, 1.0);
    float fogFactor = clamp(exp(-abs(fogExponent)), 0.0, 1.0);
    float4 preFadedColor;

    if (billboardColor.w > 0)
    {
        preFadedColor.xyz = lerp(fogColor.xyz, billboardColor.xyz, fogFactor);
        preFadedColor.w = billboardColor.w;
    }
    else
    {
        preFadedColor = lerp(fogColor, color, fogFactor);
    }

	float voxelFade = 1.0;
	if (fade.z > 0.0) {
		if (texCoord.w > 0.0) {
			voxelFade = abs(texCoord.w) * (fade.w - fade.z);
		} else if (texCoord.w < 0.0) {
			voxelFade = 1.0 - abs(texCoord.w) * (fade.w - fade.z);	
		}
		voxelFade = clamp(voxelFade, 0.0, 1.0);
	}

    oColor = lerp(float4(0, 0, 0, 0), preFadedColor, fade.xxxx * voxelFade * (1 - fogExponent * fogExponent));

    float4 vertPos = float4(abs(texCoord.x) * sign(texCoord.y), abs(texCoord.x) * sign(texCoord.z), 0, 1.0);

    float4 rotatedPos;

    float cosLength, sinLength;
    float angle = abs(texCoord.z) + abs(texCoord.y) * sign(texCoord.x) * fade.w;
	angle = fmod(angle, 3.14159265f * 2.0f);
    sincos(angle, sinLength, cosLength);
    rotatedPos.x = (cosLength * vertPos.x + -sinLength * vertPos.y);
    rotatedPos.y = (sinLength * vertPos.x + cosLength * vertPos.y);
    rotatedPos.z = 0;
    rotatedPos.w = 1.0;

	float4 vert = mul(position, basis);
    float4x4 xlate = float4x4(1, 0, 0, vert.x, 0, 1, 0, vert.y, 0, 0, 1, vert.z, 0, 0, 0, 1);
    float4x4 xlatebb = mul(xlate, billboard);
    float4x4 mvb = mul(modelViewProj, xlatebb);
    oPosition = mul(mvb, rotatedPos);

    oTexCoord = float4(0.5 * sign(texCoord.y) + 0.5,
                       0.5 * sign(texCoord.z) * flipTCoordsAndFogDensity.x + 0.5,
                       0.0, 0.0);
}

float4 PS(float4 posH : SV_POSITION,
          float4 color : COLOR,
          float4 texCoord : TEXCOORD ) : SV_TARGET
{
    float4 fragIn;
#ifdef DX9
	fragIn = tex2D(gDiffuseSampler, texCoord);
#else
    fragIn = gDiffuseMap.Sample(gTriLinearSamClamp, texCoord.xyz);
#endif
    return fragIn * color;
}

#ifdef DX11
technique11 ColorTech
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_5_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_5_0, PS() ) );
    }
}
#endif

#ifdef DX10
technique10 ColorTech
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS() ) );
    }
}
#endif

#ifdef DX10LEVEL9
technique10 ColorTech
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0_level_9_1, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0_level_9_1, PS() ) );
    }
}
#endif

#ifdef DX9
technique
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_3_0, VS() ) );
        SetPixelShader( CompileShader( ps_3_0, PS() ) );
    }
}
#endif

#ifdef DX9_2_0
technique
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_2_0, VS() ) );
        SetPixelShader( CompileShader( ps_2_0, PS() ) );
    }
}
#endif
