MOGRE SpriteManager2d        

thumb|
This is a slightly modified port of SpriteManager2d, which provides an easy way to draw 2D objects to the screen. It was done by user Smiley80.

Forum_icon_question2.gif For questions, bug reports etc. use this forum thread.

Features

  • Render two dimensional images
  • It also can write text - use a font textures and set the uv-coordinates appropriately
  • ...

Usage

  • Initialization (after you created the SceneManager). Note: This sets the properties to their default values.

SpriteManager.Initialize(sceneMgr);
  • In the program's main loop (e.g. the FrameStarted eventhandler). Note: The sprite buffer is cleared after each rendering (sprites.Clear() in Render()), so you have to enqueue the sprites you want to draw each frame.



SpriteManager.EnqueueTexture(textureName, x1, y1, x2, y2, alpha);
  • Shutdown (before root.Dispose()).



SpriteManager.Shutdown();

Properties

  • TargetQueue specifies before or after which RenderQueue the rendering should happen (default is RenderQueueGroupID.RENDER_QUEUE_OVERLAY).
  • AfterQueue indicates that the rendering should happen after the specified RenderQueue (default is false).
  • MinimalHardwareBufferSize specifies an the initial and minimum size of the HardwareBuffer. This prevents the HardwareBuffer from being constantly destroyed and recreated when the number sprites increases (if the number of sprites * 6 is below or equal the value of MinimalHardwareBufferSize) (default is 120).

EnqueueTexture in depth

EnqueueTexture(textureName, x1, y1, x2, y2, tx1, ty1, tx2, ty2, alpha)
  • textureName is the filename of the texture you want to draw. The method ensure that the texture is loaded, so it doesn't have to be done beforehand.
  • (x1; y1) is the coordinate of the top-left corner and (x2; y2) is the coodinate of the bottom-right corner of the sprite. The values of x and y range are in screen space (-1 to 1). If you want to use screen coordinates in pixel, you have to convert them using the following formulas:




For x-coordinates:

((float)x / screen-width) * 2 - 1

For y-coordinates:

-(((float)y / screen-height) * 2 - 1)

Example:
Assuming the screen resolution is 1280x960, the position should be at (320; 240) and the size of the sprite is 640x480:


x1 = 320, y1 = 240 => x1 = -0.5, y1 = 0.5 ; x2 = 960, y2 = 720 => x1 = 0.5, y2 = -0.5

  • tx1, ty1, tx2, ty2 are the uv-coordinates of the texture (0 to 1). The default is "0, 0, 1, 1" which draws the whole texture.
  • alpha is the alpha value (D'oh) which ranges from 0 to 1. Where 0 is fully transparent and 1 is fully opaque.



Performance Issues

Enqueuing a lot of sprites (100+) each frame leads to a significant framerate drop. In such case remove sprites.Clear() from Render(), enqueue the sprites only once and add a method, which allows you to remove a sprite manually.

See also

SpriteManager.cs

namespace Example
    {
        using System;
        using System.Collections.Generic;
        using System.Runtime.InteropServices;

        using Mogre;

        public static class SpriteManager
        {
            #region Fields

            private static HardwareVertexBufferSharedPtr hardwareBuffer;
            private static RenderOperation renderOp;
            private static SceneManager sceneMan;
            private static LinkedList<Sprite> sprites;

            #endregion Fields

            #region Properties

            public static bool AfterQueue
            {
                get;
                set;
            }

            public static int MinimalHardwareBufferSize
            {
                get;
                set;
            }

            public static RenderQueueGroupID TargetQueue
            {
                get;
                set;
            }

            #endregion Properties

            #region Methods

            public static void EnqueueTexture(string textureName, float x1, float y1, float x2, float y2, float alpha)
            {
                EnqueueTexture(textureName, x1, y1, x2, y2, 0, 0, 1, 1, alpha);
            }

            public static void EnqueueTexture(string textureName, float x1, float y1, float x2, float y2, float tx1, float ty1, float tx2, float ty2, float alpha)
            {
                float z = -1.0f;

                Sprite spr = new Sprite();
                spr.Alpha = alpha;

                spr.Pos = new Vector3[6];
                spr.UV = new Vector2[6];

                spr.Pos[0] = new Vector3(x1, y2, z);
                spr.UV[0] = new Vector2(tx1, ty2);

                spr.Pos[1] = new Vector3(x2, y1, z);
                spr.UV[1] = new Vector2(tx2, ty1);

                spr.Pos[2] = new Vector3(x1, y1, z);
                spr.UV[2] = new Vector2(tx1, ty1);

                spr.Pos[3] = new Vector3(x1, y2, z);
                spr.UV[3] = new Vector2(tx1, ty2);

                spr.Pos[4] = new Vector3(x2, y1, z);
                spr.UV[4] = new Vector2(tx2, ty1);

                spr.Pos[5] = new Vector3(x2, y2, z);
                spr.UV[5] = new Vector2(tx2, ty2);

                TexturePtr tp = TextureManager.Singleton.GetByName(textureName);
                if (tp == null || !tp.IsLoaded)
                {
                    tp = TextureManager.Singleton.Load(textureName, ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME);
                }

                spr.TexHandle = tp.Handle;
                tp.Dispose();

                if (!sprites.Contains(spr))
                {
                    sprites.AddLast(spr);
                }
            }

            public static void Initialize(SceneManager sceneManager)
            {
                sceneMan = sceneManager;
                TargetQueue = RenderQueueGroupID.RENDER_QUEUE_OVERLAY;
                AfterQueue = false;
                MinimalHardwareBufferSize = 120;
                sprites = new LinkedList<Sprite>();
                sceneMan.RenderQueueStarted += RenderQueueStarted;
                sceneMan.RenderQueueEnded += RenderQueueEnded;
            }

            public static void Shutdown()
            {
                if (hardwareBuffer != null)
                {
                    HardwareBuffer_Destroy();
                }

                sceneMan.RenderQueueStarted -= RenderQueueStarted;
                sceneMan.RenderQueueEnded -= RenderQueueEnded;
            }

            private static void HardwareBuffer_Create(int size)
            {
                VertexDeclaration vd;

                renderOp = new RenderOperation();
                renderOp.vertexData = new VertexData();
                renderOp.vertexData.vertexStart = 0;

                vd = renderOp.vertexData.vertexDeclaration;
                vd.AddElement(
                    0,
                    0,
                    VertexElementType.VET_FLOAT3,
                    VertexElementSemantic.VES_POSITION);

                vd.AddElement(
                    0,
                    VertexElement.GetTypeSize(VertexElementType.VET_FLOAT3),
                    VertexElementType.VET_FLOAT2,
                    VertexElementSemantic.VES_TEXTURE_COORDINATES);

                hardwareBuffer = HardwareBufferManager.Singleton.CreateVertexBuffer(
                    vd.GetVertexSize(0),
                    (uint)size,
                    HardwareBuffer.Usage.HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE,
                    true);

                renderOp.vertexData.vertexBufferBinding.SetBinding(0, hardwareBuffer);

                renderOp.operationType = RenderOperation.OperationTypes.OT_TRIANGLE_LIST;
                renderOp.useIndexes = false;
            }

            private static void HardwareBuffer_Destroy()
            {
                hardwareBuffer.Dispose();
                renderOp.vertexData.Dispose();
                renderOp.Dispose();
            }

            private static void Render()
            {
                if (sprites.Count == 0)
                {
                    return;
                }

                RenderSystem rs = Root.Singleton.RenderSystem;

                Chunk thisChunk = new Chunk();
                List<Chunk> chunks = new List<Chunk>();

                int newSize;

                newSize = sprites.Count * 6;
                if (newSize < MinimalHardwareBufferSize)
                {
                    newSize = MinimalHardwareBufferSize;
                }

                // grow hardware buffer if needed
                if (hardwareBuffer == null || hardwareBuffer.NumVertices < newSize)
                {
                    if (hardwareBuffer != null)
                    {
                        HardwareBuffer_Destroy();
                    }

                    HardwareBuffer_Create(newSize);
                }

                // write quads to the hardware buffer, and remember chunks
                unsafe
                {
                    Vertex* buffer = (Vertex*)hardwareBuffer.Lock(HardwareBuffer.LockOptions.HBL_DISCARD);

                    LinkedListNode<Sprite> node = sprites.First;
                    Sprite currSpr;
                   
                    while (node != null)
                    {
                        currSpr = node.Value;
                        thisChunk.Alpha = currSpr.Alpha;
                        thisChunk.TexHandle = currSpr.TexHandle;
                       
                        for (int i = 0; i < 6; i++)
                        {
                            *buffer++ = new Vertex(
                                currSpr.Pos[i],
                                currSpr.UV[i]);
                        }

                        thisChunk.VertexCount += 6;
                       
                        node = node.Next;

                        if (node == null || thisChunk.TexHandle != node.Value.TexHandle || thisChunk.Alpha != node.Value.Alpha)
                        {
                            chunks.Add(thisChunk);
                            thisChunk.VertexCount = 0;
                        }
                    }
                }
               
                hardwareBuffer.Unlock();

                // set up...
                RenderSystem_Setup();

                // do the real render!
                // do the real render!
                TexturePtr tp = null;
                renderOp.vertexData.vertexStart = 0;
                foreach (Chunk currChunk in chunks)
                {
                    renderOp.vertexData.vertexCount = currChunk.VertexCount;
                    tp = TextureManager.Singleton.GetByHandle(currChunk.TexHandle);
                    rs._setTexture(0, true, tp.Name);
                    rs._setTextureUnitFiltering(
                         0,
                         FilterOptions.FO_LINEAR,
                         FilterOptions.FO_LINEAR,
                         FilterOptions.FO_POINT);

                    // set alpha
                    LayerBlendModeEx_NativePtr alphaBlendMode = LayerBlendModeEx_NativePtr.Create();
                    alphaBlendMode.alphaArg1 = 0;
                    alphaBlendMode.alphaArg2 = currChunk.Alpha;
                    alphaBlendMode.source1 = LayerBlendSource.LBS_TEXTURE;
                    alphaBlendMode.source2 = LayerBlendSource.LBS_MANUAL;
                    alphaBlendMode.blendType = LayerBlendType.LBT_ALPHA;
                    alphaBlendMode.operation = LayerBlendOperationEx.LBX_MODULATE;
                    alphaBlendMode.factor = currChunk.Alpha;
                    rs._setTextureBlendMode(0, alphaBlendMode);

                    rs._render(renderOp);
                    renderOp.vertexData.vertexStart += currChunk.VertexCount;
                    alphaBlendMode.DestroyNativePtr();
                }

                if (tp != null)
                {
                    tp.Dispose();
                }

                // sprites go home!
                sprites.Clear();
            }
           
            private static void RenderQueueEnded(byte queueGroupId, string invocation, out bool repeatThisInvocation)
            {
                repeatThisInvocation = false; // shut up compiler
                if (AfterQueue && queueGroupId == (byte)TargetQueue)
                {
                    Render();
                }
            }

            private static void RenderQueueStarted(byte queueGroupId, string invocation, out bool skipThisInvocation)
            {
                skipThisInvocation = false; // shut up compiler
                if (!AfterQueue && queueGroupId == (byte)TargetQueue)
                {
                    Render();
                }
            }

            private static void RenderSystem_Setup()
            {
                RenderSystem rs = Root.Singleton.RenderSystem;

                LayerBlendModeEx_NativePtr colorBlendMode = LayerBlendModeEx_NativePtr.Create();
                colorBlendMode.blendType = LayerBlendType.LBT_COLOUR;
                colorBlendMode.source1 = LayerBlendSource.LBS_TEXTURE;
                colorBlendMode.operation = LayerBlendOperationEx.LBX_SOURCE1;

                TextureUnitState.UVWAddressingMode uvwAddressMode;
                uvwAddressMode.u = TextureUnitState.TextureAddressingMode.TAM_CLAMP;
                uvwAddressMode.v = TextureUnitState.TextureAddressingMode.TAM_CLAMP;
                uvwAddressMode.w = TextureUnitState.TextureAddressingMode.TAM_CLAMP;

                rs._setWorldMatrix(Matrix4.IDENTITY);
                rs._setViewMatrix(Matrix4.IDENTITY);
                rs._setProjectionMatrix(Matrix4.IDENTITY);
                rs._setTextureMatrix(0, Matrix4.IDENTITY);
                rs._setTextureCoordSet(0, 0);
                rs._setTextureCoordCalculation(0, TexCoordCalcMethod.TEXCALC_NONE);
                rs._setTextureBlendMode(0, colorBlendMode);
                rs._setTextureAddressingMode(0, uvwAddressMode);
                rs._disableTextureUnitsFrom(1);
                rs.SetLightingEnabled(false);
                rs._setFog(FogMode.FOG_NONE);
                rs._setCullingMode(CullingMode.CULL_NONE);
                rs._setDepthBufferParams(false, false);
                rs._setColourBufferWriteEnabled(true, true, true, false);
                rs.SetShadingType(ShadeOptions.SO_GOURAUD);
                rs._setPolygonMode(PolygonMode.PM_SOLID);
                rs.UnbindGpuProgram(GpuProgramType.GPT_FRAGMENT_PROGRAM);
                rs.UnbindGpuProgram(GpuProgramType.GPT_VERTEX_PROGRAM);
                rs._setSeparateSceneBlending(
                    SceneBlendFactor.SBF_SOURCE_ALPHA,
                    SceneBlendFactor.SBF_ONE_MINUS_SOURCE_ALPHA,
                    SceneBlendFactor.SBF_ONE,
                    SceneBlendFactor.SBF_ONE);
                rs._setAlphaRejectSettings(CompareFunction.CMPF_ALWAYS_PASS, 0, true);

                colorBlendMode.DestroyNativePtr();
            }

            #endregion Methods

            #region Nested Types

            internal struct Chunk
            {
                #region Properties

                public float Alpha
                {
                    get; set;
                }

                public uint TexHandle
                {
                    get; set;
                }

                public uint VertexCount
                {
                    get; set;
                }

                #endregion Properties
            }

            internal struct Sprite
            {
                #region Properties

                public float Alpha
                {
                    get; set;
                }

                public Vector3[] Pos
                {
                    get; set;
                }

                public uint TexHandle
                {
                    get; set;
                }

                public Vector2[] UV
                {
                    get; set;
                }

                #endregion Properties

                #region Methods

                public static bool operator !=(Sprite left, Sprite right)
                {
                    return !left.Equals(right);
                }

                public static bool operator ==(Sprite left, Sprite right)
                {
                    return left.Equals(right);
                }

                public override bool Equals(object obj)
                {
                    if (obj is Sprite)
                    {
                        return this.Equals((Sprite)obj); // use Equals method below
                    }
                    else
                    {
                        return false;
                    }
                }

                public bool Equals(Sprite other)
                {
                    bool equal = this.TexHandle == other.TexHandle && this.Alpha == other.Alpha;

                    if (!equal)
                    {
                        return false;
                    }

                    for (int i = 0; i < 6; i++)
                    {
                        if (this.Pos[i] != other.Pos[i])
                        {
                            return false;
                        }

                        if (this.UV[i] != other.UV[i])
                        {
                            return false;
                        }
                    }

                    return true;
                }

                public override int GetHashCode()
                {
                    return this.Alpha.GetHashCode() ^
                        this.Pos.GetHashCode() ^
                        this.UV.GetHashCode() ^
                        this.TexHandle.GetHashCode();
                }

                #endregion Methods
            }

            [StructLayout(LayoutKind.Explicit)]
            internal struct Vertex
            {
                [FieldOffset(0)]
                public Vector3 Pos;
                [FieldOffset(12)]
                public Vector2 UV;

                #region Constructors

                public Vertex(Vector3 pos, Vector2 uv)
                {
                    this.Pos = pos;
                    this.UV = uv;
                }

                #endregion Constructors
            }

            #endregion Nested Types
        }
    }