// Project: XnaGraphicEngine, File: ShaderEffect.cs
// Namespace: XnaGraphicEngine.Shaders, Class: ShaderEffect
// Path: C:\code\XnaGraphicEngine\Shaders, Author: Abi
// Code lines: 904, Size of file: 24,94 KB
// Creation date: 07.09.2006 05:56
// Last modified: 05.11.2006 00:32
// Generated with Commenter by abi.exDream.com
#region Using directives
#if DEBUG
//using NUnit.Framework;
#endif
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using XnaGraphicEngine.Graphics;
using XnaGraphicEngine.Helpers;
using Texture = XnaGraphicEngine.Graphics.Texture;
using XnaTexture = Microsoft.Xna.Framework.Graphics.Texture;
using XnaGraphicEngine.Game;
#endregion
namespace XnaGraphicEngine.Shaders
{
///
/// Shader effect class. You can either directly use this class by
/// providing a fx filename in the constructor or derive from this class
/// for special shader functionality (see post screen shaders for a more
/// complex example).
///
public class ShaderEffect : IGraphicContent
{
#region Some shaders
///
/// Line rendering shader
///
public static ShaderEffect lineRendering =
new ShaderEffect("LineRendering.fx");
#endregion
#region Variables
///
/// Content name for this shader
///
private string shaderContentName = "";
///
/// Effect
///
protected Effect effect = null;
///
/// Effect handles for shaders.
///
protected EffectParameter worldViewProj;
#endregion
#region Properties
///
/// Is this shader valid to render? If not we can't perform any rendering.
///
/// Bool
public bool Valid
{
get
{
return effect != null;
} // get
} // Valid
///
/// Effect
///
/// Effect
public Effect Effect
{
get
{
return effect;
} // get
} // Effect
///
/// Number of techniques
///
/// Int
public int NumberOfTechniques
{
get
{
return effect.Techniques.Count;
} // get
} // NumberOfTechniques
///
/// Get technique
///
/// Technique name
/// Effect technique
public EffectTechnique GetTechnique(string techniqueName)
{
return effect.Techniques[techniqueName];
} // GetTechnique(techniqueName)
///
/// Set value helper to set an effect parameter.
///
/// Param
/// Set matrix
private void SetValue(EffectParameter param,
ref Matrix lastUsedMatrix, Matrix newMatrix)
{
/*obs, always update, matrices change every frame anyway!
* matrix compare takes too long, it eats up almost 50% of this method.
if (param != null &&
lastUsedMatrix != newMatrix)
*/
{
lastUsedMatrix = newMatrix;
param.SetValue(newMatrix);
} // if (param)
} // SetValue(param, setMatrix)
///
/// Set value helper to set an effect parameter.
///
/// Param
/// Last used vector
/// New vector
private void SetValue(EffectParameter param,
ref Vector3 lastUsedVector, Vector3 newVector)
{
if (param != null &&
lastUsedVector != newVector)
{
lastUsedVector = newVector;
param.SetValue(newVector);
} // if (param)
} // SetValue(param, lastUsedVector, newVector)
///
/// Set value helper to set an effect parameter.
///
/// Param
/// Last used color
/// New color
private void SetValue(EffectParameter param,
ref Color lastUsedColor, Color newColor)
{
// Note: This check eats few % of the performance, but the color
// often stays the change (around 50%).
if (param != null &&
//slower: lastUsedColor != newColor)
lastUsedColor.PackedValue != newColor.PackedValue)
{
lastUsedColor = newColor;
//obs: param.SetValue(ColorHelper.ConvertColorToVector4(newColor));
param.SetValue(newColor.ToVector4());
} // if (param)
} // SetValue(param, lastUsedColor, newColor)
///
/// Set value helper to set an effect parameter.
///
/// Param
/// Last used value
/// New value
private void SetValue(EffectParameter param,
ref float lastUsedValue, float newValue)
{
if (param != null &&
lastUsedValue != newValue)
{
lastUsedValue = newValue;
param.SetValue(newValue);
} // if (param)
} // SetValue(param, lastUsedValue, newValue)
///
/// Set value helper to set an effect parameter.
///
/// Param
/// Last used value
/// New value
private void SetValue(EffectParameter param,
ref XnaTexture lastUsedValue, XnaTexture newValue)
{
if (param != null &&
lastUsedValue != newValue)
{
lastUsedValue = newValue;
param.SetValue(newValue);
} // if (param)
} // SetValue(param, lastUsedValue, newValue)
protected Matrix lastUsedWorldViewProjMatrix = Matrix.Identity;
///
/// Set world view proj matrix
///
protected Matrix WorldViewProjMatrix
{
set
{
SetValue(worldViewProj, ref lastUsedWorldViewProjMatrix, value);
} // set
} // WorldViewProjMatrix
#endregion
#region Constructor
public ShaderEffect(string shaderName)
{
if (BaseGame.Device == null)
throw new NullReferenceException(
"XNA device is not initialized, can't create ShaderEffect.");
shaderContentName = StringHelper.ExtractFilename(shaderName, true);
Load();
BaseGame.RegisterGraphicContentObject(this);
} // SimpleShader()
#endregion
#region Dispose
///
/// Dispose
///
public virtual void Dispose()
{
// Dispose shader effect
if (effect != null)
effect.Dispose();
effect = null;
} // Dispose()
#endregion
#region Load effect
///
/// Reload effect (can be useful if we change the fx file dynamically).
///
public void Load()
{
/*obs
// Dispose old shader
if (effect != null)
Dispose();
*/
// Load shader
try
{
// We have to try, there is no "Exists" method.
// We could try to check the xnb filename, but why bother? ^^
effect = BaseGame.Content.Load(
Path.Combine(Directories.ContentDirectory, shaderContentName));
} // try
#if XBOX360
catch (Exception ex)
{
Log.Write("Failed to load shader "+shaderContentName+". " +
"Error: " + ex.ToString());
// Rethrow error, app can't continue!
throw ex;
}
#else
catch
{
// Try again by loading by filename (only allowed for windows!)
// Content file was most likely removed for easier testing :)
try
{
CompiledEffect compiledEffect = Effect.CompileEffectFromFile(
Path.Combine("Shaders", shaderContentName + ".fx"),
null, null, CompilerOptions.None,
TargetPlatform.Windows);
effect = new Effect(BaseGame.Device,
compiledEffect.GetEffectCode(), CompilerOptions.None, null);
} // try
catch (Exception ex)
{
Log.Write("Failed to load shader "+shaderContentName+". " +
"Error: " + ex.ToString());
// Rethrow error, app can't continue!
throw ex;
} // catch
} // catch
#endif
GetParameters();
} // Reload()
#endregion
#region Get parameters
///
/// Get parameters, override to support more
///
protected virtual void GetParameters()
{
worldViewProj = effect.Parameters["worldViewProj"];
} // GetParameters()
#endregion
#region SetParameters
///
/// Set parameters, override to set more
///
public virtual void SetParameters()
{
if (worldViewProj != null)
worldViewProj.SetValue(BaseGame.WorldViewProjectionMatrix);
} // SetParameters()
#endregion
#region Update
///
/// Update
///
public void Update()
{
effect.CommitChanges();
} // Update()
#endregion
#region Render
///
/// Render
///
/// Technique name
/// Render delegate
public void Render(string techniqueName,
BaseGame.RenderDelegate renderDelegate)
{
if (effect == null)
return;
SetParameters();
/*will become important later in the book.
// Can we do the requested technique?
// For graphic cards not supporting ps2.0, fall back to ps1.1
if (BaseGame.CanUsePS20 == false &&
techniqueName.EndsWith("20"))
// Use same technique without the 20 ending!
techniqueName = techniqueName.Substring(0, techniqueName.Length - 2);
*/
// Start shader
effect.CurrentTechnique = effect.Techniques[techniqueName];
effect.Begin(SaveStateMode.None);
// Render all passes (usually just one)
//foreach (EffectPass pass in effect.CurrentTechnique.Passes)
for (int num = 0; num < effect.CurrentTechnique.Passes.Count; num++)
{
EffectPass pass = effect.CurrentTechnique.Passes[num];
pass.Begin();
renderDelegate();
pass.End();
} // foreach (pass)
// End shader
effect.End();
} // Render(passName, renderDelegate)
#endregion
#region Render single pass shader
///
/// Render single pass shader, little faster and simpler than
/// Render and it just uses the current technique and renderes only
/// the first pass (most shaders have only 1 pass anyway).
/// Used for MeshRenderManager!
///
/// Render delegate
public void RenderSinglePassShader(
BaseGame.RenderDelegate renderDelegate)
{
if (effect == null)
return;
// Start effect (current technique should be set)
effect.Begin(SaveStateMode.None);
// Start first pass
effect.CurrentTechnique.Passes[0].Begin();
// Render
renderDelegate();
// End pass and shader
effect.CurrentTechnique.Passes[0].End();
effect.End();
} // RenderSinglePassShader(renderDelegate)
#endregion
} // class ShaderEffect
} // namespace XnaGraphicEngine.Shaders