public RwGlTypeFaceBufferData GetVboForString(string text)
        {
            var characterRects = new List <Rectanglei>();
            var uvRects        = new List <Rectanglei>();

            int xOffset = 0;

            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];

                // Obtain character UV
                //
                if (!InternalCharacterMap.ContainsKey(c))
                {
                    LoadGlyphToTextureCache(c);
                }

                Rectanglei uvRect = InternalCharacterMap[c];

                if (uvRect.Width > 0 && uvRect.Height > 0)
                {
                    characterRects.Add(new Rectanglei(xOffset, 0, uvRect.Width, uvRect.Height));
                    uvRects.Add(uvRect);
                }

                xOffset += 32; // Temporary until character metrics are implemented
            }

            // Expand Rectanglei objects into floats for VBOs and return them
            //
            return(new RwGlTypeFaceBufferData(
                       ExpandRectangleiList(characterRects),
                       ExpandRectangleiList(uvRects)
                       ));
        }
        private void LoadGlyphToTextureCache(char c)
        {
            if (InternalCharacterMap.ContainsKey(c))
            {
                throw new InvalidOperationException("The glyph is already present in the texture cache.");
            }

            // Load and render the glyph into memory
            //
            uint glyphIndex = Face.GetCharIndex(c);

            Face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
            Face.Glyph.RenderGlyph(RenderMode.Normal);

            FTBitmap glyphBitmap = Face.Glyph.Bitmap;

            // If the bitmap is zero size, skip the texture cache and add a dummy UV map to the dictionary
            //
            if (glyphBitmap.Width == 0 || glyphBitmap.Rows == 0)
            {
                InternalCharacterMap.Add(c, new Rectanglei(0, 0, 0, 0));
                return;
            }

            // Bind the glyph cache texture in GL
            //
            GL.BindTexture(TextureTarget.Texture2D, GlTextureId);

            // Store x-offset for the next glyph to be loaded into
            //
            int charXOffset = InternalTextureWidth;

            // Resize the glyph cache texture's height if needed
            //
            if (InternalTextureHeight < glyphBitmap.Rows)
            {
                InternalTextureHeight = glyphBitmap.Rows;
            }

            InternalTextureWidth += glyphBitmap.Width;

            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgb, InternalTextureWidth, InternalTextureHeight,
                          0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero);

            // Load the glyph bitmap into the texture
            //
            byte[] gBuffer = new byte[glyphBitmap.Width * glyphBitmap.Rows * 3];

            for (int y = 0; y < glyphBitmap.Rows; y++)
            {
                for (int x = 0; x < glyphBitmap.Width; x++)
                {
                    for (int i = 0; i < 3; i++)
                    {
                        gBuffer[(x + y * glyphBitmap.Width) * 3 + i] = glyphBitmap.BufferData[x + glyphBitmap.Width * y];
                    }
                }
            }

            GL.TexSubImage2D(TextureTarget.Texture2D, 0, charXOffset, 0, glyphBitmap.Width, glyphBitmap.Rows,
                             PixelFormat.Rgb, PixelType.UnsignedByte, gBuffer);

            // Confirm the texture coordinates in the dictionary
            //
            InternalCharacterMap.Add(c, new Rectanglei(charXOffset, 0, glyphBitmap.Width, glyphBitmap.Rows));
        }