Vitone's picture

Texture-Mapped Fonts

Hi. How can I use texture-mapped fonts with OpenTK library. Does anybody know how to use wglUseFontBitmaps?


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

OpenTK comes with a cross-platform text renderer. Include OpenTK.Utilities.dll to your project and use the OpenTK.Graphics.TextPrinter class.

Vitone's picture
the Fiddler wrote:

use the OpenTK.Graphics.TextPrinter class.

Thanks, but it's so slowly works...
Can I solve my problem in another way?

the Fiddler's picture

As long as you don't create a new TextPrinter every frame, it should be quite fast. Can you post some code?

There are many ways to implement your own text printing routines, however:

  • it's unlikely you'll get better speed, unless you trade off kerning and unicode support
  • functions like wglUseFontBitmaps are even slower than the TextPrinter
  • OpenTK does not expose platform-specific methods. You'll have to use .Net routines like DrawString (in which case you'd be better off with TextPrinter) or native libraries like FreeType and FTGL
Vitone's picture

It's my test Draw() function:

public void Draw()
        {
            // Outer Fill
            GL.Color4(ColorOuterFill);
            GL.Begin(BeginMode.Polygon);
            GL.Vertex2(points[0].X, points[0].Y);
            GL.Vertex2(points[1].X, points[1].Y);
            GL.Vertex2(points[2].X, points[2].Y);
            GL.Vertex2(points[3].X, points[3].Y);
            GL.Vertex2(points[4].X, points[4].Y);
            GL.Vertex2(points[5].X, points[5].Y);
            GL.Vertex2(points[6].X, points[6].Y);
            GL.Vertex2(points[7].X, points[7].Y);
            GL.Vertex2(points[8].X, points[8].Y);
            GL.Vertex2(points[9].X, points[9].Y);
            GL.Vertex2(points[10].X, points[10].Y);
            GL.Vertex2(points[11].X, points[11].Y);
            GL.End();
            //Inner Fill
            GL.Color4(ColorInnerFill);
            GL.Begin(BeginMode.Polygon);
            GL.Vertex2(points[12].X, points[12].Y);
            GL.Vertex2(points[13].X, points[13].Y);
            GL.Vertex2(points[14].X, points[14].Y);
            GL.Vertex2(points[15].X, points[15].Y);
            GL.Vertex2(points[16].X, points[16].Y);
            GL.Vertex2(points[17].X, points[17].Y);
            GL.Vertex2(points[18].X, points[18].Y);
            GL.Vertex2(points[19].X, points[19].Y);
            GL.Vertex2(points[20].X, points[20].Y);
            GL.Vertex2(points[21].X, points[21].Y);
            GL.Vertex2(points[22].X, points[22].Y);
            GL.Vertex2(points[23].X, points[23].Y);
            GL.End();
            // Outer Frame
            GL.Color4(ColorOuterFrame);
            GL.LineWidth((float)WidthOuterFrame);
            GL.Begin(BeginMode.LineLoop);
            GL.Vertex2(points[0].X, points[0].Y);
            GL.Vertex2(points[1].X, points[1].Y);
            GL.Vertex2(points[2].X, points[2].Y);
            GL.Vertex2(points[3].X, points[3].Y);
            GL.Vertex2(points[4].X, points[4].Y);
            GL.Vertex2(points[5].X, points[5].Y);
            GL.Vertex2(points[6].X, points[6].Y);
            GL.Vertex2(points[7].X, points[7].Y);
            GL.Vertex2(points[8].X, points[8].Y);
            GL.Vertex2(points[9].X, points[9].Y);
            GL.Vertex2(points[10].X, points[10].Y);
            GL.Vertex2(points[11].X, points[11].Y);
            GL.End();
            //Inner Frame
            GL.Color4(ColorInnerFrame);
            GL.LineWidth((float)WidthInnerFrame);
            GL.Begin(BeginMode.LineLoop);
            GL.Vertex2(points[12].X, points[12].Y);
            GL.Vertex2(points[13].X, points[13].Y);
            GL.Vertex2(points[14].X, points[14].Y);
            GL.Vertex2(points[15].X, points[15].Y);
            GL.Vertex2(points[16].X, points[16].Y);
            GL.Vertex2(points[17].X, points[17].Y);
            GL.Vertex2(points[18].X, points[18].Y);
            GL.Vertex2(points[19].X, points[19].Y);
            GL.Vertex2(points[20].X, points[20].Y);
            GL.Vertex2(points[21].X, points[21].Y);
            GL.Vertex2(points[22].X, points[22].Y);
            GL.Vertex2(points[23].X, points[23].Y);
            GL.End();
            //Text
 
<b>            _printer.Begin();
            _printer.Print("Block", _font, Color.Black,
                _rectTitle, TextPrinterOptions.Default, TextAlignment.Center);
            _printer.Print(Left.ToString() + ";" + Top.ToString(), _font, Color.Black,
                _rectFooter, TextPrinterOptions.Default, TextAlignment.Center);
            _printer.End();
</b>           
            double delta = (Height - HeightTitle - HeightFooter)/5;
            double currentY = Top+HeightTitle+delta;
 
            GL.LineWidth(1.0f);
            if (true)
            for (int i = 0; i < 4; i++)
            {
                GL.Begin(BeginMode.Lines);
                GL.Vertex2(Left - 10, currentY);
                GL.Vertex2(Left, currentY);
                if (i<2)
                {
                    GL.Vertex2(Left + Width, currentY);
                    GL.Vertex2(Left + Width + 10, currentY);
                }
                GL.End();
 
<b>                _printer.Begin();
                _printer.Print("In"+i.ToString(), _font, Color.Black,
                    new RectangleF((float)((float)Left + WidthBetweenFrames +2 ), (float)(currentY - 10), (float)((float)Width - 2 * WidthBetweenFrames-4), (float)HeightFooter), TextPrinterOptions.Default, TextAlignment.Near);
                if (i<2)
                {
                    _printer.Print("Out" + i.ToString(), _font, Color.Black,
                        new RectangleF((float)((float)Left + WidthBetweenFrames + 2), (float)(currentY - 10), (float)((float)Width - 2 * WidthBetweenFrames - 4), (float)HeightFooter), TextPrinterOptions.Default, TextAlignment.Far);
                }
                _printer.End();</b>
 
                currentY += delta;
            }
            currentY = Top + HeightTitle + delta;
 
        }
 
        private void glControl1_MouseMove(object sender, MouseEventArgs e)
        {
            glControl1.Invalidate();
        }

If I have more then 20-30 objects, It's works slow when I move any object (objects) (see attachment).

AttachmentSize
Blocks.JPG18.18 KB
the Fiddler's picture

I see, by guess is that the TextPrinter is hitting a bottleneck due to state changes: enabling / disabling texturing, blending, setting up the orthographic projection, etc. You will likely get better performance if you draw all text in one go, inside a single TextPrinter.Begin() - End() block.

This is a use case I hadn't considered in the original design, I will run some tests to see if it can be made faster.

Edit: why are you invalidating the control in the mouse move event? Seems wasteful, considering you receive dozens of MouseMove events whenever you touch your mouse.

Vitone's picture

Thanks, Fiddler.

the Fiddler wrote:

you receive dozens of MouseMove events whenever you touch your mouse.

This is test application and in this way I provide dragging of objects (as a blue-filled rectangle).

the Fiddler wrote:

You will likely get better performance if you draw all text in one go, inside a single TextPrinter.Begin() - End() block

I can't to do this, because I have a lot of blocks, and I don't know how many times function Draw() will call.

I can optimise a code, but it a little helps, because I use a lot of text.

the Fiddler's picture

Ok, I ran a few tests and it seems the Begin() - End() code is not a bottleneck.

I then made a quick optimization pass and tightened up a few costly operations, resulting in a 100% speed improvement on large datasets. That was the low-hanging fruit - there's a lot of optimization potential left, even without adding a VBO renderer.

The latest code is now in SVN, if you wish to test (rev 1798+). Building is easy, just head to the Build/ folder and follow Intsructions.txt.

Edit: I made a mistake and the changes are only in the gw-next2 branch (https://opentk.svn.sourceforge.net/svnroot/opentk/branches/gw-next2). I will fix that tomorrow, but feel free to checkout that branch if you wish to test (it is stable).

Edit 2: A quick test using VBOs showed an additional 70% improvement over the immediate mode renderer, with the potential to become even faster (via MapBuffer). The final step is to move text layout caching towards the back of the pipeline, i.e. not cache text measurements (current implementation) but rather cache vertex layouts. This could easily improve performance by an additional 30-50% at the cost of memory.

Vitone's picture

Thanks, Fiddler.
But I can't execute Build.exe...

the Fiddler's picture

Which OS? Which version of .Net do you have?

Build.exe is a .Net 2.0 application (you'll have to run it through mono if you are on Linux / Mac OS).

Alternative solution: open a command prompt, head to the Build/ folder and type:

Prebuild.exe /target vs2005 /file Prebuild.xml
Vitone's picture

I use MS Windows XP SP3, .Net Framework 3.5, MS VS 2005, 2008

I get an empty solution file:

>Prebuild.exe /target vs2005 /file Prebuild.xml
Prebuild.xml
Prebuild v2.0.4
Copyright (c) 2004-2008
Matthew Holmes (matthew@wildfiregames.com),
Dan Moorehead (dan05a@gmail.com),
David Hudson (jendave@yahoo.com),
Rob Loach (http://www.robloach.net),
C.J. Adams-Collier (cjac@colliertech.org),
See 'prebuild /usage' for help

[!] File does not exist: ../Documentation/Todo.txt
[!] File does not exist: ../Documentation/Release.txt
[!] File does not exist: ../Documentation/Changelog.txt
[!] File does not exist: ../Documentation/License.txt
[!] File does not exist: ./Instructions.txt
[!] Could not resolve Solution path: ../Source/Build
[!] Could not resolve Solution path: ../Source/Bind
[!] Could not resolve Solution path: ../Source/OpenTK
[!] Could not resolve Solution path: ../Source/Utilities
[!] Could not resolve Solution path: ../Source/Examples
Creating Visual C# 2005 solution and project files

What can I do?