NeHe Tutorial 05: Solids June 2nd, 2010
Patrick Stein

Introduction

In the previous tutorial, we drew a rotating triangle and a rotating square on the screen. The next NeHe tutorial fleshes out these polygons into solid shapes: a (square-bottomed) pyramid and a cube.

Again, we’re going to start with our simple-tutorial base.

;;; *.lisp
#<:use "simple-tutorial.lisp">

Here is the resulting tut05.lisp.

;;; window title
"tut05: solid shapes"

This tutorial is almost identical to the previous one. For variety, I am going to use what I called the alternate version of managing the modelview matrix in the previous tutorial.

Rotation state

Again, our rotation state is just going to be two angles: one for the pyramid and one for the cube. They are both going to default to zero.

;;; extra decls
(defclass rotation-state ()
  ((pyramid-angle :initarg :pyramid-angle :reader pyramid-angle)
   (cube-angle :initarg :cube-angle :reader cube-angle))
  (:default-initargs :pyramid-angle 0.0
                     :cube-angle 0.0))

We’re also going to add the rotation state into our window class.

;;; extra slots
(rotation-state :initarg :rotation-state :accessor rotation-state)


And, make sure we initialize our rotation state.

;;; extra initargs
:rotation-state (make-instance 'rotation-state)

Preparing the tick function

Again, we’re going to try to stay near 60 frames per second.

;;; extra initargs (cont.)
:tick-interval (round 1000 60)  ; milliseconds per tick

And, our tick method is unchanged from the previous tutorial except that we now have a pyramid and cube instead of a triangle and quad.

;;; extra code
(defmethod glut:tick ((win my-window))
                                ; retrieve the current rotation
  (let* ((cur (rotation-state win))
                                ; retrieve the current angles
         (pyramid (pyramid-angle cur))
         (cube (cube-angle cur)))

    (setf (rotation-state win)  ; replace the rotation state
          (make-instance 'rotation-state
                         :pyramid-angle (+ pyramid 0.2)
                         :cube-angle (+ cube 0.15))))

  (glut:post-redisplay))        ; tell GLUT to redraw

Drawing rotated pyramids and cubes

In the base code, we already cleared the color buffer and the depth buffer and reset the modelview matrix. Now, we’re going to retrieve our rotations, position our pyramid, save our modelview matrix, rotate for it, draw it, and restore our saved modelview matrix. Then, we’re going to position our cube, save our modelview matrix, rotate for it, draw it, and restore our saved modelview matrix.

;;; display extra code
(let* ((cur (rotation-state win))
       (pyramid-angle (pyramid-angle cur))
       (cube-angle (cube-angle cur)))
  #<:use "position pyramid">
  (gl:with-pushed-matrix
      #<:use "rotate pyramid">
      #<:use "draw pyramid">
      )
  #<:use "position cube">
  (gl:with-pushed-matrix
      #<:use "rotate cube">
      #<:use "draw cube">
      )
  )

Drawing the pyramid

For the pyramid, we’re going to slide to the left and back into the screen.

;;; position pyramid
(gl:translate -1.5 0.0 -6.0)    ; translate left and into the screen

Then, we’re going to rotate the coordinate system around the Y-axis.

;;; rotate pyramid
                                ; rotate around the y-axis
(gl:rotate pyramid-angle 0.0 1.0 0.0)


Again, the first parameter to rotate is an angle (in degrees). The remaining parameters are the axis about which to rotate.

Now, we’re going to draw the pyramid. We’re going to draw each of the four triangles that make up the pyramid. We’re going to keep each vertexes colored the same way regardless of which face the vertex is being drawn on at the moment.

;;; draw pyramid
(gl:with-primitives :triangles  ; start drawing triangles
  #<:use "draw pyramid faces">
  )

The front face is going to be just about the same as our triangle from the previous tutorials. We’re just going to kick the bottom forward a bit.

;;; draw pyramid faces
(gl:color 1.0 0.0 0.0)          ; set the color to red
(gl:vertex 0.0 1.0 0.0)         ; top vertex (front)
(gl:color 0.0 1.0 0.0)          ; set the color to green
(gl:vertex -1.0 -1.0 1.0)       ; bottom-left vertex (front)
(gl:color 0.0 0.0 1.0)          ; set the color to blue
(gl:vertex 1.0 -1.0 1.0)        ; bottom-right vertex (front)

The right face is going to share two vertexes with our front face and introduce a third.

;;; draw pyramid faces (cont.)
(gl:color 1.0 0.0 0.0)          ; set the color to red
(gl:vertex 0.0 1.0 0.0)         ; top vertex (right)
(gl:color 0.0 0.0 1.0)          ; set the color to blue
(gl:vertex 1.0 -1.0 1.0)        ; bottom-left vertex (right)
(gl:color 0.0 1.0 0.0)          ; set the color to green
(gl:vertex 1.0 -1.0 -1.0)       ; bottom-left vertex (right)

The back face is going to share two points with the right face and one point with the front face.

;;; draw pyramid faces (cont.)
(gl:color 1.0 0.0 0.0)          ; set the color to red
(gl:vertex 0.0 1.0 0.0)         ; top vertex (back)
(gl:color 0.0 1.0 0.0)          ; set the color to green
(gl:vertex 1.0 -1.0 -1.0)       ; bottom-left vertex (back)
(gl:color 0.0 0.0 1.0)          ; set the color to blue
(gl:vertex -1.0 -1.0 -1.0)      ; bottom-left vertex (back)

The left face is going to share two points with the back face and two points with the front face (and, of course, the apex with the right face).

;;; draw pyramid faces (cont.)
(gl:color 1.0 0.0 0.0)          ; set the color to red
(gl:vertex 0.0 1.0 0.0)         ; top vertex (left)
(gl:color 0.0 0.0 1.0)          ; set the color to blue
(gl:vertex -1.0 -1.0 -1.0)      ; bottom-left vertex (left)
(gl:color 0.0 1.0 0.0)          ; set the color to green
(gl:vertex -1.0 -1.0 1.0)       ; bottom-left vertex (left)

This completes the four sides of our pyramid. The NeHe tutorial doesn’t bother drawing a bottom for the pyramid. It won’t ever be seen with the way the rest of this code is organized, but I am going to include it here for completeness.

;;; draw pyramid (cont.)
(gl:with-primitives :quads
  (gl:color 0.0 0.0 1.0)        ; set the color to blue
  (gl:vertex 1.0 -1.0 1.0)      ; front-right corner
  (gl:color 0.0 1.0 0.0)        ; set the color to green
  (gl:vertex 1.0 -1.0 -1.0)     ; back-right corner
  (gl:color 0.0 0.0 1.0)        ; set the color to blue
  (gl:vertex -1.0 -1.0 -1.0)    ; back-left corner
  (gl:color 0.0 1.0 0.0)        ; set the color to green
  (gl:vertex -1.0 -1.0 1.0))    ; front-left corner

Drawing the cube

At the point we need to position the cube, we’re sitting at the point where the triangle was drawn. So, we need to slide to the right before drawing the cube.

;;; position cube
(gl:translate  3.0 0.0 0.0)     ; translate right

Now, we’re going to rotate the coordinate system around the x-axis.

;;; rotate cube
                                ; rotate around the x-axis
(gl:rotate cube-angle 1.0 0.0 0.0)

Now, we’re going to draw the cube with each face a different color.

;;; draw cube
(gl:with-primitives :quads      ; start drawing quadrilaterals
  #<:use "draw cube faces">
  )

The top face is going to be green. We are taking care here to draw the vertexes in counter-clockwise order when viewed from above the cube.

;;; draw cube faces
(gl:color 0.0 1.0 0.0)          ; set the color to green
(gl:vertex  1.0  1.0 -1.0)      ; right top back
(gl:vertex -1.0  1.0 -1.0)      ; left top back
(gl:vertex -1.0  1.0  1.0)      ; left top front
(gl:vertex  1.0  1.0  1.0)      ; right top front

The bottom face is going to be orange. We are still taking care to draw the vertexes in counter-clockwise order when looking at this face from outside the cube. For the bottom face, that would be looking at the cube from below. For consistency, should we later want to texture map the cube, we’re going to start working from the front of the cube this time instead of the back as if we just flipped the cube 180 degrees forward and are now looking at the bottom.

;;; draw cube faces (cont.)
(gl:color 1.0 0.5 0.0)          ; set the color to orange
(gl:vertex  1.0 -1.0  1.0)      ; right bottom front
(gl:vertex -1.0 -1.0  1.0)      ; left bottom front
(gl:vertex -1.0 -1.0 -1.0)      ; left bottom back
(gl:vertex  1.0 -1.0 -1.0)      ; right bottom back

Next, we’re going to draw the front face. We are going to make it red. Again, we’re going to keep our vertexes counter clockwise and we’re going to start with the one that’s in the upper right when we’re looking at the face.

;;; draw cube faces (cont.)
(gl:color 1.0 0.0 0.0)          ; set the color to red
(gl:vertex  1.0  1.0  1.0)      ; right top front
(gl:vertex -1.0  1.0  1.0)      ; left top front
(gl:vertex -1.0 -1.0  1.0)      ; left bottom front
(gl:vertex  1.0 -1.0  1.0)      ; right bottom front

Next, we’re going to draw the back face. We are going to make it yellow. Again, we’re going to keep our vertexes counter clockwise and we’re going to start with the one that’s in the upper right when we’re looking at the face (as if we’ve rotated the cube forward 180 degrees so that what was back is now front).

;;; draw cube faces (cont.)
(gl:color 1.0 1.0 0.0)          ; set the color to yellow
(gl:vertex  1.0 -1.0 -1.0)      ; right bottom back
(gl:vertex -1.0 -1.0 -1.0)      ; left bottom back
(gl:vertex -1.0  1.0 -1.0)      ; left top back
(gl:vertex  1.0  1.0 -1.0)      ; right top back

We’re going to draw the left side in blue.

;;; draw cube faces (cont.)
(gl:color 0.0 0.0 1.0)          ; set the color to blue
(gl:vertex -1.0  1.0  1.0)      ; left top front
(gl:vertex -1.0  1.0 -1.0)      ; left top back
(gl:vertex -1.0 -1.0 -1.0)      ; left bottom back
(gl:vertex -1.0 -1.0  1.0)      ; left bottom front

For this tutorial, we’re never going to see the right side of the cube, but we’re going to draw it anyway for completeness. It will be magenta.

;;; draw cube faces (cont.)
(gl:color 1.0 0.0 1.0)          ; set the color to magenta
(gl:vertex  1.0  1.0 -1.0)      ; right top back
(gl:vertex  1.0  1.0  1.0)      ; right top front
(gl:vertex  1.0 -1.0  1.0)      ; right bottom front
(gl:vertex  1.0 -1.0 -1.0)      ; right bottom back

And, now we have a cube.

Important note

In all of the above examples, we have only used vertex inside a with-primitives call. There is good reason for this. We cannot just make a vertex whenever we want. It has to be a part of a shape. The with-primitives call starts building a shape (so far, we’ve only used triangles or quads) and then ends the shape at the end of the form. In C, we would need to do something like this to explicitly begin and end the shape.

/* example-triangle.c */
glBegin(GL_TRIANGLES);
   glVertex3f(  0.0,  1.0,  0.0 );
   glVertex3f( -1.0, -1.0,  0.0 );
   glVertex3f(  1.0, -1.0,  0.0 );
glEnd();

If you try to make a vertex that isn’t part of a shape, things get corrupted. In C, you can probably still limp along and never notice. Unless you explicitly check the OpenGL error state on a regular basis, you’ll never notice that OpenGL is screaming quietly to itself.

CL-OpenGL checks the OpenGL error state for us though. It notices right away that something has gone wrong if we try to make a vertex outside of a with-primitives call.

NeHe Tutorial 04: Rotation June 2nd, 2010
Patrick Stein

Introduction

In the previous tutorial, we drew a colored triangle and quadrilateral on the screen. The next NeHe tutorial rotates these polygons on the screen.

This tutorial will mark my first significant departures from the original NeHe tutorials. In this NeHe tutorial, he updates the polygons’ angles inside the display function. I’m going to move those out into GLUT’s tick function. The NeHe tutorial also keeps those angles in some global variables. I am going to tuck them inside my window class.

Again, we’re going to start with our simple-tutorial base.

;;; *.lisp
#<:use "simple-tutorial.lisp">

Here is the resulting tut04.lisp.

;;; window title
"tut04: rotation"

Rotation state

Our rotation state is just going to be two angles: one for the triangle and one for the quad. They are both going to default to zero.

;;; extra decls
(defclass rotation-state ()
  ((triangle-angle :initarg :triangle-angle :reader triangle-angle)
   (quad-angle :initarg :quad-angle :reader quad-angle))
  (:default-initargs :triangle-angle 0.0
                     :quad-angle 0.0))


I have opted here to make the rotation-state immutable (unless you side-step and act on the slots directly). I’m doing this largely as a personal experiment. Below, you will see that rather than update the members of the rotation state in the window, I simply replace the whole rotation state for the window. You may not wish to do this yourself, especially for something so simple as a pair of angles.

We’re also going to add the rotation state into our window class.

;;; extra slots
(rotation-state :initarg :rotation-state :accessor rotation-state)


;;; extra initargs
:rotation-state (make-instance 'rotation-state)

Preparing the tick function

CL-GLUT provides us with an easy mechanism to get a callback at a regular interval. First, we need to add another initarg when we create our window to tell it how often we’d like a callback. We’re going to try to stay near 60 frames per second. The tick interval is specified in milliseconds.

;;; extra initargs (cont.)
:tick-interval (round 1000 60)  ; milliseconds per tick

Then, we need to fill in the body of the callback. In the callback, we’re going to update the rotation state and then let GLUT know we need to redraw the screen.

;;; extra code
(defmethod glut:tick ((win my-window))
                                ; retrieve the current rotation
  (let* ((cur (rotation-state win))
                                ; retrieve the current angles
         (tri (triangle-angle cur))
         (quad (quad-angle cur)))

    (setf (rotation-state win)  ; replace the rotation state
          (make-instance 'rotation-state
                         :triangle-angle (+ tri 0.2)
                         :quad-angle (+ quad 0.15))))

  (glut:post-redisplay))        ; tell GLUT to redraw

Drawing rotated triangles and quadrilaterals

In the base code, we already cleared the color buffer and the depth buffer and reset the modelview matrix. In our previous two tutorials, we positioned the triangle, drew it, then moved from there over to where we were going to draw the quadrilateral.

Now though, we’re going to have to be more careful. We’re going to move over to where the triangle is to be drawn, rotate the coordinate system, and draw the triangle. If we then tried to translate over to where we want to draw the quadrilateral, we’d have to figure out how to do it in the rotated coordinate system. Rather than do that, we are just going to reset the transformation altogether before positioning the quad.

;;; display extra code
(let* ((cur (rotation-state win))
       (triangle-angle (triangle-angle cur))
       (quad-angle (quad-angle cur)))
  #<:use "position triangle">
  #<:use "rotate triangle">
  #<:use "draw triangle">
  #<:use "reset transformation">
  #<:use "position quad">
  #<:use "rotate quad">
  #<:use "draw quad">
  )

Drawing the triangle

For the triangle, we’re going to slide to the left and back into the screen.

;;; position triangle
(gl:translate -1.5 0.0 -6.0)    ; translate left and into the screen

Then, we’re going to rotate the coordinate system around the Y-axis.

Imagine you’re riding on your bicycle. If your front wheel were centered at the origin, it would be rotating around the X-axis.

If a revolving door is centered at the origin, it would be rotating around the Y-axis.

If your car steering wheel is centered at the origin, you rotate it around the Z-axis to turn the car.

So, to draw the triangle rotated, we’re going to rotate the coordinate system around the Y-axis.

;;; rotate triangle
                                ; rotate around the y-axis
(gl:rotate triangle-angle 0.0 1.0 0.0)


The first parameter to rotate is an angle (in degrees). The remaining parameters are the axis about which to rotate.

Now, we’re just going to draw the triangle like we did in the previous tutorial.

;;; draw triangle
(gl:with-primitives :triangles  ; start drawing triangles
  (gl:color 1.0 0.0 0.0)        ; set the color to red
  (gl:vertex 0.0 1.0 0.0)       ; top vertex
  (gl:color 0.0 1.0 0.0)        ; set the color to green
  (gl:vertex -1.0 -1.0 0.0)     ; bottom-left vertex
  (gl:color 0.0 0.0 1.0)        ; set the color to blue
  (gl:vertex 1.0 -1.0 0.0))     ; bottom-right vertex

Resetting the modelview transform

To reset the transformation, we’re just going to load the identity transformation again. Below, we will show an alternate way to write this code that doesn’t involve going the whole way back to a blank slate.

;;; reset transformation
(gl:load-identity)

Drawing the quadrilateral

In the previous tutorials, we translated 3.0 0.0 0.0 to get from where the triangle was drawn to where the quadrilateral will be drawn. This time, though, we have already gone back to center. We will need to translate to the right and back into the screen.

;;; position quad
(gl:translate  1.5 0.0 -6.0)    ; translate right and into the screen

Now, we’re going to rotate the coordinate system around the x-axis.

;;; rotate quad
                                ; rotate around the x-axis
(gl:rotate quad-angle 1.0 0.0 0.0)

Then, we’ll just draw the quadrilateral exactly as we did in the previous tutorial.

;;; draw quad
(gl:color 0.5 0.5 1.0)          ; set the color to light blue
(gl:with-primitives :quads      ; start drawing quadrilaterals
  (gl:vertex -1.0  1.0  0.0)    ; top-left vertex
  (gl:vertex  1.0  1.0  0.0)    ; top-right vertex
  (gl:vertex  1.0 -1.0  0.0)    ; bottom-right vertex
  (gl:vertex -1.0 -1.0  0.0))   ; bottom-left vertex

In the previous tutorial, we mentioned that the color is now set to light blue until we explicitly change it again. Similarly, the modelview matrix is set to be shifted to the right and into the screen and then rotated around the x-axis. It will be like this until we explicitly reset it. Fortunately, our template code resets the modelview matrix to the identity matrix at the very beginning of our display routine.

Drawing rotated triangles and quadrilaterals (alternate version)

In the previous section, we positioned, rotated, and drew the triangle. Then, we reset the modelview matrix and positioned, rotated, and drew the quadrilateral.

Sometimes, you don’t want to have to reset everything back to the the identity matrix before continuing on. For that, we can take advantage of the with-pushed-matrix macro provided by CL-OpenGL.

OpenGL maintains a (finite) stack on which you can push the modelview matrix (and a smaller stack on which you can push the projection matrix). In C, you have to explicitly push and pop the matrix:

/* example-of-push-matrix.c */
glPushMatrix();
   // do something here
glPopMatrix();


With CL-OpenGL, you can take advantage of the with- pattern to avoid having to remember to keep your pushes and pops paired up. The with-pushed-matrix effectively remembers the current transformation and restores it at the end of the form.

;;; display extra code (alternate) .lisp
(let* ((cur (rotation-state win))
       (triangle-angle (triangle-angle cur))
       (quad-angle (quad-angle cur)))
  #<:use "position triangle">
  (gl:with-pushed-matrix
      #<:use "rotate triangle">
      #<:use "draw triangle">
      )
  #<:use "position quad (original) .lisp">
  (gl:with-pushed-matrix
      #<:use "rotate quad">
      #<:use "draw quad">
      )
  )

Here, we can go back to the original positioning for the quadrilateral because at the time we’re going to move, we’re back to using the original matrix from where we positioned the triangle. We don’t have to move back into the screen, but we have to move twice as far to the right.

;;; position quad (original) .lisp
(gl:translate 3.0 0.0 0.0)      ; translate right


Everything else is the same as in the previous section.

NeHe Tutorial 03: Color June 1st, 2010
Patrick Stein

Introduction

In the previous tutorial, we drew a plain triangle and quadrilateral on the screen. The next NeHe tutorial colors this triangle and quadrilateral.

We’re going to start with our simple-tutorial base.

;;; *.lisp
#<:use "simple-tutorial.lisp">

;;; window title
"tut03: color"

Drawing colored triangles and quadrilaterals

In the base display code, we already cleared the color buffer and the depth buffer and reset the modelview matrix. Now, we’re going to translate the modelview matrix so that when we draw our triangle, it is going to be in front of our viewpoint and off to our left. Then, we’ll draw the triangle, translate over toward the right, and draw the quadrilateral.

;;; display extra code
(gl:translate -1.5 0.0 -6.0)    ; translate left and into the screen
#<:use "draw triangle">
(gl:translate 3.0 0.0 0.0)      ; translate right
#<:use "draw quadrilateral">


The above is untouched from the previous tutorial.

Drawing with vertex coloring

Now that we’ve moved over to the side a little bit and back a ways, we’re going to draw a triangle. We open with the with-primitives call and then specify the vertexes.

;;; draw triangle
(gl:with-primitives :triangles  ; start drawing triangles
  #<:use "draw triangle vertexes">
  )

Before, we simply listed the vertexes. Here, we are going to specify a color before each vertex.

;;; draw triangle vertexes
  (gl:color 1.0 0.0 0.0)        ; set the color to red
  (gl:vertex 0.0 1.0 0.0)       ; top vertex


The arguments to color are the red, green, and blue values (respectively). The values range from zero (for the darkest) to one (for the brightest). I have omitted here the optional fourth argument for the alpha channel. It defaults to 1.0.

It is important to note that we have set the global color to red. This vertex will be red because the global color was red at the time we created the vertex. If we failed to ever set the color again, everything would be red.

Here, however, we’re going to make the next vertex green.

;;; draw triangle vertexes (cont.)
  (gl:color 0.0 1.0 0.0)        ; set the color to green
  (gl:vertex -1.0 -1.0 0.0)     ; bottom-left vertex

We are going to make the final vertex blue for this triangle.

;;; draw triangle vertexes (cont.)
  (gl:color 0.0 0.0 1.0)        ; set the color to blue
  (gl:vertex 1.0 -1.0 0.0)      ; bottom-right vertex

Note: the global color is now blue. We could leave it blue and it would be blue until we set it to some other color.

Drawing with flat coloring

Drawing quadrilaterals is much like drawing triangles. Here, of course, we need four vertexes. In this case, however, we’re going to color the whole quadrilateral the same color. So, we are just going to set the global color to a light blue and then draw the quadrilateral exactly as we did in the previous tutorial.

;;; draw quadrilateral
(gl:color 0.5 0.5 1.0)          ; set the color to light blue
(gl:with-primitives :quads      ; start drawing quadrilaterals
  (gl:vertex -1.0  1.0  0.0)    ; top-left vertex
  (gl:vertex  1.0  1.0  0.0)    ; top-right vertex
  (gl:vertex  1.0 -1.0  0.0)    ; bottom-right vertex
  (gl:vertex -1.0 -1.0  0.0))   ; bottom-left vertex

Now, the color is still this light blue. It will remain so until we reset the color to red when drawing the triangle during the next time our screen is redrawn.

NeHe Tutorial 02: Drawing Triangles and Quadrilaterals June 1st, 2010
Patrick Stein

Introduction

In the previous tutorial, we made a basic shell of a CL-OpenGL application. I have slightly modified it for this tutorial so that it has some hooks where we can add in code specific to this tutorial.

In this tutorial, we’re going to draw a triangle and a quadrilateral in our window. We’re going to start with our simple-tutorial base.

;;; *.lisp
#<:use "simple-tutorial.lisp">

Here is the whole tut02.lisp.

;;; window title
"tut02: triangles and quads"

Drawing triangles and quadrilaterals

In the base display code, we already cleared the color buffer and the depth buffer and reset the modelview matrix. Now, we’re going to translate the modelview matrix so that when we draw our triangle, it is going to be in front of our viewpoint and off to our left. Then, we’ll draw the triangle, translate over toward the right, and draw the quadrilateral.

;;; display extra code
(gl:translate -1.5 0.0 -6.0)    ; translate left and into the screen
#<:use "draw triangle">
(gl:translate 3.0 0.0 0.0)      ; translate right
#<:use "draw quadrilateral">


The parameters to gl:translate are x, y, and z (respectively). After the gl:load-identity, the modelview matrix is centered at the origin with the positive x axis pointing to the right of your screen, the positive y axis pointing up your screen, and the positive z-axis pointing out of your screen.

With the way that we set up the projection matrix in the reshape method, the origin of the modelview space should be dead-center in our window.

Drawing triangles

Now that we’ve moved over to the side a little bit and back a ways, we’re going to draw a triangle. The CL-OpenGL code looks like this:

;;; draw triangle
(gl:with-primitives :triangles  ; start drawing triangles
  (gl:vertex  0.0  1.0  0.0)    ; top vertex
  (gl:vertex -1.0 -1.0  0.0)    ; bottom-left vertex
  (gl:vertex  1.0 -1.0  0.0))   ; bottom-right vertex


The with-primitives form lets OpenGL know how to use the vertexes we’re going to make. In this case, it’s going to make a triangle out of each set of three vertexes. If we had six vertexes there, we’d end up with two triangles.

Here, we drew the vertexes in clockwise order. By default, OpenGL considers this triangle to be facing away from us, then. With our current OpenGL settings, this does not make a difference since OpenGL will draw both front and back faces.

Each call to vertex gives the x, y, and z (respectively) coordinates in the modelview projection for the vertex. You will note that I used floating-point numbers here. I could have easily written them as integers like (gl:vertex 1 -1 0). CL-OpenGL would convert them to floating point numbers for me on the fly. I tend to use floating point constants when possible to try to save it the extra work. I should check, sometime, to be sure though that I don’t pay a boxing/unboxing penalty that negates the benefit.

Drawing quadrilaterals

Drawing quadrilaterals is much like drawing triangles. Here, of course, we need four vertexes.

;;; draw quadrilateral
(gl:with-primitives :quads      ; start drawing quadrilaterals
  (gl:vertex -1.0  1.0  0.0)    ; top-left vertex
  (gl:vertex  1.0  1.0  0.0)    ; top-right vertex
  (gl:vertex  1.0 -1.0  0.0)    ; bottom-right vertex
  (gl:vertex -1.0 -1.0  0.0))   ; bottom-left vertex


In this case, we drew a square. We could draw any convex quadrilateral.

Again, we drew the vertexes in clockwise order. By default, OpenGL considers this triangle to be facing away from us, then. With our current OpenGL settings, this does not make a difference since OpenGL will draw both front and back faces.

Toggling Fullscreen mode

We’re also going to add a slot that keeps track of whether or not our window is full screen.

;;; extra slots
(fullscreen :initarg :fullscreen :reader fullscreen-p)


;;; extra initargs
:fullscreen nil

Then, before we display our window, we’re going to switch to fullscreen mode if this is true.

;;; display-window extra code
(when (fullscreen-p win)        ; check to see if fullscreen needed
  (glut:full-screen))           ; if so, then tell GLUT

Switching based on keyboard event

Here, we add an extra case to the keypress handler. We destroy our window and create a new one with the fullscreen property toggled if we get an 'f' on the keyboard.

;;; keyboard extra cases
((#\f #\F)                      ; when we get an 'f'
                                ; save whether we're in fullscreen
     (let ((full (fullscreen-p win)))
       (glut:close win)         ; close the current window
       (glut:display-window     ; open a new window with fullscreen toggled
           (make-instance 'my-window
                          :fullscreen (not full)))))

NeHe Tutorials for CL-OpenGL June 1st, 2010
Patrick Stein

Introduction

The Neon Helium Productions (NeHe) online tutorials (http://nehe.gamedev.net/) are the best resources available for coders trying to learn specific OpenGL rendering techniques in C/C++. The CL-OpenGL library (http://common-lisp.net/project/cl-opengl/) is, to my mind, the most straightforward mapping of the OpenGL, GLU, and GLUT APIs into Common Lisp. In this series, I hope to combine the best of both so that the aspiring Lisp coder can quickly access these OpenGL techniques.

I am aware that someone else reworked the first six tutorials for CL-OpenGL. However, I can’t track those down any longer. The original website is gone. I am also aware that I won’t have a great deal of time for this sort of coding in the near future, but I hope to tackle a bunch of these this summer. Most of the tutorials are fairly short.

In this first tutorial, we’re going to generate a simple template file that opens an OpenGL window.

;;; *.lisp
#<:use "glut-template.lisp">

Here is the resulting: intro.lisp.

Setting Up CL-OpenGL

To get started with CL-OpenGL, you will need a Lisp implementation that supports ASDF and CFFI, a git client, and OpenGL libraries. I will probably flesh this section out at some later date. For now, I am just barely going to touch on the prerequisites.

Lisp implementation.

CFFI.

git client.

OpenGL libraries. (Under Windows, you may need FreeGlut.dylib.)

Once you have all of the above, you will need to clone the CL-OpenGL repository so that you have the sources on your machine. The CL-OpenGL git repository is http://github.com/3b/cl-opengl.git.

# clone-repository.sh
% cd /where/you/want/to/put/the/cl-opengl/sources
% git clone http://github.com/3b/cl-opengl.git

You need to ensure that the cl-opengl/ directory that you just created is included in your asdf:*central-registry* list. For example, I have this in my ~/.sbclrc file

;;; asdf-prep.lisp
(dolist (subdir (list ;; ... some other packages ...
                      #P"cl-opengl/"))
  (push (merge-pathnames subdir #P"/usr/local/asdf-install/site/")
        asdf:*central-registry*))

Basic Template

Once you have CL-OpenGL installed, you’ll be able to use the following template.

;;; glut-template.lisp
#<:use "load opengl">

(defclass my-window (glut:window)
  ()
  (:default-initargs :width 400 :height 300
                     :title "My Window Title"
                     :x 100 :y 100
                     :mode '(:double :rgb :depth)))

#<:use "initialization method">
#<:use "additional glut methods">

#<:use "create an instance of our window">

Loading OpenGL

Usually, we’re going to load OpenGL, GLU, and GLUT.

;;; load opengl
(require :asdf)                 ; need ASDF to load other things
(asdf:load-system :cl-opengl)   ; load OpenGL bindings
(asdf:load-system :cl-glu)      ; load GLU bindings
(asdf:load-system :cl-glut)     ; load GLUT bindings

Setting up OpenGL

Our initialization method can do anything we need to do in terms of loading textures or fonts or what-have-you. We could do some with the normal CLOS initialize-object method if it is stuff we can do before OpenGL is initialized. For our purposes though, we need to wait until after OpenGL is ready but before our window is displayed so we make sure to go before the glut:display-window call.

;;; initialization method
(defmethod glut:display-window :before ((win my-window))
  #<:use "prepare opengl">
)

To prepare the default OpenGL environment that we’re going to use, we’re first going to turn on smooth shading. This allows colors to blend across our polygons. Later tutorials will go into more detail about smooth shading.

;;; prepare opengl
(gl:shade-model :smooth)        ; enables smooth shading

The next line here sets the color used to clear the screen. OpenGL color values range from zero to one with zero being the darkest and one being the brightest. The parameters here are (in order) the red, green, blue, and alpha channels. The alpha channel doesn’t really come into play when clearing the screen, so it doesn’t much matter in this instance. We’re going to use a black background.

;;; prepare opengl (cont.)
(gl:clear-color 0 0 0 0)        ; background will be black

The next several lines prepare the depth buffer. OpenGL keeps a variety of buffers that are the same dimensions as your window. You were probably expecting the color buffer that stores the actual pixel values that are rendered on the screen. The depth can be used to keep track of the depth of the last item drawn to the screen or to prevent an object from drawing if it doesn’t have a depth thats less than the current value of the depth buffer for the current pixel.

;;; prepare opengl (cont.)
(gl:clear-depth 1)              ; clear buffer to maximum depth
(gl:enable :depth-test)         ; enable depth testing
(gl:depth-func :lequal)         ; okay to write pixel if its depth
                                ; is less-than-or-equal to the
                                ; depth currently written

We are also going to tell OpenGL that we’d like it to make things in perspective look as nice as possible.

;;; prepare opengl (cont.)
                                ; really nice perspective correction
(gl:hint :perspective-correction-hint :nicest)

Display function

Almost all applications will need at least a GLUT display function. Usually, they will do more than this, but this will get us started.

;;; additional glut methods
(defmethod glut:display ((win my-window))
  (gl:clear :color-buffer-bit :depth-buffer-bit)
  (gl:load-identity))

Resizing the window

The following method gets called when your window is first created and any time your window is resized. We will use it to prepare our projection matrix.

;;; additional glut methods (cont.)
(defmethod glut:reshape ((win my-window) width height)
  #<:use "glut reshape -- prepare viewport">
  #<:use "glut reshape -- prepare projection">
  #<:use "glut reshape -- switch to model view">
)

To initialize the viewport, we simply take the given width and height and use them as the horizontal and vertical extents of our coordinate system.

;;; glut reshape -- prepare viewport
(gl:viewport 0 0 width height)  ; reset the current viewport

Next, we’re going to prepare the projection matrix. We’re going to set things up for a perspective view so that distant objects appear smaller than closer objects. We’re going to assume that the window accounts for a 45-degree field of view from left to right. We’re going to assume that objects in our scene can be anywhere from 1/10 to 100 units in front of our viewpoint.

First, we switch into the mode where matrix commands will change the projection matrix. Then, we make sure we’re starting from the identity matrix. Then, we prepare our perspective transformation assuming 45-degrees from left-to-right and a proportional amount from top-to-bottom (taking care not to divide by zero in the proportion).

;;; glut reshape -- prepare projection
(gl:matrix-mode :projection)    ; select the projection matrix
(gl:load-identity)              ; reset the matrix

;; set perspective based on window aspect ratio
(glu:perspective 45 (/ width (max height 1)) 1/10 100)

Once we are done setting up the projection matrix, we need to switch back to the model-view matrix so that further transforms will affect the space we’re viewing rather than where we are viewing things from.

;;; glut reshape -- switch to model view
(gl:matrix-mode :modelview)     ; select the modelview matrix
(gl:load-identity)              ; reset the matrix

Creating and displaying our window

To create and display an instance of our window, we simply go ahead and create and instance and pass it to glut:display-window.

;;; create an instance of our window
(glut:display-window (make-instance 'my-window))

Updates In Email

Email:

l