#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 } }