///<summary> /// Collect rendering passes. Here, passes are converted into render target operations /// and queued with queueRenderSystemOp. ///</summary> protected void CollectPasses(CompositorTargetOperation finalState, CompositionTargetPass target) { /// Here, passes are converted into render target operations Pass targetpass; Technique srctech; Material srcmat; foreach (CompositionPass pass in target.Passes) { switch (pass.Type) { case CompositorPassType.Clear: QueueRenderSystemOp(finalState, new RSClearOperation( pass.ClearBuffers, pass.ClearColor, pass.ClearDepth, pass.ClearStencil)); break; case CompositorPassType.Stencil: QueueRenderSystemOp(finalState, new RSStencilOperation( pass.StencilCheck, pass.StencilFunc, pass.StencilRefValue, pass.StencilMask, pass.StencilFailOp, pass.StencilDepthFailOp, pass.StencilPassOp, pass.StencilTwoSidedOperation )); break; case CompositorPassType.RenderScene: if ((int)pass.FirstRenderQueue < (int)finalState.CurrentQueueGroupID) { /// Mismatch -- warn user /// XXX We could support repeating the last queue, with some effort LogManager.Instance.Write("Warning in compilation of Compositor " + compositor.Name + ": Attempt to render queue " + pass.FirstRenderQueue + " before " + finalState.CurrentQueueGroupID); } /// Add render queues for (RenderQueueGroupID x = pass.FirstRenderQueue; x <= pass.LastRenderQueue; ++x) { Debug.Assert(x >= 0); finalState.RenderQueues[(int)x] = true; } finalState.CurrentQueueGroupID = (RenderQueueGroupID)((int)pass.LastRenderQueue + 1); finalState.FindVisibleObjects = true; finalState.MaterialScheme = target.MaterialScheme; break; case CompositorPassType.RenderQuad: srcmat = pass.Material; if (srcmat == null) { /// No material -- warn user LogManager.Instance.Write("Warning in compilation of Compositor " + compositor.Name + ": No material defined for composition pass"); break; } srcmat.Load(); if (srcmat.SupportedTechniques.Count == 0) { /// No supported techniques -- warn user LogManager.Instance.Write("Warning in compilation of Compositor " + compositor.Name + ": material " + srcmat.Name + " has no supported techniques"); break; } srctech = srcmat.GetBestTechnique(0); /// Create local material Material localMat = CreateLocalMaterial(); /// Copy and adapt passes from source material for (int i = 0; i < srctech.NumPasses; i++) { Pass srcpass = srctech.GetPass(i); /// Create new target pass targetpass = localMat.GetTechnique(0).CreatePass(); srcpass.CopyTo(targetpass); /// Set up inputs int numInputs = pass.GetNumInputs(); for (int x = 0; x < numInputs; x++) { string inp = pass.Inputs[x]; if (inp != string.Empty) { if (x < targetpass.NumTextureUnitStages) { targetpass.GetTextureUnitState(x).SetTextureName(GetSourceForTex(inp)); } else { /// Texture unit not there LogManager.Instance.Write("Warning in compilation of Compositor " + compositor.Name + ": material " + srcmat.Name + " texture unit " + x + " out of bounds"); } } } } QueueRenderSystemOp(finalState, new RSQuadOperation(this, pass.Identifier, localMat)); break; } } }
/// <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; } } }