CS 488/688: Introduction to Computer Graphics

Fall 2016 Assignment 1: OpenGL


In this assignment you will learn the basics of programming real-time 3D graphics using OpenGL. You will create geometric primitives, draw them to the screen, and modify the contents of the 3D environment in response to user interaction.

In particular, you will develop a 3D sandbox environment for constructing coloured, low-resolution blocky landscapes from cubes. The program is inspired by the 3D sandbox game Minecraft, though it's a pretty improverished version of that game, since you can only build upward in columns. (Also, there is no texturing, no shading, no redstone, no creepers, no cats, etc.) Let's call it Barcraft.

The Barcraft environment

The most obvious feature of the Barcraft environment is the grid, which lies on the ground (i.e., the plane y = 0) and defines the legal squares upon which individual bars may be grown. Each grid cell is a unit square, and the grid itself is an N×N arrangement of legal cells for some constant N. In the environment, we actually draw a grid of size (N+2)×(N+2), surrounding the main grid with an extra ring of cells as a visual reference. This extra ring is purely for decoration—no bars can be grown on it.

Every live (non-ring) grid cell supports a bar. The bar is a stack of unit cubes, all of the same colour. The point of the Barcraft environment is to navigate around on the grid, growing and shrinking the bars and assigning colours to them, in order to produce a simple landscape, sculpture, or heightfield.

At any time, one cell in the grid is "active". Commands to grow or shrink bars should affect that cell. The active cell is clearly indicated in the user interface in some way, for example, by highlighting the cell or the top of the bar that lives on it. Whatever form of indication is used, it should be drawn on top of the rest of the Barcraft environment; otherwise it may be hidden by tall bars in other cells that cover up the active cell. (It may also be possible to include useful indicators in the outer ring of cells.)

For the most part, we will use the keyboard to control the modelling environment, as explained below. In addition, we can manipulate the view in a few simple ways via the mouse: we can rotate the grid around its centre to look at the design from all angles, and scale it up or down.

User interaction

Your implementation of Barcraft should support (at least) the following commands and interactions:

Skeleton code

If you haven't already, follow the instructions in Assignment 0 for downloading the framework and skeleton code. You'll find the skeleton code for this assignment in the A1/ directory. It should compile and run out of the box using the same premake4/make commands used for Assignment 0. You'll see the the skeleton program sets up the control panel, including a Quit button and single example of a colour slider and radio button. It also draws a ground grid for you. Notice that the grid is of size 18×18. The live area of the grid is 16×16, as defined by the constant DIM in A1.cpp; the program automatically adds the extra ring of cells around the live core. The program defines reasonable initial values for the projection, view and model matrices, and passes them into the vertex shader for you.

Here is a suggested sequence of steps to get you started with this assignment. (But feel free to work on the assignment in any order you want!)

  1. First and foremost, get a cube on the screen. This is probably the hardest part of the assignment! Define the geometry of a unit cube, and get it into OpenGL by creating appropriate attribute arrays and vertex buffers. You must use modern OpenGL commands here and throughout the assignment. That is, eventually you'll draw cubes using a function like glDrawArrays() or glDrawElements(), and you should never call functions like glVertex().
  2. See if you can draw a stack of cubes instead of just one.
  3. If you can do that, hook up the space and backspace keys to grow and shrink that stack.
  4. Now define an active cell, and figure out a reasonable way to indicate it in the user interface. As mentioned above, the indicator should always be drawn on top of all other 3D geometry. That's easily done by temporarily disabling depth testing. Once the indicator is drawn, add the code to move it around using the arrow keys. Then make sure that space and backspace are applied to the active cell.
  5. Next, add in the functionality for copying by holding down shift with the arrow keys.
  6. Turn the single colour selector into at least eight colours. Write the code to hook up clicks on radio buttons with changing the colour of the bar in the active cell and setting the colour for any new bars. Make sure that editing the colours modifies all bars of that colour in the world.
  7. Add in mouse rotation. This is most easily accomplished by modifying the model-to-world transformation while holding the camera fixed. As is typical in interactive programs, it'll help to keep track of the previous x position of the mouse, so that you can rotate by an amount proportional to the difference in x every time you receive a mouse event. (Note that after rotating, the arrow keys might become quite unintuitive, since the grid is rotated relative to the directions of the keys. You don't have to worry about that for this assignment.)
  8. Add scaling by responding to scroll events. There are probably several ways to do this. One would be to scale the world itself by manipulating the model-to-world transformation. Another is to move the camera closer to or farther from the centre of the grid. Either technique is fine, as long as the scaling is bounded within reasonable limits.
  9. Finally, add in the Reset button and its hotkey, setting all the values manipulated above back to the defaults.


The submission process for this assignment is basically the same as the one for Assignment 0. Prepare a ZIP file of the A1/ directory, omitting unnecessary files (executables, build files, etc.). Upload the ZIP file to LEARN.

As with Assignment 0, you must submit two other files in your A1/ directory: the README and a screenshot. If you omit either file, you will receive a deduction. See Assignment 0 for instructions about how to prepare these two files.

Other thoughts

As OpenGL has evolved, the API has actually gotten a bit more streamlined. The part of OpenGL you talk to from inside a C++ application tends to focus on telling the GPU about the pieces of data that power your program, and maybe setting a couple of system-wide parameters; most of the actual "graphics" has been pushed into the shaders.

If it helps, here is the complete set of OpenGL API calls we used in the model solution. This is for information only: you should feel free to use a subset of these functions, and to include others not on the list. But you should not use deprecated OpenGL calls from the compatibility profile: no glBegin(), glVertex(), or glEnd()!

Ideas for extensions

The core Barcraft environment is a great starting point for expansion in new directions. We'd be excited to see what additional features you can pack on top of the base interface, if you've got the time. We may award a bonus point or two for especially ambitious extensions. Here are some random ideas for extensions:

If you do extend the base interface, be sure to document your extensions in your README file. Keep in mind that you must still satisfy the core objectives listed here. If your changes are so radical that your modified program is incompatible with Barcraft, you must include a "compatibility mode" that makes the interface behave like the requirements here. (Or consider creating an entirely separate executable.)

Step 5: Objective list

Every assignment includes a list of objectives. Your mark in the assignment will be based primarily on the achievement of these objectives, with possible deductions outside the list if necessary.

Assignment 1 objectives