// Project: XnaShooter, File: Billboard.cs // Namespace: XnaShooter.Graphics, Class: Billboard // Path: C:\code\XnaShooter\Graphics, Author: Abi // Code lines: 43, Size of file: 1,37 KB // Creation date: 27.12.2006 09:19 // Last modified: 27.12.2006 17:04 // Generated with Commenter by abi.exDream.com #region Using directives using System; using System.Collections.Generic; using System.Text; using System.Threading; using XnaShooter.Game; using XnaShooter.Helpers; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using XnaShooter.Shaders; using XnaTexture = Microsoft.Xna.Framework.Graphics.Texture2D; #endregion namespace XnaShooter.Graphics { /// /// Billboard /// class Billboard { #region Variables /// /// Right and up vectors for billboard calculations, will be /// set by Billboard.CalcVectors each frame (called always by Graphics)! /// internal static Vector3 vecRight = new Vector3(1.0f, 0.0f, 0.0f), vecUp = new Vector3(0.0f, 1.0f, 0.0f); /// /// For rendering billboard on ground (on z plane, e.g. for ring effect) /// internal static Vector3 // Do it on xy plane instead of xz plane (better visibility) vecGroundRight = new Vector3(0.0f, 1.0f, 0.0f), vecGroundUp = new Vector3(1.0f, 0.0f, 0.0f);//, //obs: vecGroundDepth = new Vector3(0.0f, 0.0f, 1.0f); /// /// Declaration for the billboard effect rendering process below! /// static VertexDeclaration decl = null; /// /// Make sure decl is not longer used if device got lost! /// static public void Dispose() { if (decl != null) decl.Dispose(); decl = null; billboards.Clear(); } // Dispose() /// /// Blend mode /// public enum BlendMode { /// /// Use normal alpha blending (src alpha, inv src alpha) /// NormalAlphaBlending, /// /// Light effect with help of destination color and inv src alpha. /// LightEffect, /// /// Additive blending effect, just adding color with one, one blending. /// AdditiveEffect, } // enum BlendMode /// /// Texture billboard list /// class TextureBillboardList { /// /// Tex /// public XnaTexture tex; /// /// Light blend mode, usually NormalAlphaBlending /// public BlendMode lightBlendMode; /// /// Billboard vertices /// public List vertices = new List(); /// /// Billboard indices /// public List indices = new List(); /// /// Create texture billboard list /// /// Set tex /// Set light blend mode public TextureBillboardList(XnaTexture setTex, BlendMode setLightBlendMode) { tex = setTex; lightBlendMode = setLightBlendMode; } // TextureBillboardList(setTex, setLightBlendMode) } // class TextureBillboardList static List billboards = new List(); /// /// Get texture billboard /// /// Tex /// Texture billboard list static private TextureBillboardList GetTextureBillboard( XnaTexture tex, BlendMode lightBlendMode) { if (billboards.Count > 0 && billboards[billboards.Count - 1].tex == tex && billboards[billboards.Count - 1].lightBlendMode == lightBlendMode) return billboards[billboards.Count - 1]; // Not found? Then create new and add to billboards list TextureBillboardList texBillboard = new TextureBillboardList(tex, lightBlendMode); billboards.Add(texBillboard); // Return result return texBillboard; } // GetTextureBillboard(tex) #endregion #region Calc vectors /// /// Calc vectors for billboards, will create helper vectors for /// billboard rendering, should just be called every frame. /// public static void CalcVectors() { // Only use the inverse view matrix, world matrix is assumed to be // Idendity, simply grab the values out of the inverse view matrix. Matrix invViewMatrix = BaseGame.InverseViewMatrix; vecRight = new Vector3( invViewMatrix.M11, invViewMatrix.M12, invViewMatrix.M13); vecUp = new Vector3( invViewMatrix.M21, invViewMatrix.M22, invViewMatrix.M23); } // CalcVectors() #endregion #region Render billboard /// /// Render billboards /// public static void RenderBillboards() { if (billboards.Count == 0) return; // Update billboard vectors CalcVectors(); // Reset world space BaseGame.WorldMatrix = Matrix.Identity; // Enable alpha blending BaseGame.EnableAlphaBlending(); // Disable z buffer to prevent effects to be disabling each other! BaseGame.Device.RenderState.DepthBufferEnable = true; BaseGame.Device.RenderState.DepthBufferWriteEnable = false; // Never go outside of billboard texture BaseGame.Device.SamplerStates[0].AddressU = TextureAddressMode.Clamp; BaseGame.Device.SamplerStates[0].AddressV = TextureAddressMode.Clamp; // No culling BaseGame.Device.RenderState.CullMode = CullMode.None; // Always use VertexPositionColorTexture format! if (decl == null) decl = new VertexDeclaration( BaseGame.Device, VertexPositionColorTexture.VertexElements); BaseGame.Device.VertexDeclaration = decl; BlendMode useLightBlendMode = BlendMode.NormalAlphaBlending; // Render all billboards ShaderEffect.effectShader.SetParametersOptimizedGeneral(); ShaderEffect.effectShader.Effect.Begin(); ShaderEffect.effectShader.Effect.CurrentTechnique.Passes[0].Begin(); //foreach (TextureBillboardList billboard in billboards) for (int num=0; num 0 && billboard.indices.Count > 0) { ShaderEffect.effectShader.SetParameters(billboard.tex); ShaderEffect.effectShader.Update(); if (billboard.lightBlendMode == BlendMode.LightEffect) { if (useLightBlendMode != BlendMode.LightEffect) { useLightBlendMode = BlendMode.LightEffect; BaseGame.Device.RenderState.SourceBlend = Blend.DestinationColor; BaseGame.Device.RenderState.DestinationBlend = Blend.SourceAlpha; } // if (useLightBlendMode) } // if (billboard.lightBlendMode) else if (billboard.lightBlendMode == BlendMode.AdditiveEffect) { if (useLightBlendMode != BlendMode.AdditiveEffect) { useLightBlendMode = BlendMode.AdditiveEffect; BaseGame.Device.RenderState.SourceBlend = Blend.SourceAlpha; BaseGame.Device.RenderState.DestinationBlend = Blend.One; } // if (useLightBlendMode) } // else if else { if (useLightBlendMode != BlendMode.NormalAlphaBlending) { useLightBlendMode = BlendMode.NormalAlphaBlending; BaseGame.Device.RenderState.SourceBlend = Blend.SourceAlpha; BaseGame.Device.RenderState.DestinationBlend = Blend.InverseSourceAlpha; } // if (useLightBlendMode) } // else //Log.Write("rendering " + billboard.indices.Count / 3 + // ", of type=" + billboard.tex); VertexPositionColorTexture[] vertices = billboard.vertices.ToArray(); short[] indices = billboard.indices.ToArray(); BaseGame.Device.DrawUserIndexedPrimitives( PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, indices.Length / 3); } // if (billboardVertices.Count) } // foreach (billboard) ShaderEffect.effectShader.Effect.CurrentTechnique.Passes[0].End(); ShaderEffect.effectShader.Effect.End(); //*/ // Remove all billboards billboards.Clear(); //BaseGame.Device.RenderState.AlphaTestEnable = false; BaseGame.Device.RenderState.DepthBufferEnable = true; BaseGame.Device.RenderState.DepthBufferWriteEnable = true; BaseGame.EnableAlphaBlending(); // Reset texturing BaseGame.Device.SamplerStates[0].AddressU = TextureAddressMode.Wrap; BaseGame.Device.SamplerStates[0].AddressV = TextureAddressMode.Wrap; } // EndRendering() #endregion #region Billboard rendering for effects /// /// Render 3D Billboard into scene. Used for 3d effects. /// Set texture before hand, this is required to support both /// normal textures and animated textures. /// /// Position in world space /// Size in world coordinates /// Rotation around billboard z axis /// Color, usually white public static void Render(XnaTexture tex, BlendMode lightBlendMode, Vector3 pos, float size, float rotation, Color col) { // Invisible? if (col.A == 0) return; TextureBillboardList texBillboard = GetTextureBillboard(tex, lightBlendMode); // Rotation Matrix rotMatrix = Matrix.CreateRotationZ(rotation) * BaseGame.InverseViewMatrix; Vector3 upLeft = new Vector3(-1, -1, 0); upLeft = Vector3.TransformNormal(upLeft, rotMatrix); Vector3 upRight = new Vector3(+1, -1, 0); upRight = Vector3.TransformNormal(upRight, rotMatrix); Vector3 downRight = new Vector3(+1, +1, 0); downRight = Vector3.TransformNormal(downRight, rotMatrix); Vector3 downLeft = new Vector3(-1, +1, 0); downLeft = Vector3.TransformNormal(downLeft, rotMatrix); Vector3 vec; vec = pos + upLeft * size; int index = texBillboard.vertices.Count; texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(0.0f, 0.0f))); vec = pos + downLeft * size; texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(0.0f, 1.0f))); vec = pos + downRight * size; texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(1.0f, 1.0f))); vec = pos + upRight * size; texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(1.0f, 0.0f))); texBillboard.indices.AddRange(new short[] { (short)(index+0), (short)(index+1), (short)(index+2), (short)(index+0), (short)(index+2), (short)(index+3), }); } // Render(tex, pos, size) /// /// Render /// /// Tex /// Position /// Size /// Rotation /// Color public static void Render(XnaTexture tex, Vector3 pos, float size, float rotation, Color col) { Render(tex, BlendMode.NormalAlphaBlending, pos, size, rotation, col); } // Render(tex, pos, size) /// /// Render /// /// Tex /// Position /// Size /// Rotation /// Color public static void Render(Texture tex, Vector3 pos, float size, float rotation, Color col) { Render(tex.XnaTexture, BlendMode.NormalAlphaBlending, pos, size, rotation, col); } // Render(tex, pos, size) /// /// Render /// /// Tex /// Animation step /// Position /// Size /// Rotation /// Color public static void Render(AnimatedTexture tex, int animationStep, BlendMode lightBlendMode, Vector3 pos, float size, float rotation, Color col) { Render(tex.GetAnimatedTexture(animationStep), lightBlendMode, pos, size, rotation, col); } // Render(tex, animationStep, pos) /// /// Render 3D Billboard into scene. Used for 3d effects. /// Set texture before hand, this is required to support both /// normal textures and animated textures. /// This method does not support rotation (it is a bit faster). /// /// Position in world space /// Size in world coordinates /// Color, usually white public static void Render(XnaTexture tex, BlendMode lightBlendMode, Vector3 pos, float size, Color col) { // Invisible? if (col.A == 0) return; TextureBillboardList texBillboard = GetTextureBillboard(tex, lightBlendMode); Vector3 vec; int index = texBillboard.vertices.Count; vec = pos + ((-vecRight + vecUp) * size); texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(0.0f, 0.0f))); vec = pos + ((-vecRight - vecUp) * size); texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(0.0f, 1.0f))); vec = pos + ((vecRight - vecUp) * size); texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(1.0f, 1.0f))); vec = pos + ((vecRight + vecUp) * size); texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(1.0f, 0.0f))); texBillboard.indices.AddRange(new short[] { (short)(index+0), (short)(index+1), (short)(index+2), (short)(index+0), (short)(index+2), (short)(index+3), }); } // Render(tex, pos, size) /// /// Render /// /// Tex /// Position /// Size /// Color public static void Render(Texture tex, Vector3 pos, float size, Color col) { Render(tex.XnaTexture, BlendMode.NormalAlphaBlending, pos, size, col); } // Render(tex, pos, size) /// /// Render /// /// Tex /// Animation step /// Position /// Size /// Rotation /// Color public static void Render(AnimatedTexture tex, int animationStep, BlendMode lightBlendMode, Vector3 pos, float size, Color col) { Render(tex.GetAnimatedTexture(animationStep), lightBlendMode, pos, size, col); } // Render(tex, animationStep, pos) /// /// Render on ground (Z/Y Plane), used for effects which are /// rendered on the ground/landscape. /// Does also support coloring for blending in/out effects. /// Set texture before hand, this is required to support both /// normal textures and animated textures. /// public static void RenderOnGround(XnaTexture tex, BlendMode blendMode, Vector3 pos, float size, float rotation, Color col, Vector3 vecGroundRight, Vector3 vecGroundUp) { // Invisible? if (col.A == 0) return; TextureBillboardList texBillboard = GetTextureBillboard(tex, blendMode);//BlendMode.NormalAlphaBlending); Vector3 right = vecGroundRight; right = Vector3.TransformNormal(right, Matrix.CreateRotationZ(rotation)); Vector3 up = vecGroundUp; up = Vector3.TransformNormal(up, Matrix.CreateRotationZ(rotation)); Vector3 vec = pos + ((-right - up) * size); //obs: CustomVertex.PositionColoredTextured[] billboardVertices = // new CustomVertex.PositionColoredTextured[4]; int index = texBillboard.vertices.Count; texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(0.0f, 1.0f))); vec = pos + ((right - up) * size); texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(1.0f, 1.0f))); vec = pos + ((right + up) * size); texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(1.0f, 0.0f))); vec = pos + ((-right + up) * size); texBillboard.vertices.Add( new VertexPositionColorTexture( vec, col, new Vector2(0.0f, 0.0f))); texBillboard.indices.AddRange(new short[] { (short)(index+0), (short)(index+1), (short)(index+2), (short)(index+0), (short)(index+2), (short)(index+3), }); } // RenderOnGround(pos, size, col) /// /// Render on ground /// /// Tex /// Position /// Size /// Rotation /// Color public static void RenderOnGround(Texture tex, Vector3 pos, float size, float rotation, Color col, Vector3 vecGroundRight, Vector3 vecGroundUp) { RenderOnGround(tex.XnaTexture, BlendMode.NormalAlphaBlending, pos, size, rotation, col, vecGroundRight, vecGroundUp); } // RenderOnGround(tex, pos, size) #endregion #region Unit testing #if DEBUG /// /// Test render billboards /// public static void TestRenderBillboards() { Texture plasma = null, fireball = null, ring = null, smoke = null; TestGame.Start("Test render billboards", delegate { plasma = new Texture("Plasma"); fireball = new Texture("FireBall"); ring = new Texture("ExplosionRing"); smoke = new Texture("Smoke"); }, delegate { BaseGame.Device.Clear(Color.Blue); for (int num = 0; num < 200; num++) { BaseGame.DrawLine( new Vector3(-12.0f + num / 4.0f, 13.0f, 0), new Vector3(-17.0f + num / 4.0f, -13.0f, 0), new Color((byte)(255 - num), 14, (byte)num)); } // for TextureFont.WriteText(2, 30, "cam pos=" + BaseGame.CameraPos); Billboard.Render(plasma, new Vector3(-40.0f, 0.0f, 0.0f), 5.0f, BaseGame.TotalTimeMs * (float)Math.PI / 1000.0f, Color.White); Billboard.Render(plasma, new Vector3(0.0f, 0.0f, 0.0f), 10.0f, (float)Math.PI / 8, Color.Gray); Billboard.Render(plasma, new Vector3(40.0f, 0.0f, 0.0f), 20.0f, BaseGame.TotalTimeMs * (float)Math.PI / 5000.0f, Color.Red); Billboard.Render(fireball, new Vector3(-40.0f, +50.0f, 0.0f), 5.0f, 0, Color.White); Billboard.Render(fireball, new Vector3(0.0f, +50.0f, 0.0f), 10.0f, (float)Math.PI / 8, Color.Yellow); Billboard.Render(fireball, new Vector3(40.0f, +50.0f, 0.0f), 20.0f, (float)Math.PI * 3 / 8, Color.Red); Billboard.RenderOnGround(ring, new Vector3(-25.0f, 0.0f, -100.0f), 5.0f, 0, Color.White, vecGroundRight, vecGroundUp); Billboard.RenderOnGround(ring, new Vector3(0.0f, 0.0f, -100.0f), 10.0f, (float)Math.PI / 8, Color.Blue, vecGroundRight, vecGroundUp); Billboard.RenderOnGround(ring, new Vector3(25.0f, 0.0f, -100.0f), 20.0f, BaseGame.TotalTimeMs * (float)Math.PI / 5000.0f, Color.White, vecGroundRight, vecGroundUp); Billboard.RenderBillboards(); }); } // TestRenderBillboards() //*/ #endif #endregion } // class Billboard } // namespace XnaShooter.Graphics