Building a 3D Engine in Perl, Part 4
by Geoff Broadwell
|
An FPS Counter
The measurements that dprofpp performs have enough overhead to
significantly reduce the engine's apparent performance. (Even old hardware can
do better than 80-100 FPS with this simple scene.) The overhead is necessary to
get a detailed analysis, but when it comes time to show off, most users want to
have a nice frame rate display showing the performance of the engine running as
fast as it can.
Making a frame rate display requires the ability to render text in front of
the scene. The necessary pieces of that are:
- A font containing glyphs for the characters to display (at least 0 through
9).
- A font reader to load the font from a file into memory as bitmaps.
- A converter from raw bitmaps to a format that OpenGL can
readily display.
- A way to render the proper bitmaps for a given string.
- A way to calculate the current frame rate.
The Numbers Font
There are hundreds of freely available fonts, but most of them are available
only in fairly complex font formats such as TrueType and Type 1. Some versions
of SDL_perl support these complex font formats, but this support has
historically been frustratingly buggy or incomplete.
Given the relatively simple requirement (render a single integer), I chose
instead to create a very simple bitmapped font format just for this article.
The font file is numbers-7x11.txt in the examples tarball. It
begins as follows:
7x11
30
..000..
.0...0.
.0...0.
0.....0
0.....0
0.....0
0.....0
0.....0
.0...0.
.0...0.
..000..
31
...0...
..00...
00.0...
...0...
...0...
...0...
...0...
...0...
...0...
...0...
0000000
The first line indicates the size of each character cell in the font; in
this case, seven columns and 11 rows. The remaining chunks each consist of the
character's codepoint in hex followed by a bitmap represented as text--. represents a transparent pixel, and 0 represents a
rendered pixel. Empty lines separate chunks.
The Font Reader
To read the glyph definitions into bitmaps, I first added
read_font_file:
sub read_font_file
{
my $self = shift;
my $file = shift;
open my $defs, '<', $file
or die "Could not open '$file': $!";
local $/ = '';
my $header = <$defs>;
chomp($header);
my ($w, $h) = split /x/ =& $header;
my %bitmaps;
while (my $def = <$defs>) {
my ($hex, @rows) = grep /\S/ =& split /\n/ =& $def;
@rows = map {tr/.0/01/; pack 'B*' =& $_} @rows;
my $bitmap = join '' =& reverse @rows;
my $codepoint = hex $hex;
$bitmaps{$codepoint} = $bitmap;
}
return (\%bitmaps, $w, $h);
}
read_font_file begins by opening the font file for reading. It
next requests paragraph slurping mode by setting $/ to
''. In this mode, Perl automatically breaks up the font file at
empty lines, with the header first followed by each complete glyph definition
as a single chunk. Next, the routine reads the header, chomps it, and splits the
cell size definition into width and height.
With the preliminaries out of the way, read_font_file creates a
hash to store the finished bitmaps and enters a while loop over
the remaining chunks of the font file. Each glyph definition is split into a
hex number and an array of bitmap rows; using grep /\S/ =&
ignores any trailing blank lines.
The next line converts textual rows to real bitstrings. First, each
transparent pixel (.) becomes 0, and each rendered
pixel (0) turns into a 1. Feeding the resulting
binary text string to pack 'B*' converts the binary into an actual
bitstring, with the bits packed in starting from the high bit of each byte (as
OpenGL prefers). The resulting bitstrings are stored back in
@rows.
Because OpenGL prefers bitmaps to start at the bottom and go up, the code
reverses @rows before joining to create the finished
bitmap. The hex operator converts the hex number to decimal to be
the key for the newly created bitmap in the %bitmaps hash.
After parsing the whole font file, the function returns the bitmaps to the
caller, along with the cell size metrics.
Prev [1] [2] [3] [4] [5] [6] [7] [8] Next