/* * RenderQueue.cs * Authors: August Zinsser * * Copyright August Zinsser 2007 * This program is distributed under the terms of the GNU General Public License */ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Pina3D.Particles; namespace Pina3D { /// /// Manages a "queue" of all ColladaModels and particles to be rendered. Automatically sorts the queue for optimal render time. /// public class RenderQueue { public enum BlendMode { Alpha, Additive }; protected Dictionary> mRenderables; // Holds "subqueues" sorted by vertex type to minimize vertex delcarations protected List mParticlesAlphaBlend; protected List mParticlesAdditiveBlend; public int Count { get { int runningTotal = 0; foreach (KeyValuePair> kvp in mRenderables) { runningTotal += kvp.Value.Count; } return runningTotal; } } /// /// Allocates internal variables /// public RenderQueue() { mRenderables = new Dictionary>(); mParticlesAlphaBlend = new List(); mParticlesAdditiveBlend = new List(); } /// /// Tells each model and particle in the render queue to render itself /// public void Render() { Pina.Graphics.GraphicsDevice.RenderState.DepthBufferEnable = true; Pina.Graphics.GraphicsDevice.RenderState.DepthBufferWriteEnable = true; Pina.Graphics.GraphicsDevice.RenderState.AlphaBlendEnable = false; //Pina.Graphics.GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add; //Pina.Graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha; //Pina.Graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha; //Pina.Graphics.GraphicsDevice.RenderState.SeparateAlphaBlendEnabled = false; foreach (KeyValuePair> kvp in mRenderables) { // TODO: Implement IComparable and then sort the renderables (optimization) // TODO: Store the sourceData of each Renderable and then render each sourceData once with a list of each Renderable's PinaState (optimization) // TODO: Support transparent geometry (new feature) for (int i = 0; i < kvp.Value.Count; i++) { kvp.Value[i].Render(); } } // "Quality" Alpha Blended (requires back-to-front draw order) //Pina.Graphics.GraphicsDevice.RenderState.DepthBufferEnable = true; //Pina.Graphics.GraphicsDevice.RenderState.DepthBufferWriteEnable = true; //Pina.Graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true; //Pina.Graphics.GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add; //Pina.Graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha; //Pina.Graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha; //Pina.Graphics.GraphicsDevice.RenderState.SeparateAlphaBlendEnabled = false; // "Fast" Alpha Blended (does NOT require back-to-front draw order, but some particles may occaisonally falsely occlude other objects. Draw them last to minimize this) Pina.Graphics.GraphicsDevice.RenderState.DepthBufferEnable = true; Pina.Graphics.GraphicsDevice.RenderState.DepthBufferWriteEnable = false; Pina.Graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true; Pina.Graphics.GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add; Pina.Graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha; Pina.Graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha; Pina.Graphics.GraphicsDevice.RenderState.SeparateAlphaBlendEnabled = false; for (int i = 0; i < mParticlesAlphaBlend.Count; i++) { mParticlesAlphaBlend[i].Render(); } QuadRenderer.RenderAllNow(); // Additive Blended Pina.Graphics.GraphicsDevice.RenderState.DepthBufferEnable = true; Pina.Graphics.GraphicsDevice.RenderState.DepthBufferWriteEnable = false; Pina.Graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true; Pina.Graphics.GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add; Pina.Graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha; Pina.Graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.One; Pina.Graphics.GraphicsDevice.RenderState.SeparateAlphaBlendEnabled = false; for (int i = 0; i < mParticlesAdditiveBlend.Count; i++) { mParticlesAdditiveBlend[i].Render(); } QuadRenderer.RenderAllNow(); } /// /// Tells the specified collada model to render itself each render cycle /// /// public void Add(Renderable renderable) { string vertDec = "defaultDec"; // Is this a new vertex delcaration? if (!mRenderables.ContainsKey(vertDec)) mRenderables.Add(vertDec, new List()); // Add to the appropriate sublist mRenderables[vertDec].Add(renderable); } /// /// Tells the specified particle to render itself each render cycle /// /// public void Add(Particle particle, BlendMode blendMode) { if (blendMode == BlendMode.Alpha) mParticlesAlphaBlend.Add(particle); else mParticlesAdditiveBlend.Add(particle); } /// /// Removes the specified renderable from the render queue /// /// true if the model was found and removed, false otherwise public bool Remove(Renderable renderable) { string vertDec = "defaultDec"; // Does this vertex declaration even exist? if (!mRenderables.ContainsKey(vertDec)) return false; // Try to remove the model return mRenderables[vertDec].Remove(renderable); } /// /// Removes the specified particle from the render queue /// /// /// true if the model was found and removed, false otherwise public bool Remove(Particle particle) { // Try to remove the particle if (mParticlesAlphaBlend.Contains(particle)) return mParticlesAlphaBlend.Remove(particle); else return mParticlesAdditiveBlend.Remove(particle); } /// /// Removes all models and particles from the render queue /// public void Flush() { mRenderables.Clear(); mParticlesAlphaBlend.Clear(); mParticlesAdditiveBlend.Clear(); } /// /// Removes particles from the render queue, but leaves models /// public void FlushParticles() { mParticlesAlphaBlend.Clear(); mParticlesAdditiveBlend.Clear(); } } }