#region File Description
//-----------------------------------------------------------------------------
// AlphaTestEffect.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
namespace Microsoft.Xna.Framework.Graphics
{
///
/// Built-in effect that supports alpha testing.
///
public class AlphaTestEffect : Effect, IEffectMatrices, IEffectFog
{
#region Effect Parameters
EffectParameter textureParam;
EffectParameter diffuseColorParam;
EffectParameter alphaTestParam;
EffectParameter fogColorParam;
EffectParameter fogVectorParam;
EffectParameter worldViewProjParam;
EffectParameter shaderIndexParam;
#endregion
#region Fields
bool fogEnabled;
bool vertexColorEnabled;
Matrix world = Matrix.Identity;
Matrix view = Matrix.Identity;
Matrix projection = Matrix.Identity;
Matrix worldView;
Vector3 diffuseColor = Vector3.One;
float alpha = 1;
float fogStart = 0;
float fogEnd = 1;
CompareFunction alphaFunction = CompareFunction.Greater;
int referenceAlpha;
bool isEqNe;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
#endregion
#region Public Properties
///
/// Gets or sets the world matrix.
///
public Matrix World
{
get { return world; }
set
{
world = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.Fog;
}
}
///
/// Gets or sets the view matrix.
///
public Matrix View
{
get { return view; }
set
{
view = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.Fog;
}
}
///
/// Gets or sets the projection matrix.
///
public Matrix Projection
{
get { return projection; }
set
{
projection = value;
dirtyFlags |= EffectDirtyFlags.WorldViewProj;
}
}
///
/// Gets or sets the material diffuse color (range 0 to 1).
///
public Vector3 DiffuseColor
{
get { return diffuseColor; }
set
{
diffuseColor = value;
dirtyFlags |= EffectDirtyFlags.MaterialColor;
}
}
///
/// Gets or sets the material alpha.
///
public float Alpha
{
get { return alpha; }
set
{
alpha = value;
dirtyFlags |= EffectDirtyFlags.MaterialColor;
}
}
///
/// Gets or sets the fog enable flag.
///
public bool FogEnabled
{
get { return fogEnabled; }
set
{
if (fogEnabled != value)
{
fogEnabled = value;
dirtyFlags |= EffectDirtyFlags.ShaderIndex | EffectDirtyFlags.FogEnable;
}
}
}
///
/// Gets or sets the fog start distance.
///
public float FogStart
{
get { return fogStart; }
set
{
fogStart = value;
dirtyFlags |= EffectDirtyFlags.Fog;
}
}
///
/// Gets or sets the fog end distance.
///
public float FogEnd
{
get { return fogEnd; }
set
{
fogEnd = value;
dirtyFlags |= EffectDirtyFlags.Fog;
}
}
///
/// Gets or sets the fog color.
///
public Vector3 FogColor
{
get { return fogColorParam.GetValueVector3(); }
set { fogColorParam.SetValue(value); }
}
///
/// Gets or sets the current texture.
///
public Texture2D Texture
{
get { return textureParam.GetValueTexture2D(); }
set { textureParam.SetValue(value); }
}
///
/// Gets or sets whether vertex color is enabled.
///
public bool VertexColorEnabled
{
get { return vertexColorEnabled; }
set
{
if (vertexColorEnabled != value)
{
vertexColorEnabled = value;
dirtyFlags |= EffectDirtyFlags.ShaderIndex;
}
}
}
///
/// Gets or sets the alpha compare function (default Greater).
///
public CompareFunction AlphaFunction
{
get { return alphaFunction; }
set
{
alphaFunction = value;
dirtyFlags |= EffectDirtyFlags.AlphaTest;
}
}
///
/// Gets or sets the reference alpha value (default 0).
///
public int ReferenceAlpha
{
get { return referenceAlpha; }
set
{
referenceAlpha = value;
dirtyFlags |= EffectDirtyFlags.AlphaTest;
}
}
#endregion
#region Methods
///
/// Creates a new AlphaTestEffect with default parameter settings.
///
public AlphaTestEffect(GraphicsDevice device)
: base(device, Resources.AlphaTestEffect)
{
CacheEffectParameters();
}
///
/// Creates a new AlphaTestEffect by cloning parameter settings from an existing instance.
///
protected AlphaTestEffect(AlphaTestEffect cloneSource)
: base(cloneSource)
{
CacheEffectParameters();
fogEnabled = cloneSource.fogEnabled;
vertexColorEnabled = cloneSource.vertexColorEnabled;
world = cloneSource.world;
view = cloneSource.view;
projection = cloneSource.projection;
diffuseColor = cloneSource.diffuseColor;
alpha = cloneSource.alpha;
fogStart = cloneSource.fogStart;
fogEnd = cloneSource.fogEnd;
alphaFunction = cloneSource.alphaFunction;
referenceAlpha = cloneSource.referenceAlpha;
}
///
/// Creates a clone of the current AlphaTestEffect instance.
///
public override Effect Clone()
{
return new AlphaTestEffect(this);
}
///
/// Looks up shortcut references to our effect parameters.
///
void CacheEffectParameters()
{
textureParam = Parameters["Texture"];
diffuseColorParam = Parameters["DiffuseColor"];
alphaTestParam = Parameters["AlphaTest"];
fogColorParam = Parameters["FogColor"];
fogVectorParam = Parameters["FogVector"];
worldViewProjParam = Parameters["WorldViewProj"];
shaderIndexParam = Parameters["ShaderIndex"];
}
///
/// Lazily computes derived parameter values immediately before applying the effect.
///
protected internal override void OnApply()
{
// Recompute the world+view+projection matrix or fog vector?
dirtyFlags = EffectHelpers.SetWorldViewProjAndFog(dirtyFlags, ref world, ref view, ref projection, ref worldView, fogEnabled, fogStart, fogEnd, worldViewProjParam, fogVectorParam);
// Recompute the diffuse/alpha material color parameter?
if ((dirtyFlags & EffectDirtyFlags.MaterialColor) != 0)
{
diffuseColorParam.SetValue(new Vector4(diffuseColor * alpha, alpha));
dirtyFlags &= ~EffectDirtyFlags.MaterialColor;
}
// Recompute the alpha test settings?
if ((dirtyFlags & EffectDirtyFlags.AlphaTest) != 0)
{
Vector4 alphaTest = new Vector4();
bool eqNe = false;
// Convert reference alpha from 8 bit integer to 0-1 float format.
float reference = (float)referenceAlpha / 255f;
// Comparison tolerance of half the 8 bit integer precision.
const float threshold = 0.5f / 255f;
switch (alphaFunction)
{
case CompareFunction.Less:
// Shader will evaluate: clip((a < x) ? z : w)
alphaTest.X = reference - threshold;
alphaTest.Z = 1;
alphaTest.W = -1;
break;
case CompareFunction.LessEqual:
// Shader will evaluate: clip((a < x) ? z : w)
alphaTest.X = reference + threshold;
alphaTest.Z = 1;
alphaTest.W = -1;
break;
case CompareFunction.GreaterEqual:
// Shader will evaluate: clip((a < x) ? z : w)
alphaTest.X = reference - threshold;
alphaTest.Z = -1;
alphaTest.W = 1;
break;
case CompareFunction.Greater:
// Shader will evaluate: clip((a < x) ? z : w)
alphaTest.X = reference + threshold;
alphaTest.Z = -1;
alphaTest.W = 1;
break;
case CompareFunction.Equal:
// Shader will evaluate: clip((abs(a - x) < Y) ? z : w)
alphaTest.X = reference;
alphaTest.Y = threshold;
alphaTest.Z = 1;
alphaTest.W = -1;
eqNe = true;
break;
case CompareFunction.NotEqual:
// Shader will evaluate: clip((abs(a - x) < Y) ? z : w)
alphaTest.X = reference;
alphaTest.Y = threshold;
alphaTest.Z = -1;
alphaTest.W = 1;
eqNe = true;
break;
case CompareFunction.Never:
// Shader will evaluate: clip((a < x) ? z : w)
alphaTest.Z = -1;
alphaTest.W = -1;
break;
case CompareFunction.Always:
default:
// Shader will evaluate: clip((a < x) ? z : w)
alphaTest.Z = 1;
alphaTest.W = 1;
break;
}
alphaTestParam.SetValue(alphaTest);
dirtyFlags &= ~EffectDirtyFlags.AlphaTest;
// If we changed between less/greater vs. equal/notequal
// compare modes, we must also update the shader index.
if (isEqNe != eqNe)
{
isEqNe = eqNe;
dirtyFlags |= EffectDirtyFlags.ShaderIndex;
}
}
// Recompute the shader index?
if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0)
{
int shaderIndex = 0;
if (!fogEnabled)
shaderIndex += 1;
if (vertexColorEnabled)
shaderIndex += 2;
if (isEqNe)
shaderIndex += 4;
shaderIndexParam.SetValue(shaderIndex);
dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
}
}
#endregion
}
}