using System; using System.Collections.Generic; using System.Linq; using System.Text; using Aiwaz.Contracts; using Aiwaz.Common; using Aiwaz.Core; using Aiwaz.Resources.Attributes; using System.Collections.ObjectModel; using System.IO; namespace Aiwaz.Resources { public class TechniqueNotFoundException : Exception { } public class UnableToLoadShaderException : Exception { public UnableToLoadShaderException(string errors) : base(errors) { } } [CreationParameters("Load a shader from file")] public class ShaderParams : ICreationParams { [RequiredParameter] public string FileName; public string TechniqueName; public ShaderParams() { FileName = null; TechniqueName = null; } } [AiwazResource("Shader", "A complex shader to perform individual shading operations when rendering geometry buffers.")] public class Shader : CommandUser, IShader { #region IShader Members const byte applyFirstPassCommandType = 0; const byte nextPassCommandType = 1; private InternalShader internalShader; private string techniqueName; private byte priority = 0; public bool Changed { get; set; } public Shader(ShaderParams parameters) { creationParams = parameters; Console.WriteLine(string.Format("Creating Shader ({0} Technique: {1})..", parameters.FileName, parameters.TechniqueName)); internalShader = Engine.FindNamedObject(parameters.FileName) as InternalShader; if (internalShader == null) internalShader = new InternalShader(parameters.FileName); internalShader.OnChanged += internalShader_OnChanged; Commands.Add(new Command(this, CommandFlags.StartChain | CommandFlags.SubChainStart, applyFirstPassCommandType, 2, this.Priority)); Commands.Add(new Command(this, CommandFlags.SubChainEnd, nextPassCommandType, byte.MaxValue, this.Priority)); this.TechniqueName = creationParams.TechniqueName; } void internalShader_OnChanged(object sender, EventArgs e) { this.TechniqueName = creationParams.TechniqueName; Changed = true; } public string TechniqueName { get { return techniqueName; } set { techniqueName = value; if (string.IsNullOrEmpty(techniqueName)) { this.Technique = internalShader.Effect.GetTechniqueByIndex(0); if (this.Technique != null) techniqueName = this.Technique.Description.Name; } else this.Technique = internalShader.Effect.GetTechniqueByName(TechniqueName); if (this.Technique == null) throw new TechniqueNotFoundException(); CurrentPass = 0; } } public SlimDX.Direct3D10.EffectTechnique Technique { get; protected set; } public SlimDX.Direct3D10.Effect Effect { get { return internalShader.Effect; } } public byte Priority { get { return priority; } set { priority = value; foreach (var command in Commands) if (command.Type == applyFirstPassCommandType) command.SubPriority = priority; } } public int CurrentPass { get; protected set; } #endregion public override CommandExecuteResult ExecuteCommand(byte commandType, CommandBuffer currentBuffer, int currentPositon) { if (commandType == applyFirstPassCommandType) { CurrentPass = 0; Engine.EngineStates.LastShader = this; } else if (commandType == nextPassCommandType) { if (this.Technique == null) return CommandExecuteResult.None; if (CurrentPass >= this.Technique.Description.PassCount) CurrentPass = this.Technique.Description.PassCount; else CurrentPass++; if (CurrentPass >= this.Technique.Description.PassCount) return CommandExecuteResult.None; return CommandExecuteResult.RetrySubChainSkipHead; } return CommandExecuteResult.None; } #region IDisposable Members public override void Dispose() { Engine.EngineStates.LastShader = null; base.Dispose(); } #endregion private ShaderParams creationParams; public override ICreationParams CreationParams { get { return creationParams; } } public override ObservableCollection Children { get { return null; } } } internal class InternalShader : IDisposable { #region IInternalShader Members private string fileName; public InternalShader(string fileName) { this.fileName = fileName; Engine.RegisterNamedObject(fileName, this); Engine.RegisterEngineDisposable(this); Engine.FileSystem.Attach(fileName, OnShaderChanged); } public event EventHandler OnChanged; private void OnShaderChanged(Stream stream) { using (stream) { if (Effect != null) Effect.Dispose(); string errors; Effect = SlimDX.Direct3D10.Effect.FromStream(Engine.Device, stream, "fx_4_0", #if DEBUG SlimDX.Direct3D10.ShaderFlags.EnableStrictness | SlimDX.Direct3D10.ShaderFlags.Debug, #else SlimDX.Direct3D10.ShaderFlags.EnableStrictness, #endif SlimDX.Direct3D10.EffectFlags.None, null, null, out errors); if (!string.IsNullOrEmpty(errors)) throw new UnableToLoadShaderException(errors); else if (OnChanged != null) OnChanged(this, EventArgs.Empty); } } ~InternalShader() { if (!Engine.Initialized) return; Engine.UnregisterNamedObject(fileName); if (Engine.UnregisterEngineDisposable(this)) this.Dispose(); } public SlimDX.Direct3D10.Effect Effect { get; protected set; } #endregion #region IDisposable Members public void Dispose() { if (this.Effect != null) this.Effect.Dispose(); this.Effect = null; Engine.UnregisterNamedObject(fileName); Engine.UnregisterEngineDisposable(this); } #endregion } }