Sign In/My Account | View Cart  
advertisement


Listen Print Discuss

Building a 3D Engine in Perl, Part 2

by Geoff Broadwell
December 29, 2004

This article is the second in a series aimed at building a full 3D engine in Perl. The first article, Building a 3D Engine in Perl, covered basic program structure, opening an OpenGL window using SDL, basic projection and viewing setup, simple object rendering, object transformations, and depth ordering using the OpenGL depth buffer.

Editor's note: see also the rest of the series, lighting and movement, and profiling your application.

This time, I'll discuss rotating and animating the view, SDL event and keyboard handling, and compensating for frame rate variations. As a bonus, I'll demonstrate some real-world refactoring, including a conversion from procedural to (weakly) object-oriented code. Before I start, however, there were a couple of issues discovered since the first article went live:

  • In the first article, I wrote "orthogonal projection." This should be "orthographic projection," which reminds me again that no matter how many times you proofread, you can still miss the bug--in code or in prose. Unfortunately, prose is a bit harder to write tests for.
  • Todd Ross discovered a problem with SDL on FreeBSD 5.3, which caused the code to die immediately with "Bad system call (core dumped)." A short while later, he reported the workaround. He set his LD_PRELOAD environment variable with a little magic, and everything worked fine:

    setenv LD_PRELOAD /usr/lib/libc_r.so

    His research method is a good one to follow if you should encounter a similar problem. He installed another SDL_Perl application, in this case Frozen Bubble. Once he was sure it worked, he checked the code in the launcher script and found the magic shown above. A quick test confirmed that this worked for his code as well.

    Frozen Bubble is a 2D application, so if it works fine but your OpenGL programs don't, check to make sure that OpenGL works at all. Unix variants should supply the glxinfo and glxgears programs. Use glxinfo to gather details on your OpenGL driver; it serves as both a sanity check and a good addition to bug reports. glxgears does a simple animated rendering of meshing gears. This tells you whether OpenGL works correctly (at least for basic stuff) and what performance your OpenGL driver and hardware can provide. Both programs work under X on Apple's OS X 10.3 as well.

Keep your comments, questions, and bug reports coming! I'd like to recognize your contribution in the next article, but if you'd rather remain anonymous, that's fine too.

Without further ado, let's start. If you want try the code at each stage without all the typing, download the example source code. It includes a README.steps file that should help you follow along more easily.

Moving the Viewpoint

At the end of the last article, our scene had a set of axis lines roughly in the center of the screen, with a big white cube behind them and a rotated flat yellow box to the right:

sub draw_view
{
    draw_axes();

    glColor(1, 1, 1);
    glPushMatrix;
    glTranslate( 0, 0, -4);
    glScale( 2, 2, 2);
    draw_cube();
    glPopMatrix;

    glColor(1, 1, 0);
    glPushMatrix;
    glTranslate( 4, 0, 0);
    glRotate( 40, 0, 0, 1);
    glScale(.2, 1, 2);
    draw_cube();
    glPopMatrix;
}

Let's move that white cube to the right by changing the first glTranslate call as follows:

glTranslate( 12, 0, -4);

Now the right side of the window cuts off the white box. If I wanted to fix that while maintaining the relative positions of all the objects, there are a few possible changes I could make:

  • Use a wider projection (FOV) angle to see more of the scene at once. Unfortunately, it's already at 90 degrees, which is fairly wide. The perspective effect is already very strong; much wider, and the rendering will look too distorted.
  • Individually move all objects in the scene the same distance to the left. This would certainly work, but is a lot of effort, especially when there are many objects in the scene.
  • Move the viewpoint right to recenter the view. This is my preference.

I want to move the viewpoint to the right, a positive X direction, so I add +6 to the X component of the viewing translation:

sub set_view_3d
{
    glTranslate(6, -2, -10);
}

Now the scene is even farther to the right. The problem is that OpenGL combines the transformations used to modify the view (viewing transformations) with those used to transform objects in the scene (modeling transformations) in the modelview matrix. OpenGL has no way to know whether I intend any given modelview transformation to alter the view or the objects in the scene; it treats all of them as altering the objects. By translating +6 X, I effectively moved every object 6 units to the right, rather than moving my viewpoint right as intended.

I hinted at the solution before: moving the viewpoint right is equivalent to moving all objects in the scene to the left. The solution to this problem is to reverse the sign of the translation:

sub set_view_3d
{
    glTranslate(-6, -2, -10);
}

This puts the viewpoint at (6, 2, 10) where I wanted it, roughly recentering the scene. Now you can see why the viewing translation from the first article moved the viewpoint to a point slightly above (+Y) and some distance closer to the user (+Z) than the origin. I simply reversed the signs of the viewpoint coordinates I wanted, (0, 2, 10).

The scene is now centered, but with this static view, it's difficult to tell the true location and relative sizes of the objects in the scene. Perhaps I can rotate the view a bit to see this. I'll rotate it 90 degrees counterclockwise (positive rotation) around the Y axis:

sub set_view_3d
{
    glTranslate(-6, -2, -10);
    glRotate(90, 0, 1, 0);
}

Well, that certainly rotated things, but it's still hard to see where the objects really are. Why did the scene end up all over on the left like that and with the axis lines in front?

Pages: 1, 2, 3, 4, 5

Next Pagearrow