Problems with floating point coordinates accuracy?

Since the gui coordinate system uses a very small region between (-1.0,-1.0) and (1.0,1.0) is it possible that this may cause problems with the rendering pipeline when converting coordinates to screen space?

For example instead of bliting a 640 by 320 graphic onto a 640 by 320 region of screen pixels we may end up blitting 639.4 by 320.5 because of rounding errors thus introducing a small scaling distortion when we would be expecting a 1 to 1 blit.

This is very unlikely floats ( are more then enough to do the gui. It should be more like 639.99994 by 320.00005. I think the problem that you might be referring is in the aspect ratio set up.

Zingbat, don’t just say it. Do you have any proof of it ?
Perhaps you made mistake in the conversion ?
In –[HERE]– I convert it this way :

# determine window aspect ratio

  def screenPixel(self,posR2D):
      return (posSPx, posSPy)

posR2D is in render2d coord space
posSPx, posSPy are in screen pixel

It’s correct as far as I can see, so distortion larger than .0001 is obviously a sign of mistake.

The example above is just hypothetical. I don’t have any problem except when rendering pixel perfect fonts like these:

I can render text almost pixel perfect with the right font and a trick David (drwr) showed me in another thread, but there’s always a couple of pixels that get out of place no matter what i do. This should not happen and we should be able to do something like this:

Cg programming is very complex and has many traps like the accumulation of rotation errors i read in the OpenGL Redbook. I thought that if there are too many matrix transformations to render a textured quad to the screen the render may not be a 1 to 1 texture pixel to screen pixel relation by a very small margin in special cases.

A pixel size in aspect2d for a 800x600 screen is 2/600=0.00333… and for 1600x1200 screen is 2/1200=0.001666… , which are very small values for text that is supposed to be 10 sometimes as low as 7 pixels tall.

I was just wandering if this could be a possible cause, considering how small these values are.

Floating-point numbers can represent very small numbers with precision. It’s larger numbers they have trouble with. In general, you get about five digits of precision, never minding zeroes to the left or the right; so you can store the number 0.000012345 equally as precisely as the number 1,234,500.0. But what you can’t store is the number 1,2345,00.000012345.

Floating-point roundoff due to the funny aspect-ratio scaling is not likely to cause a problem in itself. It really doesn’t matter whether the scale of the screen is 1.0 … -1.0 or whether it is 1000.0 … -1000.0.

But, sure, if you want pixel accuracy there are a lot of places that things can go wrong, and if you’re not careful to control every one of them, boom, you lose a pixel or two.

If you’re having a specific problem with pixel accuracy, perhaps we can help you track down the problem if you post your code.


Check it out:

Some letters like M and N don’t render and they should as you can see from the generated bitmap font or the when using OpenOffice Writer with a point size of 6pt (8pt on the Macs) or with Gimp with a font height of 8 pixels.

The font used was called Silkscreen and it’s a typical font used for pixel perfect text rendering of small buttons and menus in html pages.

# Standard imports

import sys

# Panda imports and configuration

from pandac.PandaModules import *

loadPrcFileData("", """
fullscreen #t
win-origin 0 0
win-size 1024 768

import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class World(DirectObject):
   def __init__(self):
      # Setup basics
      winProps.setTitle("Font Test")

      # I/O bindings

      # Load font and adjust parameters
      # The font used - Silkscreen - has different adjustments is different 
      # applications to obtain it's optimal size.
      # With Gimp we need to use a font size of 8 pixels.
      # With OpenOffice Write we need to use a font size of 6 points.  
      myFont = loader.loadFont("Silkscreen.ttf")
      # The default point size is 10pt which corresponds to text being 
      # scaled to 1.0 height. The relation between point size and scale is 
      # 'font height == point size / 10' .

      # Set the pixels per unit value to how much pixels are allocated per 
      # unit of font height. The default of 30 pixels per unit. 

      # Turn off the anti-aliasing method that uses rescaling.
      # The default is 2.

      # Use filters that don't blur text.

      # The native antilias method works best for sharp and small fonts and 
      # gives good results for bigger fonts too.

      # Create text node.
      textno = TextNode("myText")
      textno.setText("SOME TEXT:\n\\|\"@#$%&/{([)]=}'?+*~^,;.:<>\n0123456789\nABCDEFGHIJKLMNOPQRSTUVWXYZ")
      np = aspect2d.attachNewNode(textno)

      # Apply an offset to fix the low-level pixel accuracy. 
      np.setTexOffset(TextureStage.getDefault(), 0.5/256, -0.5/256)

      # Pixel size (use aspect2d for square pixels) assuming a 8 pixel height
      # font size.
      pixelSize = 2.0 / winHeight

      # Font height in Panda units is 8.0 * pixelSize.
      # This value should be used when scaling the TextNode with a default 
      # scale of 1.0 .
      height = 8.0 * pixelSize

   def handleEscapeDown(self):

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

world = World()

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Run the program


So your problem with the M and N characters is not a problem with OpenGL, or Panda3D, or anything to do with vertices or texture coordinates. That’s the way the letters are rendered by the FreeType library, which is the open-source library that Panda uses to interpret font files. It is easy to prove this with a command like:


(you have to run this command after the text has been generated). When you examine the generated t.png file, you can see the font glyphs exactly as they are written into the texture, which (since you have set scale_factor to 1) is exactly the way they came out of FreeType. Lo and behold, the letters look exactly as they do onscreen.

I did a Google search on “silkscreen” and “freetype” and came up with this link: in which the author reports having similar problems with FreeType and this Silkscreen font, and he got a corrected font file from the author.

It’s an unusual .ttf file–it appears that it’s intended to be rendered at exactly one pixel size, and at larger sizes it just generates blocky letters. You might even be better off just forgoing FreeType and the ttf file altogether, and use the Sillkscreen.bmp file in your distribution there–you can generate an egg file that references into it and uses it like a font. You’ll just need to define a polygon for each character, and it shouldn’t be hard to write a simple Python program to generate this.


Another idea: you could use egg-mkfont to generate an egg file and an associated .rgb file, and hand-edit the .rgb file using Gimp to clean up any remaining weirdnesses introduced by FreeType. Then use the egg file as the font.


So it’s something to do with freetype and the way some fonts are created, that freetype doesn’t deal with very well.

"It’s an unusual .ttf file–it appears that it’s intended to be rendered at exactly one pixel size, and at larger sizes it just generates blocky letters. "

That’s how most fonts are created for this purpose. They are usually drawn like pixel blocks and are supposed to be drawn at a point size of 6pt PCs or 8pt MAC and Linux or a multiple of these. The Xara tutorial above explains the differences how PCs and other computers treat point sizes at the end of the tut.

"Another idea: you could use egg-mkfont to generate an egg file and an associated .rgb file, and hand-edit the .rgb file using Gimp to clean up any remaining weirdnesses introduced by FreeType. Then use the egg file as the font. "

That may be the best solution. I have tried other fonts like PixelArial and the some legacy fonts like the old Amiga font converted to ttf. Some work perfect without any changes others like PixelArial have minimal problems in certain letters but overall the rendering appears to be correct. PixelArial is rendered correctly with OpenOffice Write however, which points for this to be a problem with freetype no reading .ttf fonts very well in special cases like rendering pixel perfect text.

Thanks for the help.

If I were designing a font that was intended only to be used at a specific resolution, I would use one of the many standard fixed-size bitmap font formats instead of a scalable font format like TTF. That would eliminate any possibility of artifacts being introduced when the font is inadvertently scaled. But that’s just me. :slight_smile:

Note that FreeType supports many, many kinds of font formats, not just TTF. So if you have, for instance, the original Amiga font in its original (fixed-size bitmap) format, that would be likely to work much better.


There’s a good one here among other legacy fonts from old micros and arcade games:

I was looking for a good and readable font at small sizes that could be used for dialogs and log messages and found one called ProFont here that is as good as PixelArial without giving any trouble to freetype:

It’s an amazingly readable font that will look perfect at 9pt or using the Panda code above:

myFont = loader.loadFont(“ProFontWindows.ttf”)
pointSize = 10.0
pixelsPerUnit = 9

Then scale it to:
pixelSize = 2.0 / winHeight
height = pixelsPerUnit * pixelSize

There’s no difference between the way it is rendered in the ProFont site above in a browser or inside a Panda3D window: