Byzantine Reality

Searching for Byzantine failures in the world around us

HOWTO: Converting Simple OpenGL Code From C++ to Perl

Over the last few weeks I’ve been trying to learn OpenGL. However, I’ve had a miserable time trying to get various implementations of it to work on my Mac, notably with C++ and Java. I was surprised to learn that installing the Perl OpenGL implementation (or POGL) is pretty simple, and that converting OpenGL code written in C++ to Perl is also reasonably easy. The following is a heuristic method you can use to do the same, using an example from the OpenGL SuperBible by Richard S. Wright, Jr. The code is freely available from his site at http://www.starstonesoftware.com/OpenGL/, and feel free to use my Perl conversion of his code.

This ended up being longer than I thought, so I’ll put a general summary here and then dive into it:
1. Install OpenGL support for Perl (POGL)
2. Change the comments from // to #
3. Import the POGL library
4. Change declarations of GL data types to just scalars.
5. Convert subroutine styles from C++ to Perl
6. Change all uses of a variable with $variable.
7. Add curly braces in one-line “if” blocks and convert “else if” to “elsif”
8. Remove the opening declaration of the main function and it’s return value
9. Change the GLUT instantiation line (the first line of the main method)
10. If functions are passed around, add \& to pass their references.

If you have Mac OS X, Perl is already installed and good to go. To install POGL, open a Terminal and type the following:
sudo cpan
install OpenGL
quit

Pretty simple! Running this as root is recommended, otherwise some of the files needed to update your basic Perl install may not go well. I said “yes” to all the optional installs and it was smooth sailing!

With that out of the way, let’s begin by analyzing Wright’s C++ OpenGL code:

// Stencil.cpp
// OpenGL SuperBible
// Richard S. Wright Jr.
// rwright@starstonesoftware.com

#include “../../shared/gltools.h” // OpenGL toolkit

// Initial square position and size
GLfloat x = 0.0f;
GLfloat y = 0.0f;
GLfloat rsize = 25;

// Step size in x and y directions
// (number of pixels to move each time)
GLfloat xstep = 1.0f;
GLfloat ystep = 1.0f;

// Keep track of windows changing width and height
GLfloat windowWidth;
GLfloat windowHeight;

///////////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
GLdouble dRadius = 0.1; // Initial radius of spiral
GLdouble dAngle; // Looping variable

// Clear blue window
glClearColor(0.0f, 0.0f, 1.0f, 0.0f);

// Use 0 for clear stencil, enable stencil test
glClearStencil(0.0f);
glEnable(GL_STENCIL_TEST);

// Clear color and stencil buffer
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

// All drawing commands fail the stencil test, and are not
// drawn, but increment the value in the stencil buffer.
glStencilFunc(GL_NEVER, 0×0, 0×0);
glStencilOp(GL_INCR, GL_INCR, GL_INCR);

// Spiral pattern will create stencil pattern
// Draw the spiral pattern with white lines. We
// make the lines white to demonstrate that the
// stencil function prevents them from being drawn
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_LINE_STRIP);
for(dAngle = 0; dAngle < 400.0; dAngle += 0.1)
{
glVertex2d(dRadius * cos(dAngle), dRadius * sin(dAngle));
dRadius *= 1.002;
}
glEnd();

// Now, allow drawing, except where the stencil pattern is 0×1
// and do not make any further changes to the stencil buffer
glStencilFunc(GL_NOTEQUAL, 0×1, 0×1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

// Now draw red bouncing square
// (x and y) are modified by a timer function
glColor3f(1.0f, 0.0f, 0.0f);
glRectf(x, y, x + rsize, y – rsize);

// All done, do the buffer swap
glutSwapBuffers();
}

///////////////////////////////////////////////////////////
// Called by GLUT library when idle (window not being
// resized or moved)
void TimerFunction(int value)
{
// Reverse direction when you reach left or right edge
if(x > windowWidth-rsize || x < -windowWidth)
xstep = -xstep;

// Reverse direction when you reach top or bottom edge
if(y > windowHeight || y < -windowHeight + rsize)
ystep = -ystep;

// Check bounds. This is in case the window is made
// smaller while the rectangle is bouncing and the
// rectangle suddenly finds itself outside the new
// clipping volume
if(x > windowWidth-rsize)
x = windowWidth-rsize-1;

if(y > windowHeight)
y = windowHeight-1;

// Actually move the square
x += xstep;
y += ystep;

// Redraw the scene with new coordinates
glutPostRedisplay();
glutTimerFunc(33,TimerFunction, 1);
}

///////////////////////////////////////////////////////////
// Called by GLUT library when the window has chanaged size
void ChangeSize(int w, int h)
{
GLfloat aspectRatio;

// Prevent a divide by zero
if(h == 0)
h = 1;

// Set Viewport to window dimensions
glViewport(0, 0, w, h);

// Reset coordinate system
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

// Establish clipping volume (left, right, bottom, top, near, far)
aspectRatio = (GLfloat)w / (GLfloat)h;
if (w <= h)
{
windowWidth = 100;
windowHeight = 100 / aspectRatio;
glOrtho (-100.0, 100.0, -windowHeight, windowHeight, 1.0, -1.0);
}
else
{
windowWidth = 100 * aspectRatio;
windowHeight = 100;
glOrtho (-windowWidth, windowWidth, -100.0, 100.0, 1.0, -1.0);
}

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

///////////////////////////////////////////////////////////
// Program entry point
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STENCIL);
glutInitWindowSize(800,600);
glutCreateWindow(“OpenGL Stencil Test”);
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutTimerFunc(33, TimerFunction, 1);
glutMainLoop();

return 0;
}

Note: There are no notes in the downloadable files on Wright’s site as to the license that this code is released under, nor have I asked him for his permission. However, as it is free to download it, I do not see a problem showing it to you here.

Begin by replacing C style comments with Perl style comments: Replace all instances of // with #. There may be a leftover /# that is not replaced correctly, look for those and replace those with ##.

Next, import the POGL libraries / packages by taking out this C++ line:
#include "../../shared/gltools.h" # OpenGL toolkit

And by putting these lines at the very top of your program:
#!/usr/bin/perl -w
use strict;
use OpenGL qw/ :all /;
use Math::Trig;

You don’

t always need the Trigonometry libraries, but with graphic manipulation (especially spheres!) it couldn’t hurt. Next, replace all declarations of the GL data types with “my $”, and for floating point numbers, remove the “f” off the end if it is there. For example,

GLfloat x = 0.0f; becomes my $x = 0.0;
Don’t forget to remove any casts to GL data types. Also, find all instances where a float is used C++ style (0.0f) and change those to Perl style (0.0).

Next, let’s straighten up the subroutines. Over the last few weeks all the C++ OpenGL code I’ve seen either takes in arguments or doesn’t, but have not returned any values (has a void return type). We can change those as follows:
Case 1: Takes no arguments:
void RenderScene(void) becomes sub RenderScene
Case 2: Takes in arguments:
void ChangeSize(int w, int h) { becomes
sub ChangeSize {
my ($w, $h) = @_; 

Either way, it isn’t particularly difficult. Now comes the tedious part. Find any uses of a variable and add in a $ before it, so h = 0; becomes $h = 0;.

Next, find all your if statements and if they don’t have curly braces on them, add them (even if it’s just one line inside the if block). If the C++ code uses “else if” statements, change those to “elsif”.

Finally, remove the beginning of the main function and its closing curly brace:
int main(int argc, char* argv[])
{

is removed, and return 0; } is removed.

Next, we need to call the GLUT library “the Perl” way, so make this change:
glutInit(&argc, argv); becomes eval {glutInit(); 1} or die "This test requires GLUT:\n$@\n";

Let’s wrap it up by appending “” to function names when we give their names to other functions:
glutReshapeFunc(ChangeSize); becomes glutReshapeFunc(\&ChangeSize);

My final output looks like this:

#!/usr/bin/perl -w
use strict;
use OpenGL qw/ :all /;
use Math::Trig;

# Stencil.cpp
# OpenGL SuperBible
# Richard S. Wright Jr.
# rwright@starstonesoftware.com
# Converted to Perl by Chris Bunch

# Initial square position and size
my $x = 0.0;
my $y = 0.0;
my $rsize = 25;

# Step size in x and y directions
# (number of pixels to move each time)
my $xstep = 1.0;
my $ystep = 1.0;

# Keep track of windows changing width and height
my $windowWidth;
my $windowHeight;

##############################
# Called to draw scene
sub RenderScene
{
my $dRadius = 0.1; # Initial radius of spiral
my $dAngle; # Looping variable

# Clear blue window
glClearColor(0.0, 0.0, 1.0, 0.0);

# Use 0 for clear stencil, enable stencil test
glClearStencil(0.0);
glEnable(GL_STENCIL_TEST);

# Clear color and stencil buffer
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

# All drawing commands fail the stencil test, and are not
# drawn, but increment the value in the stencil buffer.
glStencilFunc(GL_NEVER, 0×0, 0×0);
glStencilOp(GL_INCR, GL_INCR, GL_INCR);

# Spiral pattern will create stencil pattern
# Draw the spiral pattern with white lines. We
# make the lines white to demonstrate that the
# stencil function prevents them from being drawn
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINE_STRIP);
for($dAngle = 0; $dAngle < 400.0; $dAngle += 0.1)
{
glVertex2d($dRadius * cos($dAngle), $dRadius * sin($dAngle));
$dRadius *= 1.002;
}
glEnd();

# Now, allow drawing, except where the stencil pattern is 0×1
# and do not make any further changes to the stencil buffer
glStencilFunc(GL_NOTEQUAL, 0×1, 0×1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

# Now draw red bouncing square
# (x and y) are modified by a timer function
glColor3f(1.0, 0.0, 0.0);
glRectf($x, $y, $x + $rsize, $y – $rsize);

# All done, do the buffer swap
glutSwapBuffers();
}

##############################
# Called by GLUT library when idle (window not being
# resized or moved)
sub TimerFunction {
my ($value) = @_;
# Reverse direction when you reach left or right edge
if($x > $windowWidth-$rsize || $x < -$windowWidth) {
$xstep = -$xstep; }

# Reverse direction when you reach top or bottom edge
if($y > $windowHeight || $y < -$windowHeight + $rsize) {
$ystep = -$ystep; }

# Check bounds. This is in case the window is made
# smaller while the rectangle is bouncing and the
# rectangle suddenly finds itself outside the new
# clipping volume
if($x > $windowWidth-$rsize) {
$x = $windowWidth-$rsize-1; }

if($y > $windowHeight) {
$y = $windowHeight-1; }

# Actually move the square
$x += $xstep;
$y += $ystep;

# Redraw the scene with new coordinates
glutPostRedisplay();
glutTimerFunc(33,\&TimerFunction, 1);
}

##############################
# Called by GLUT library when the window has chanaged size
sub ChangeSize {
my ($w, $h) = @_;
my $aspectRatio;

# Prevent a divide by zero
if($h == 0) {
$h = 1; }

# Set Viewport to window dimensions
glViewport(0, 0, $w, $h);

# Reset coordinate system
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

# Establish clipping volume (left, right, bottom, top, near, far)
$aspectRatio = $w / $h;
if ($w <= $h)
{
$windowWidth = 100;
$windowHeight = 100 / $aspectRatio;
glOrtho (-100.0, 100.0, -$windowHeight, $windowHeight, 1.0, -1.0);
}
else
{
$windowWidth = 100 * $aspectRatio;
$windowHeight = 100;
glOrtho (-$windowWidth, $windowWidth, -100.0, 100.0, 1.0, -1.0);
}

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

##############################
# Program entry point
eval {glutInit(); 1} or die “This test requires GLUT:\n$@\n”;
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STENCIL);
glutInitWindowSize(800,600);
glutCreateWindow(“OpenGL Stencil Test”);
glutReshapeFunc(\&ChangeSize);
glutDisplayFunc(\&RenderScene);
glutTimerFunc(33, \&TimerFunction, 1);
glutMainLoop();

Whew! Try running it and see how it works! It takes a couple tries to get it right, but once you do, you’ve got a pretty good (although tedious) way to convert C++ OpenGL code to Perl! It takes longer than I’d like though, so I may have to write a script that does this for us…