MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


Build a Simple 3D Pipeline in Tcl

by Michael J. Norton
08/12/2005

As a kid, did you ever take something apart just to see how it worked? I still remember taking apart my old Apple ][ just to see if I could get it back together. The integrated circuits sat on sockets, so you could easily pry them off with a small screw driver. It was an interesting exercise in seeing how computer hardware functions.

It's been several decades since I last dismantled an Apple ][, but I still find myself taking things apart just to satisfy my curiosity. I don't take apart computers that much anymore. I mean, where's the fun in taking apart a laptop? Eeek!

My interest has shifted to old game software autopsies. Old popular video game source code can be found everywhere on the net. Not only that, but game console development environments are becoming available to the hobbyist. Development environments exist on Linux for the GameCube (see also Linux on the GameCube, the Sony PlayStation 2, and the old Sega Dreamcast console). These game console platforms have some interesting homebrewed 3D demos for them on the internet. These incredible hobbyist efforts sparked my interest in reverse engineering 3D graphics for the game console environment. In some cases, excluding the PlayStation 2, the Linux environments don't support the OpenGL API. Which means coding 3D from scratch is necessary.

Related Reading

Game Design Complete
By Patrick O'Luanaigh

Do you need any of these development environments to experiment with game console 3D programming? Certainly not. I am surrounded by computers at work and at home; Macintosh, PC, Sun, and SGI. Despite the differences in hardware and software on all of these systems, they all have a nice little scripting language, Tcl, which is more than capable of stepping up to the task. Using Tcl, you can literally prototype algorithms in the interpreter much in the same way you would prototype an electronics circuit on an experimenter's breadboard. In our case, we're going to assemble a game console to experiment with using Tcl. Pretty cool, huh?

Simple 3D Pipeline

Sometimes the internet can be overwhelming in information. My initial Googling for 3D game and programming graphics yielded a lot of pages that were, in a nutshell, a re-hash of college students' lectures in linear algebra and vectors, typically followed with an object-oriented coding example of a spinning cube with light sources. Have fun dissecting that code!

Relying on my ancient knowledge of 3D computer graphics, which is in an eroding data buffer in my mind, I recollected the basics of the 3D graphics pipeline. Why is this information important? Understanding the linear algebra involved is one thing; the order of implementation of the matrices is the other half of the equation. Most website tutorials make you dig this out of the complex source code that accompanies the lengthy discourse on linear algebra. As the physicist Richard Feynman used to say, Q.E.D., right? In my earlier years of college I would have prided myself in deriving the solution. Fast forward 20 years later, and my attitude has shifted to "just show me the solution." Let's take a look at a solution to a simple 3D graphics pipeline.

World Coordinates

The object we're going to render exists in a fictitious 3D world. Objects in our world are mapped to x, y, and z coordinates. For simplicity, we're going to render a polygon to our Tcl game console. The three vertices of the polygon are defined in world coordinates as follows:

set vertex_list { \ 
    {0.57735026919 0.57735026919 0.57735026919} \ 
    {0.57735026919 -0.57735026919 -0.57735026919} \ 
    {-0.57735026919 -0.57735026919 0.57735026919} }  
 

Our polygon is a triangle, which is one face of a tetrahedron, a three-sided pyramid. We're only going to render one face, as shown in Figure 1, to keep it simple.

figure 1
Figure 1. Wish output display of polygon

Rotation About an Arbitrary Axis

This is the stuff lengthy, boring, game tutorial web pages are made of. I'll spare you and just point out where we're going to perform the linear algebra transformations to display the polygon on our Tcl game console.

figure 2
Figure 2. Arbitrary rotation matrix

Figure 2. shows us our arbitrary rotation matrix. If you just want to rotate your polygon around the x axis, you adjust the alpha angle value, in radians. If you want to perform a rotation about the x, y, and z axes, then you must input the alpha, theta, and phi angle values in radians. The arbitrary rotation matrix is a linear algebra concatenation of all three transformation matrices into one universal matrix.

proc create_rotation_matrix_R { alpha theta 
phi } { 
 
 
    # calculate the trig identities 
    set sin_alpha [expr {sin($alpha)}] 
    set cos_alpha [expr {cos($alpha)}] 
    set sin_theta [expr {sin($theta)}] 
    set cos_theta [expr {cos($theta)}] 
    set sin_phi [expr {sin($phi)}] 
    set cos_phi [expr {cos($phi)}] 
 
    # Eq 3.2 Rx, Real-Time Rendering, Moller & Haines 
    set Rx { \ 
        {1 0 0 0} \ 
        {0 0 0 0} \ 
        {0 0 0 0} \ 
        {0 0 0 1}\ 
    } 
 
    # set the Rx matrix components  
    lset Rx 1 1 $cos_alpha 
    lset Rx 1 2 $sin_alpha 
    lset Rx 2 1 [expr {-1*$sin_alpha}] 
    lset Rx 2 2 $cos_alpha 
 
 
    # Eq 3.3 Rx, Real-Time Rendering, Moller & Haines 
    set Ry { \ 
        {0 0 0 0} \ 
        {0 1 0 0} \ 
        {0 0 0 0} \ 
        {0 0 0 1} \ 
    } 
 
    lset Ry 0 0 $cos_theta 
    lset Ry 0 2 [expr {-1*$sin_theta}] 
    lset Ry 2 0 $sin_theta 
    lset Ry 2 2 $cos_theta 
 
    # Eq 3.4 Rx, Real-Time Rendering, Moller & Haines 
    set Rz { \ 
        {0 0 0 0} \ 
        {0 0 0 0} \ 
        {0 0 1 0} \ 
        {0 0 0 1} \ 
    } 
    lset Rz 0 0 $cos_phi 
    lset Rz 0 1 $sin_phi 
    lset Rz 1 0 [expr {-1*$sin_phi}] 
    lset Rz 1 1 $cos_phi 
 
    return [matrix_multiply [matrix_multiply $Rx $Ry] $Rz] 
} 
 

You'll notice in the comments I mention the Real-Time Rendering text, by Moller and Haines. This was my source of information for the linear algebra equations. As a side note, this is a great book; the second edition is available for over $60. I picked up my first edition off of eBay for $0.99.

The arbitrary rotation matrix procedure, create_rotation_matrix_R, calls the procedure matrix_multiply. This is a straightforward matrix multiply that is covered in all high-school algebra 2 and college linear algebra text books.

Projecting 3D onto the 2D Game Console

The last component of our simple 3D pipeline is the 2D projection to our game console viewport. The procedure create_rotation_matrix_R returns a matrix, R, that represents the current transformed orientation of our polygon in world coordinates. Now we must pass in the vertices of the polygon and calculate the new x', y', and z' values.

The x', y', and z' values represent the vertex points in the orientation in which we would like to draw them on the game console. Now we need to project the 3D world coordinates onto the 2D game console viewport. Interestingly enough, most game tutorial sites spend a lot of character bytes on explaining the arbitrary matrix rotation described above, and mention nothing about the projection matrix.

For our projection transformation, we're going to pass in the matrix R we created with the arbitrary rotation procedure. In the back of your mind just remember this: R=P. This is what the projection procedure looks like:

proc gc_projection { x y z d P } { 
 
   
global gc_width 
   
global gc_height 
 
   
set xprime \ 
        [expr {[lindex $P 0 0]*$x+\ 
            [lindex $P 1 0]*$y+[lindex $P 2 
0]*$z}] 
   
set yprime \ 
        [expr {[lindex $P 0 1]*$x+\ 
            [lindex $P 1 1]*$y+[lindex $P 2 
1]*$z}] 
 
   
set zprime\ 
        [expr \ 
            {([lindex $P 0 2]*$x+\ 
            [lindex $P 1 2]*$y+[lindex $P 2 2]*$z 
+ 10)}] 
 
   
set aspect_ratio [expr $gc_width / $gc_height] 
   
set xp [expr ($gc_width / 2) + ($xprime * $d / $zprime)] 
   
set yp [expr ($gc_height /2 ) + \ 
       
($aspect_ratio * $yprime * $d / $zprime)] 
   
return [list $xp $yp] 
}

The aspect ratio is the ratio of the game console's width in pixels, divided by the game console's height in pixels. The projection and aspect ratio equations are covered in the book 3D Math Primer for Graphics and Game Development, by Fletcher Dunn.

The following lines of code convert the polygon from world coordinates to game console (viewport) coordinates.

set R [create_rotation_matrix_R $theta_Rx $theta_Ry 
$theta_Rz] 
 
set d 2000 
 
set display_list {} 
foreach vertex $vertex_list { 
    set x [lindex $vertex 0] 
    set y [lindex $vertex 1] 
    set z [lindex $vertex 2] 
    set screen_data [gc_projection $x $y $z $d $R] 
    puts "screen data($screen_data)" 
    lappend display_list $screen_data 
}

This code creates a display list of 2D game console coordinates that represents the polygon points, projected to 2D, that we want to render. That's it! Pretty simple pipeline for learning 3D.

Virtual Game Console

We are now able to prototype game-graphics algorithms on any computer system that runs Tcl. Tcl is a great tool for playing with graphics algorithms. You can test an idea on the fly, or examine and debug values in the interpreter. Once you get a handle on the algorithm, you can go code it in C or C++. At work, I can use my Sun workstation, and when I get home, I usually do my work exclusively on my PowerBook. For compatibility, I will SSH into my SGI from my PowerBook. The code works on all the above platforms.

The pipeline should be simple enough to expand upon. Add a 3D object, such as a cube, using polygons, or finish the code to display a three-sided pyramid. Rotate the objects and add a light source. And by all means--have fun!

Example Code

Michael J. Norton is a software engineer at Cisco Systems.


Return to the Mac DevCenter

Copyright © 2009 O'Reilly Media, Inc.