Text Rendering In D3D11/12

Started by
9 comments, last by ErnieDingo 7 years, 8 months ago

Hi community!

I about to start text rendering implementation.

I am going to use it for:
1. Rendering current player’s health, ammo count, …
2. In near future - debug information.
3. In the FAR future – text dialogs, some buttons, settings dialog.

I found 3 ways how it might be accomplished:

1. Create bitmap with Direct2D and draw it on backbuffer.

Pros: no 3rd party dependency.

Cons: ?Performance?

2. Using 3rd party lib.
Pros: no need to create layout management
Cons1: Extra dependency with (probably) lower quality than Microsoft’s D2D.
Cons2: Big concern about performance

Concern: No idea about licensing, and how it might hit me later.

3. Full implementation via sprites composition.
Pros: Probably faster that D2D
Cons1: Entire layout management
Cons2: Have no artist, so I should create sprites by myself.

Also I am not aware what is the best practice in the industry.

What would you recommend?

Advertisement

If you want to go all-in for a good implementation of text rendering you should look up the concept of "Signed Distance Field Text Rendering".

The original article that introduced the concept was written by Valve and can be found here: http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf

I'm currently too lazy to implement this myself in my game, so I'm using two 3rd party libraries :D
*libRocket for general UIs - it loads truetype fonts and renders them as sprites, and uses a type of HTML/CSS for layouts.
*Dear IMGUI, for debug / developer interfaces.

Both have abstract renderers so you're in control of the actual D3D/GL calls.

It would be nice to have something a lot simpler that I can use for text though... I'm thinking about forking libRocket and deleting everything except text rendering :lol:

Currently rolling my own directly into a Vertex Buffer using a 3rd party app to create the source texture. It has some limitations, but is fairly quick (from initial tests). I might even try to publish it for others to use at some point.

C# and Sharpdx. I should look into that signed distance field because it looks like it is quite adaptable to what I am doing!

Will post some screenshots of my initial outcomes once code is complete.

Indie game developer - Game WIP

Strafe (Working Title) - Currently in need of another developer and modeler/graphic artist (professional & amateur's artists welcome)

Insane Software Facebook

Check out a third party library called Sciter. You can write a UI layout by using HTML and a javascript variant called TIScript.

The integration into an engine is easy but you will still have to get familiar with a few differences in the HTML and CSS.

Also, the forums are responsive and quite helpful, and it is free to use if you only use the binaries.

I've been trying out Sciter myself for a little while too. If you just want to do basic stuff then it can work fine, it handles plain HTML/CSS without customizations. If you want to do advanced UI though, trying to figure out how to get standard things to work in TIScript can be rather frustrating. For the most part, anything you can do in (standard?) JS you can do in TIScript, but the syntax or objects or methods will (almost always) be different. It also has a rendering path to use D2D directly, which has improved rendering performance over the regular path. I've used Awesomium in the past for HTML as a UI, but Sciter is a lot easier and mostly cleaner to integrate than Awesomium was, at least for a smaller project.

I've attached my code for C# and Sharpdx, there is some code missing in here, but the basic function is now working. still some bugs, but if you want to post a 256 char message to the screen then it does it. This function uses Codehead's font generator file format.

Code was hacked together in a day. I need to go through, remove redundant code, formatting, refactoring etc.

Whole bunch of functionality I have not implemented as you can see. Will break this into 2 classes and move some functions around which will naturally shrink the code base.

No warranties offered :)


using System;
using SharpDX;
using D3D11 = SharpDX.Direct3D11;
using DXGI = SharpDX.DXGI;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;

namespace Insane3d
{
    public struct WriterTextureObject
    {
        public D3D11.ShaderResourceView m_d3dTexture;

        public int m_textureWidth;
        public int m_textureHeight;
        public byte m_bpp;
        public byte m_charOffset;
        public byte[] m_charWidths;
        public int m_cellWidth;
        public int m_cellHeight;
        public int m_columnCount;
        
    }

    public class WriterString
    {
        private bool m_updated;
        private int m_fontIndex;

        public string m_message;
        public float m_x; // in -1.0 to 1.0 co-ords
        public float m_y; // in -1.0 to 1.0 co-ords
        public bool m_uniformSpacing; // use this if you want the text render to apply cell width rather than char width.
        public string m_uid;

        public int m_startingIndex;
        public int m_runLength;
        public bool m_enabled;
      
        public bool Updated
        {
            get { return m_updated; }
            set { m_updated = value; }            
        }

        public int FontIndex
        {
            get { return m_fontIndex; }
            set { m_fontIndex = value; }
            
        }
    }

    public class DirectXWriter : IDisposable
    {
        List<WriterTextureObject> m_objects;
        List<WriterString> m_strings;
        
        D3DBufferContainer m_staticStrings;  // we wont use buffer containers/ we will use our own triangle buffer.

        float m_screenWidth;
        float m_screenHeight;

        static int s_maxEntities = 64;
        static int s_maxCharacters = 128;
        static int s_verticesPerChar = 6;

        D3D11.Device m_device;
        D3D11.DeviceContext m_context;
        D3D11.Effect m_effect;

        bool m_update;

        D3D11.Buffer m_D3DtriangleVertexBuffer;
        List<VertexPointMaster> m_staticVertexList;
        D3D11.VertexBufferBinding[] m_binding;

        /*----------------------------------------------------------------------
        *
        *
        *  
        *  
        *----------------------------------------------------------------------*/
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="a_parentDevice"></param>
        /// <param name="a_context"></param>
        /// <param name="a_effect"></param>
        public DirectXWriter(D3D11.Device a_parentDevice, D3D11.DeviceContext a_context, D3D11.Effect a_effect)
        {
            m_device = a_parentDevice;
            m_context = a_context;
            m_effect = a_effect;
            m_update = false;

            m_objects = new List<WriterTextureObject>();

            m_staticVertexList = new List<VertexPointMaster>();

            m_screenWidth = 1920;
            m_screenHeight = 1080;

            m_staticStrings = new D3DBufferContainer();

            m_strings = new List<WriterString>();

            m_binding = new D3D11.VertexBufferBinding[2];

            DirectXHelper.CreateInitialD3DBuffer(m_device, m_context, ref m_D3DtriangleVertexBuffer, s_maxCharacters * s_verticesPerChar * s_maxEntities, ref m_binding);
        }

        /// <summary>
        /// Main Rendering Function
        /// </summary>
        public void Render()
        {
            if (m_update == true)
            {
                CreateD3DBufferFromData();
                m_update = false;
            }

            RenderBuffers();
        }

        /*----------------------------------------------------------------------
        *
        *
        * 
        * 
        *----------------------------------------------------------------------*/

        /// <summary>
        /// Takes in a list of vertices and creates a direct3d buffer, if the buffer exists and is too small, it will resize it.
        /// </summary>

        void CreateD3DBufferFromData()
        {
            bool recreateBinding = false;
            int vertCount = 0;
            int startCount = 0;
            int offsetCount = 0;

            Debug.Assert(s_maxCharacters * s_verticesPerChar * s_maxEntities > 0);

            if (m_D3DtriangleVertexBuffer != null)
            {
                if (m_D3DtriangleVertexBuffer.Description.SizeInBytes < s_maxCharacters * s_verticesPerChar * s_maxEntities * GlobalStatics.s_vertexSize)
                {
                    m_D3DtriangleVertexBuffer.Dispose();
                    m_D3DtriangleVertexBuffer = null;
                }
                else
                {
                    DataStream astream;
                    var dataBox = m_context.MapSubresource(m_D3DtriangleVertexBuffer, D3D11.MapMode.WriteDiscard, D3D11.MapFlags.None, out astream);

                    for (int k = 0; k < m_strings.Count; k++)
                    {
                        offsetCount = k * s_maxCharacters * s_verticesPerChar;
                        vertCount = startCount + m_strings[k].m_runLength;

                        if (m_strings[k].m_enabled && m_strings[k].Updated)
                        {
                            astream.Position = offsetCount * GlobalStatics.s_vertexSize;
                            
                            for (int i = startCount; i < vertCount; i++)
                            {
                                astream.Write(m_staticVertexList[i].m_vertex);
                                astream.Write(m_staticVertexList[i].getNormal());
                                astream.Write(m_staticVertexList[i].m_texture);
                                astream.Write(m_staticVertexList[i].getBlendVector());
                            }

                            m_strings[k].Updated = false;
                        }

                        startCount = startCount + m_strings[k].m_runLength;
                    }

                    m_context.UnmapSubresource(m_D3DtriangleVertexBuffer, 0); //to update the data on GPU
                    astream.Dispose();
                }
            }

            if (m_D3DtriangleVertexBuffer == null)
            {
                // now write out the buffer correctly
                var stream = new DataStream(s_maxCharacters * s_verticesPerChar * s_maxEntities * GlobalStatics.s_vertexSize, true, true);


                for (int k = 0; k < m_strings.Count; k++)
                {
                    offsetCount = k * s_maxCharacters * s_verticesPerChar;
                    vertCount = startCount + m_strings[k].m_runLength;

                    if (m_strings[k].m_enabled)
                    {
                        stream.Position = offsetCount * GlobalStatics.s_vertexSize;

                        for (int i = startCount; i < vertCount; i++)
                        {
                            stream.Write(m_staticVertexList[i].m_vertex);
                            stream.Write(m_staticVertexList[i].getNormal());
                            stream.Write(m_staticVertexList[i].m_texture);
                            stream.Write(m_staticVertexList[i].getBlendVector());
                        }
                    }

                    startCount = startCount + m_strings[k].m_runLength;
                }
                stream.Position = 0;

                m_D3DtriangleVertexBuffer = new D3D11.Buffer(m_device, stream, new D3D11.BufferDescription
                {
                    BindFlags = D3D11.BindFlags.VertexBuffer,
                    CpuAccessFlags = D3D11.CpuAccessFlags.Write,
                    OptionFlags = D3D11.ResourceOptionFlags.None,
                    SizeInBytes = (int)stream.Length,
                    Usage = D3D11.ResourceUsage.Dynamic

                });
                recreateBinding = true;
                stream.Dispose();
            }

            if (recreateBinding)
            {
                if (m_binding[0].Buffer != null)
                {
                    m_binding[0].Buffer.Dispose();
                }

                m_binding[0] = new D3D11.VertexBufferBinding(m_D3DtriangleVertexBuffer, GlobalStatics.s_vertexSize, 0);
            }
        }


        /*----------------------------------------------------------------------
        *
        *
        *  
        *  
        *----------------------------------------------------------------------*/


        /// <summary>
        /// Function that creates the geometry for the string in screen space.  Returns a UID than can be used to update the string contents and position.
        /// </summary>
        /// <param name="a_message">String to be presented on screen</param>
        /// <param name="a_uid">Pass in blank if new string entry, or pass in previous UID to overwrite current item</param>
        /// <param name="a_fontIndex">Index of font set you want to use</param>
        /// <param name="a_x">Position in screen space of a 1920 x 1080 window</param>
        /// <param name="a_y">Position in screen space of a 1920 x 1080 window</param>
        /// <returns></returns>
        public string updateString(string a_message, string a_uid, int a_fontIndex, int a_x, int a_y)  // pass in a blank UID will make it new
        {

            m_update = true;

            WriterString newstring = new WriterString();
            int foundIndex = -1;

            newstring.m_uid = a_uid;

            if (a_uid != "")
            {
                for (int i = 0; i < m_strings.Count; i++)
                {
                    if (m_strings[i].m_uid == a_uid)
                    {
                        foundIndex = i;
                        newstring = m_strings[i];
                        i = m_strings.Count;                        
                    }
                }                
            }            
            else
            {
                newstring = new WriterString();
                foundIndex = m_strings.Count;
                m_strings.Add(newstring);
            }
                  
            string uid = createStringGeometry(newstring, a_x, a_y, 5);

            newstring.m_message = a_message;
            newstring.m_runLength = newstring.m_message.Length * s_verticesPerChar;
            newstring.m_startingIndex = foundIndex * s_maxCharacters * s_verticesPerChar;
                        
            newstring.m_enabled = true;
            newstring.Updated = true;
            newstring.FontIndex = a_fontIndex;            
 
            m_strings[foundIndex] = newstring;

            return uid;
        }

        // we need to do some work to make the max length of a string 256 for instance.
        // this means that in the vertex buffer, we can then allocate static lengths.
        // the run length and starting index then can be aligned to 256 for example
        // that means that when a string is updated, we only have to update part of the buffer, not rebuild it completely.
        /// <summary>
        /// Private function that creates all the vertex data for the triangles.  Triangles are using Screen space co-ordinates.
        /// </summary>
        /// <param name="newstring"></param>
        /// <param name="a_x"></param>
        /// <param name="a_y"></param>
        /// <param name="a_scale"></param>
        /// <returns></returns>
        string createStringGeometry(WriterString newstring, int a_x, int a_y, float a_scale)
        {
            newstring.m_uid = Helper.GetUniqueKey(8);
            
            int x = a_x;

            if (newstring.m_message.Length > s_maxCharacters)
            {
                newstring.m_message = newstring.m_message.Substring(0, s_maxCharacters);
            }

            for (int i = 0; i < newstring.m_message.Length ; i++)
            {
                x  = createCharQuad(newstring.m_message[i], x, a_y, ref m_staticVertexList, 0, a_scale);
            }
            
            return newstring.m_uid;
        }


        /*----------------------------------------------------------------------
        *
        *
        *  
        *  
        *----------------------------------------------------------------------*/

        int createCharQuad(char a_char, int a_x, int a_y, ref List<VertexPointMaster> a_list, int a_fontIndex, float a_scale)
        {
            int index = (byte)a_char;

            WriterTextureObject text = m_objects[a_fontIndex];
            float scaledWidth;

            if (index == 0)
            {
                scaledWidth = (text.m_charWidths[index] / 2 * a_scale);
            }
            else
            {
                scaledWidth = (text.m_charWidths[index] * a_scale);
            }
            index = index - text.m_charOffset;

            Debug.Assert(index >= 0 && index < 256);

            float topX, topy;
            float botX, boty;

            int textIndex = index % text.m_columnCount;

            // u coords
            topX = textIndex * text.m_cellWidth;
            botX = topX + text.m_charWidths[index];
            
            topX = topX / text.m_textureWidth;
            botX = botX / text.m_textureWidth;

            int result =  a_x + (int) scaledWidth + 15; // 15 is fudge factor for spacing, will do something more orietated to char set later.

            // v coords

            textIndex = index / text.m_columnCount;

            topy = textIndex * text.m_cellHeight;
            boty = topy + text.m_cellHeight;
            topy = topy / text.m_textureHeight;
            boty = boty / text.m_textureHeight;

            float topxCoord, topyCoord; //top left corner
            float botxCoord, botyCoord; // bottom right corner

            topxCoord = convertPixelXCoordToScreen(a_x);
            topyCoord = convertPixelYCoordToScreen(a_y);
            botxCoord = convertPixelXCoordToScreen(a_x + text.m_charWidths[index] * a_scale) ;            
            botyCoord = convertPixelYCoordToScreen(a_y + text.m_cellHeight * a_scale) ;

            VertexPointMaster vp1;
            
            // tri 1 - Upper left.

            vp1 = new VertexPointMaster();
            vp1.setVertex(new Vector3(topxCoord, topyCoord, 0));
            vp1.setTexture(new Vector2(topX, topy));
            vp1.setTexturePrimary(a_fontIndex);
            vp1.setDepthBlend(1); // we might want to make this semi transparent later or play with colours etc.
            a_list.Add(vp1);


            // tri 1 - Upper right.
            vp1 = new VertexPointMaster();
            vp1.setVertex(new Vector3(botxCoord,  topyCoord,0));
            vp1.setTexture(new Vector2(botX, topy));
            vp1.setTexturePrimary(a_fontIndex);
            vp1.setDepthBlend(1);
            a_list.Add(vp1);

            // tri 1 - Bottom left.
            vp1 = new VertexPointMaster();
            vp1.setVertex(new Vector3(topxCoord, botyCoord,0));
            vp1.setTexture(new Vector2(topX, boty));
            vp1.setTexturePrimary(a_fontIndex);
            vp1.setDepthBlend(1);
            a_list.Add(vp1);



            // tri 2 - Bottom left.
            vp1 = new VertexPointMaster();
            vp1.setVertex(new Vector3(topxCoord, botyCoord, 0));
            vp1.setTexture(new Vector2(topX, boty));
            vp1.setTexturePrimary(a_fontIndex);
            vp1.setDepthBlend(1); // we might want to make this semi transparent later or play with colours etc.
            a_list.Add(vp1);

            // tri 2 - Upper right
            vp1 = new VertexPointMaster();
            vp1.setVertex(new Vector3(botxCoord, topyCoord, 0));
            vp1.setTexture(new Vector2(botX, topy));
            vp1.setTexturePrimary(a_fontIndex);
            vp1.setDepthBlend(1); // we might want to make this semi transparent later or play with colours etc.
            a_list.Add(vp1);

            // tri 2 - Bottom right.
            vp1 = new VertexPointMaster();
            vp1.setVertex(new Vector3(botxCoord, botyCoord, 0));
            vp1.setTexture(new Vector2(botX, boty));
            vp1.setTexturePrimary(a_fontIndex);
            vp1.setDepthBlend(1); // we might want to make this semi transparent later or play with colours etc.
            a_list.Add(vp1);

            return result; // returns the width of the char added in pixels
        }

        /*----------------------------------------------------------------------
        *
        *
        *  
        *  
        *----------------------------------------------------------------------*/

        void RenderBuffers()
        {
            GlobalStatics.DXManager.setZBufferState(false);

            D3D11.BlendState blend = GlobalStatics.DXManager.getBlendState(0);

            m_context.OutputMerger.SetBlendState(blend);

            DirectXHelper.LoadVertexBuffertoGPU(m_context, m_binding[0], GlobalStatics.DXManager.s_bufferLayout, SharpDX.Direct3D.PrimitiveTopology.TriangleList);
            for (int i = 0; i < m_strings.Count; i++)
            {
                if (m_strings[i].m_enabled)
                {
                    //1. set the texture, then get the immediate context updated. (ALA set the variables prior to getting the state of the byte code such as texture type and matrices
                    //2. get the bytecode and apply to the immediate context. 
                    //3. pass vertex data directive to immediate context for render

                    GlobalStatics.s_primaryTexture.SetResource(m_objects[0].m_d3dTexture);

                    m_effect.GetTechniqueByName("TextWriter").GetPassByIndex(0).Apply(m_context);

                    m_context.Draw(m_strings[i].m_runLength, m_strings[i].m_startingIndex);
                }
            }
            m_effect.GetTechniqueByIndex(0).GetPassByIndex(0).Apply(m_context);

            GlobalStatics.DXManager.setZBufferState(true);
        }

        /*----------------------------------------------------------------------
        *
        *
        *  
        *  
        *----------------------------------------------------------------------*/

        public bool LoadFont(string a_filename, bool a_mipmapped)
        {
            WriterTextureObject obj = new WriterTextureObject();
            D3D11.Texture2D tex;

            BinaryReader reader = new BinaryReader(File.Open(a_filename, FileMode.Open));

            // Check header - should read 0xBF 0xF2 (BFF2)
            byte h0 = reader.ReadByte();
            byte h1 = reader.ReadByte();

            if (h0 != 0xBF || h1 != 0xF2)
            {
                reader.Close();
                return false;
            }

            // Get image dimensions
            obj.m_textureWidth = reader.ReadInt32();
            obj.m_textureHeight = reader.ReadInt32();

            // Get cell dimensions
            obj.m_cellWidth = reader.ReadInt32();
            obj.m_cellHeight = reader.ReadInt32();

            // Sanity check (prevent divide by zero)
            if (obj.m_cellWidth <= 0 || obj.m_cellHeight <= 0)
            {
                throw new IOException("Invalid header content");
            }

            // Pre-calculate column count
            obj.m_columnCount = obj.m_textureWidth / obj.m_cellWidth;

            // Get colour depth
            obj.m_bpp = reader.ReadByte();

            // Get base offset
            obj.m_charOffset = reader.ReadByte();

            // Read width information
            obj.m_charWidths = new byte[256];
            for (int wLoop = 0; wLoop < 256; ++wLoop)
            {
                obj.m_charWidths[wLoop] = reader.ReadByte();
            }

            // Get bitmap
            int bitLen = (obj.m_textureHeight * obj.m_textureWidth) * (obj.m_bpp / 8);
            byte[] bits = new byte[bitLen];

            bits = reader.ReadBytes(bitLen);
            reader.Close();

            D3D11.Texture2DDescription desc = new D3D11.Texture2DDescription()
            {
                ArraySize = 1,
                BindFlags = D3D11.BindFlags.ShaderResource,
                CpuAccessFlags = D3D11.CpuAccessFlags.Write,
                Format = DXGI.Format.B8G8R8A8_UNorm,
                Height = obj.m_textureHeight,
                MipLevels = 1,
                SampleDescription = new DXGI.SampleDescription(1, 0),
                Usage = D3D11.ResourceUsage.Dynamic,
                Width = obj.m_textureWidth

            };

            if (a_mipmapped)
            {
                desc.Usage = D3D11.ResourceUsage.Default;
                desc.CpuAccessFlags = D3D11.CpuAccessFlags.None;  // its none because we load the data into the texture as a full resource update, not update the contents after with map resource.
                desc.Format = DXGI.Format.R8G8B8A8_UNorm_SRgb;
                desc.MipLevels = 0;
                desc.OptionFlags = D3D11.ResourceOptionFlags.GenerateMipMaps;
                desc.SampleDescription.Count = 1;
                desc.SampleDescription.Quality = 0;
                desc.BindFlags = D3D11.BindFlags.RenderTarget | D3D11.BindFlags.ShaderResource;
            }

            if (desc.MipLevels != 0)
            {
                // if we arent doing mip maps, then load the resource directly
                DataStream buffer = new DataStream(obj.m_textureHeight * obj.m_textureWidth * 4, true, true);

                load24BitBitmapInto32BitTexture(bits, buffer, obj);

                DataRectangle rect = new DataRectangle(buffer.DataPointer, obj.m_textureWidth * 4);
                tex = new D3D11.Texture2D(m_device, desc, rect); /// this creates a texture and populates it,

                obj.m_d3dTexture = new D3D11.ShaderResourceView(m_device, tex);
            }
            else
            {
                tex = new D3D11.Texture2D(m_device, desc); /// we need to create a blank one then populate it with the mip mapped texture.

                int stride = obj.m_textureWidth * 4;
                var buffer = new DataStream(obj.m_textureHeight * stride, true, true);

                load24BitBitmapInto32BitTexture(bits, buffer, obj);

                DataBox box = new DataBox(buffer.DataPointer, stride, 1);

                m_device.ImmediateContext.UpdateSubresource(box, tex, D3D11.Resource.CalculateSubResourceIndex(0, 0, 4));

                buffer.Dispose();

                D3D11.ShaderResourceViewDescription srvDesc = new D3D11.ShaderResourceViewDescription();
                srvDesc.Format = desc.Format;
                srvDesc.Dimension = SharpDX.Direct3D.ShaderResourceViewDimension.Texture2D;
                srvDesc.Texture2D.MostDetailedMip = 0;
                srvDesc.Texture2D.MipLevels = -1;

                obj.m_d3dTexture = new D3D11.ShaderResourceView(m_device, tex, srvDesc);
            }

            if (a_mipmapped)
            {
                m_context.GenerateMips(obj.m_d3dTexture);
            }

            m_objects.Add(obj);

            return true;
        }

        void load24BitBitmapInto32BitTexture(byte[] a_bits, DataStream a_buffer, WriterTextureObject a_obj)
        {
            for (int i = 0; i < a_bits.Length; i += 3)
            {
                a_buffer.WriteByte(a_bits[i]);
                a_buffer.WriteByte(a_bits[i + 1]);
                a_buffer.WriteByte(a_bits[i + 2]);
                // if i % 3 = 2 then add an alpha channel byte
                if (a_bits[i] > 0)
                {
                    a_buffer.WriteByte(a_bits[i]); //need this to convert to full 32 bit
                }
                else
                {
                    a_buffer.WriteByte(0); //need this to convert to full 32 bit
                }
            }
        }

        float convertPixelXCoordToScreen(float a_x)
        {
            float val = (a_x / m_screenWidth / 2.0f) - 1.0f;

            return val;
        }

        float convertPixelYCoordToScreen(float a_y)
        {
            float val = 1.0f - (a_y / m_screenHeight / 2.0f);

            return val;
        }

        float convertWidthToScreen(float a_width)
        {
            float val = a_width / m_screenWidth / 2;

            return val;
        }

        float convertHeightToScreen(float a_width)
        {
            float val = a_width / m_screenHeight / 2;

            return val;
        }



        #region IDisposable Support
        private bool disposedValue = false; // To detect redundant calls

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    if (m_D3DtriangleVertexBuffer != null)
                    {
                        m_D3DtriangleVertexBuffer.Dispose();
                    }
                    if (m_staticStrings != null)
                    {
                        m_staticStrings.Dispose();
                    }

                    // TODO: dispose managed state (managed objects).
                }

                // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
                // TODO: set large fields to null.

                disposedValue = true;
            }
        }

        // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
        // ~DirectXWriter() {
        //   // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        //   Dispose(false);
        // }

        // This code added to correctly implement the disposable pattern.
        public void Dispose()
        {
            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
            Dispose(true);
            // TODO: uncomment the following line if the finalizer is overridden above.
            // GC.SuppressFinalize(this);
        }
        #endregion
    }
}

Indie game developer - Game WIP

Strafe (Working Title) - Currently in need of another developer and modeler/graphic artist (professional & amateur's artists welcome)

Insane Software Facebook

No warranties offered :)

Good Job!

I wish you’ll finish it! :)

I was surprised how many people use HTML/CSS/Java in games.

After a little bit of research, I came to conclusion that I need to implement not one, but 3 text renderers in my engine :):

  1. Sprites for Debugging (fast and lo-quality)
  2. DirectWrite for score, ammo, dialogs, menus, and other plain high-quality text.
  3. 3D meshes for animated text on a scene (like rotated and bouncing score value after something was destroyed)

If I'll found out that DirectWrite is very slow, I will start Research-2! :)

Thanks you all for help!

No warranties offered :)

Good Job!

I wish you’ll finish it! :)

I was surprised how many people use HTML/CSS/Java in games.

After a little bit of research, I came to conclusion that I need to implement not one, but 3 text renderers in my engine :):

  1. Sprites for Debugging (fast and lo-quality)
  2. DirectWrite for score, ammo, dialogs, menus, and other plain high-quality text.
  3. 3D meshes for animated text on a scene (like rotated and bouncing score value after something was destroyed)

If I'll found out that DirectWrite is very slow, I will start Research-2! :)

Thanks you all for help!

Just a heads up on that also, the Writer object should be interchangeable to put things like meshes etc into the renderer. I have only done text, but in theory, if I inherit the writer object I should be able to do other things like sprites etc. I will post the code some where a little more public, but i'm doing the same thing write now, I need guages etc.

So, instead of rendering text, I can derive a new instance of the object to say render guages or other graphics. But its working, just test it and its fine.

Plenty to add. Just don't write 3 renderers, just make the code interchangeable at key points!

Link to a screenshot of render of text in action -> http://insanesoftware.com.au/testtext.bmp

Indie game developer - Game WIP

Strafe (Working Title) - Currently in need of another developer and modeler/graphic artist (professional & amateur's artists welcome)

Insane Software Facebook

Plenty to add. Just don't write 3 renderers, just make the code interchangeable at key points!

1. I want to use them simultanious in different modes: none, or 1/2/3, or 1+2/1+3/2+3, or 1+2+3.

2. They will do different jobs and will have different interfaces.

3. Sprite and DW will render at last stage, but 3D will render on gbuffer stage.

4. I don't think extra new()'s, virtual pointers is a good thing here. (I am C++ programmer :wink: )

This topic is closed to new replies.

Advertisement