#region File Description //----------------------------------------------------------------------------- // EnvironmentMapEffect.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; #endregion namespace Microsoft.Xna.Framework.Graphics { /// /// Built-in effect that supports environment mapping. /// public class EnvironmentMapEffect : Effect, IEffectMatrices, IEffectLights, IEffectFog { #region Effect Parameters EffectParameter textureParam; EffectParameter environmentMapParam; EffectParameter environmentMapAmountParam; EffectParameter environmentMapSpecularParam; EffectParameter fresnelFactorParam; EffectParameter diffuseColorParam; EffectParameter emissiveColorParam; EffectParameter eyePositionParam; EffectParameter fogColorParam; EffectParameter fogVectorParam; EffectParameter worldParam; EffectParameter worldInverseTransposeParam; EffectParameter worldViewProjParam; EffectParameter shaderIndexParam; #endregion #region Fields bool oneLight; bool fogEnabled; bool fresnelEnabled; bool specularEnabled; Matrix world = Matrix.Identity; Matrix view = Matrix.Identity; Matrix projection = Matrix.Identity; Matrix worldView; Vector3 diffuseColor = Vector3.One; Vector3 emissiveColor = Vector3.Zero; Vector3 ambientLightColor = Vector3.Zero; float alpha = 1; DirectionalLight light0; DirectionalLight light1; DirectionalLight light2; float fogStart = 0; float fogEnd = 1; 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.World | EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.Fog; } } /// /// Gets or sets the view matrix. /// public Matrix View { get { return view; } set { view = value; dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.EyePosition | 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 emissive color (range 0 to 1). /// public Vector3 EmissiveColor { get { return emissiveColor; } set { emissiveColor = 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 ambient light color (range 0 to 1). /// public Vector3 AmbientLightColor { get { return ambientLightColor; } set { ambientLightColor = value; dirtyFlags |= EffectDirtyFlags.MaterialColor; } } /// /// Gets the first directional light. /// public DirectionalLight DirectionalLight0 { get { return light0; } } /// /// Gets the second directional light. /// public DirectionalLight DirectionalLight1 { get { return light1; } } /// /// Gets the third directional light. /// public DirectionalLight DirectionalLight2 { get { return light2; } } /// /// 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 the current environment map texture. /// public TextureCube EnvironmentMap { get { return environmentMapParam.GetValueTextureCube(); } set { environmentMapParam.SetValue(value); } } /// /// Gets or sets the amount of the environment map RGB that will be blended over /// the base texture. Range 0 to 1, default 1. If set to zero, the RGB channels /// of the environment map will completely ignored (but the environment map alpha /// may still be visible if EnvironmentMapSpecular is greater than zero). /// public float EnvironmentMapAmount { get { return environmentMapAmountParam.GetValueSingle(); } set { environmentMapAmountParam.SetValue(value); } } /// /// Gets or sets the amount of the environment map alpha channel that will /// be added to the base texture. Range 0 to 1, default 0. This can be used /// to implement cheap specular lighting, by encoding one or more specular /// highlight patterns into the environment map alpha channel, then setting /// EnvironmentMapSpecular to the desired specular light color. /// public Vector3 EnvironmentMapSpecular { get { return environmentMapSpecularParam.GetValueVector3(); } set { environmentMapSpecularParam.SetValue(value); bool enabled = (value != Vector3.Zero); if (specularEnabled != enabled) { specularEnabled = enabled; dirtyFlags |= EffectDirtyFlags.ShaderIndex; } } } /// /// Gets or sets the Fresnel factor used for the environment map blending. /// Higher values make the environment map only visible around the silhouette /// edges of the object, while lower values make it visible everywhere. /// Setting this property to 0 disables Fresnel entirely, making the /// environment map equally visible regardless of view angle. The default is /// 1. Fresnel only affects the environment map RGB (the intensity of which is /// controlled by EnvironmentMapAmount). The alpha contribution (controlled by /// EnvironmentMapSpecular) is not affected by the Fresnel setting. /// public float FresnelFactor { get { return fresnelFactorParam.GetValueSingle(); } set { fresnelFactorParam.SetValue(value); bool enabled = (value != 0); if (fresnelEnabled != enabled) { fresnelEnabled = enabled; dirtyFlags |= EffectDirtyFlags.ShaderIndex; } } } /// /// This effect requires lighting, so we explicitly implement /// IEffectLights.LightingEnabled, and do not allow turning it off. /// bool IEffectLights.LightingEnabled { get { return true; } set { if (!value) throw new NotSupportedException("EnvironmentMapEffect does not support setting LightingEnabled to false."); } } #endregion #region Methods /// /// Creates a new EnvironmentMapEffect with default parameter settings. /// public EnvironmentMapEffect(GraphicsDevice device) : base(device, Resources.EnvironmentMapEffect) { CacheEffectParameters(null); DirectionalLight0.Enabled = true; EnvironmentMapAmount = 1; EnvironmentMapSpecular = Vector3.Zero; FresnelFactor = 1; } /// /// Creates a new EnvironmentMapEffect by cloning parameter settings from an existing instance. /// protected EnvironmentMapEffect(EnvironmentMapEffect cloneSource) : base(cloneSource) { CacheEffectParameters(cloneSource); fogEnabled = cloneSource.fogEnabled; fresnelEnabled = cloneSource.fresnelEnabled; specularEnabled = cloneSource.specularEnabled; world = cloneSource.world; view = cloneSource.view; projection = cloneSource.projection; diffuseColor = cloneSource.diffuseColor; emissiveColor = cloneSource.emissiveColor; ambientLightColor = cloneSource.ambientLightColor; alpha = cloneSource.alpha; fogStart = cloneSource.fogStart; fogEnd = cloneSource.fogEnd; } /// /// Creates a clone of the current EnvironmentMapEffect instance. /// public override Effect Clone() { return new EnvironmentMapEffect(this); } /// /// Sets up the standard key/fill/back lighting rig. /// public void EnableDefaultLighting() { AmbientLightColor = EffectHelpers.EnableDefaultLighting(light0, light1, light2); } /// /// Looks up shortcut references to our effect parameters. /// void CacheEffectParameters(EnvironmentMapEffect cloneSource) { textureParam = Parameters["Texture"]; environmentMapParam = Parameters["EnvironmentMap"]; environmentMapAmountParam = Parameters["EnvironmentMapAmount"]; environmentMapSpecularParam = Parameters["EnvironmentMapSpecular"]; fresnelFactorParam = Parameters["FresnelFactor"]; diffuseColorParam = Parameters["DiffuseColor"]; emissiveColorParam = Parameters["EmissiveColor"]; eyePositionParam = Parameters["EyePosition"]; fogColorParam = Parameters["FogColor"]; fogVectorParam = Parameters["FogVector"]; worldParam = Parameters["World"]; worldInverseTransposeParam = Parameters["WorldInverseTranspose"]; worldViewProjParam = Parameters["WorldViewProj"]; shaderIndexParam = Parameters["ShaderIndex"]; light0 = new DirectionalLight(Parameters["DirLight0Direction"], Parameters["DirLight0DiffuseColor"], null, (cloneSource != null) ? cloneSource.light0 : null); light1 = new DirectionalLight(Parameters["DirLight1Direction"], Parameters["DirLight1DiffuseColor"], null, (cloneSource != null) ? cloneSource.light1 : null); light2 = new DirectionalLight(Parameters["DirLight2Direction"], Parameters["DirLight2DiffuseColor"], null, (cloneSource != null) ? cloneSource.light2 : null); } /// /// 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 world inverse transpose and eye position? dirtyFlags = EffectHelpers.SetLightingMatrices(dirtyFlags, ref world, ref view, worldParam, worldInverseTransposeParam, eyePositionParam); // Recompute the diffuse/emissive/alpha material color parameters? if ((dirtyFlags & EffectDirtyFlags.MaterialColor) != 0) { EffectHelpers.SetMaterialColor(true, alpha, ref diffuseColor, ref emissiveColor, ref ambientLightColor, diffuseColorParam, emissiveColorParam); dirtyFlags &= ~EffectDirtyFlags.MaterialColor; } // Check if we can use the only-bother-with-the-first-light shader optimization. bool newOneLight = !light1.Enabled && !light2.Enabled; if (oneLight != newOneLight) { oneLight = newOneLight; dirtyFlags |= EffectDirtyFlags.ShaderIndex; } // Recompute the shader index? if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0) { int shaderIndex = 0; if (!fogEnabled) shaderIndex += 1; if (fresnelEnabled) shaderIndex += 2; if (specularEnabled) shaderIndex += 4; if (oneLight) shaderIndex += 8; shaderIndexParam.SetValue(shaderIndex); dirtyFlags &= ~EffectDirtyFlags.ShaderIndex; } } #endregion } }