Files
bluflame/aiwaz/Aiwaz.Resources/Shader.cs
2026-04-18 22:31:51 +02:00

233 lines
6.9 KiB
C#

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