/// <summary>
/// Internal method for splitting the passes into illumination passes.
/// </summary>
public void CompileIlluminationPasses()
{
ClearIlluminationPasses();
// don't need to split transparent passes since they are rendered seperately
if(this.IsTransparent) {
return;
}
// start off with ambient passes
IlluminationStage stage = IlluminationStage.Ambient;
bool hasAmbient = false;
for(int i = 0; i < passes.Count; /* increment in logic */) {
Pass pass = (Pass)passes[i];
IlluminationPass iPass;
switch(stage) {
case IlluminationStage.Ambient:
// keep looking for ambient only
if(pass.IsAmbientOnly) {
iPass = new IlluminationPass();
iPass.OriginalPass = pass;
iPass.Pass = pass;
iPass.Stage = stage;
illuminationPasses.Add(iPass);
hasAmbient = true;
// progress to the next pass
i++;
}
else {
// split off any ambient part
if(pass.Ambient.CompareTo(ColorEx.Black) != 0 ||
pass.Emissive.CompareTo(ColorEx.Black) != 0 ||
pass.AlphaRejectFunction != CompareFunction.AlwaysPass) {
Pass newPass = new Pass(this, pass.Index);
pass.CopyTo(newPass);
if (newPass.AlphaRejectFunction != CompareFunction.AlwaysPass) {
// Alpha rejection passes must retain their transparency, so
// we allow the texture units, but override the colour functions
for (int tindex = 0; tindex < newPass.NumTextureUnitStages; tindex++)
{
TextureUnitState tus = newPass.GetTextureUnitState(tindex);
tus.SetColorOperationEx(LayerBlendOperationEx.Source1, LayerBlendSource.Current, LayerBlendSource.Current);
}
}
else
// remove any texture units
newPass.RemoveAllTextureUnitStates();
// also remove any fragment program
if(newPass.HasFragmentProgram) {
newPass.SetFragmentProgram("");
}
// We have to leave vertex program alone (if any) and
// just trust that the author is using light bindings, which
// we will ensure there are none in the ambient pass
newPass.Diffuse = new ColorEx(newPass.Diffuse.a, 0f, 0f, 0f); // Preserving alpha
newPass.Specular = ColorEx.Black;
// Calculate hash value for new pass, because we are compiling
// illumination passes on demand, which will loss hash calculate
// before it add to render queue first time.
newPass.RecalculateHash();
iPass = new IlluminationPass();
iPass.DestroyOnShutdown = true;
iPass.OriginalPass = pass;
iPass.Pass = newPass;
iPass.Stage = stage;
illuminationPasses.Add(iPass);
hasAmbient = true;
}
if(!hasAmbient) {
// make up a new basic pass
Pass newPass = new Pass(this, pass.Index);
pass.CopyTo(newPass);
newPass.Ambient = ColorEx.Black;
newPass.Diffuse = ColorEx.Black;
// Calculate hash value for new pass, because we are compiling
// illumination passes on demand, which will loss hash calculate
// before it add to render queue first time.
newPass.RecalculateHash();
iPass = new IlluminationPass();
iPass.DestroyOnShutdown = true;
iPass.OriginalPass = pass;
iPass.Pass = newPass;
iPass.Stage = stage;
illuminationPasses.Add(iPass);
hasAmbient = true;
}
// this means we are done with ambients, progress to per-light
stage = IlluminationStage.PerLight;
}
break;
case IlluminationStage.PerLight:
if(pass.RunOncePerLight) {
// if this is per-light already, use it directly
iPass = new IlluminationPass();
iPass.DestroyOnShutdown = false;
iPass.OriginalPass = pass;
iPass.Pass = pass;
iPass.Stage = stage;
illuminationPasses.Add(iPass);
// progress to the next pass
i++;
}
else {
// split off per-light details (can only be done for one)
if(pass.LightingEnabled &&
(pass.Diffuse.CompareTo(ColorEx.Black) != 0 ||
pass.Specular.CompareTo(ColorEx.Black) != 0)) {
// copy existing pass
Pass newPass = new Pass(this, pass.Index);
pass.CopyTo(newPass);
if (newPass.AlphaRejectFunction != CompareFunction.AlwaysPass) {
// Alpha rejection passes must retain their transparency, so
// we allow the texture units, but override the colour functions
for (int tindex = 0; tindex < newPass.NumTextureUnitStages; tindex++)
{
TextureUnitState tus = newPass.GetTextureUnitState(tindex);
tus.SetColorOperationEx(LayerBlendOperationEx.Source1, LayerBlendSource.Current, LayerBlendSource.Current);
}
}
else
// remove any texture units
newPass.RemoveAllTextureUnitStates();
// also remove any fragment program
if(newPass.HasFragmentProgram) {
newPass.SetFragmentProgram("");
}
// Cannot remove vertex program, have to assume that
// it will process diffuse lights, ambient will be turned off
newPass.Ambient = ColorEx.Black;
newPass.Emissive = ColorEx.Black;
// must be additive
newPass.SetSceneBlending(SceneBlendFactor.One, SceneBlendFactor.One);
iPass = new IlluminationPass();
iPass.DestroyOnShutdown = true;
iPass.OriginalPass = pass;
iPass.Pass = newPass;
iPass.Stage = stage;
illuminationPasses.Add(iPass);
}
// This means the end of per-light passes
stage = IlluminationStage.Decal;
}
break;
case IlluminationStage.Decal:
// We just want a 'lighting off' pass to finish off
// and only if there are texture units
if(pass.NumTextureUnitStages > 0) {
if(!pass.LightingEnabled) {
// we assume this pass already combines as required with the scene
iPass = new IlluminationPass();
iPass.DestroyOnShutdown = false;
iPass.OriginalPass = pass;
iPass.Pass = pass;
iPass.Stage = stage;
illuminationPasses.Add(iPass);
}
else {
// Copy the pass and tweak away the lighting parts
Pass newPass = new Pass(this, pass.Index);
pass.CopyTo(newPass);
newPass.Ambient = ColorEx.Black;
newPass.Diffuse = new ColorEx(newPass.Diffuse.a, 0f, 0f, 0f); // Preserving alpha
newPass.Specular = ColorEx.Black;
newPass.Emissive = ColorEx.Black;
newPass.LightingEnabled = false;
// modulate
newPass.SetSceneBlending(SceneBlendFactor.DestColor, SceneBlendFactor.Zero);
// Calculate hash value for new pass, because we are compiling
// illumination passes on demand, which will loss hash calculate
// before it add to render queue first time.
newPass.RecalculateHash();
// there is nothing we can do about vertex & fragment
// programs here, so people will just have to make their
// programs friendly-like if they want to use this technique
iPass = new IlluminationPass();
iPass.DestroyOnShutdown = true;
iPass.OriginalPass = pass;
iPass.Pass = newPass;
iPass.Stage = stage;
illuminationPasses.Add(iPass);
}
}
// always increment on decal, since nothing more to do with this pass
i++;
break;
}
}
}