CS409/CS809 Tutorial 7: Local Coordinate System Display

This tutorial will show you how to display models in a local coordinate system and to display billboards.  As before, the demonstration will use the ObjLibrary to handle vectors.

This tutorial assumes that you have a completed copy of Tutorial 6 or 8.  The class developed in the exercises is assumed to be named CoordinateSystem and to be in a header and source file of the same name.  This tutorial assumes that your class does not have a variable for the right vector, but it should still work fine if it does.

Setting up an OpenGL Project with the Obj Library of Files

1.      Start Microsoft Visual Studio 2015.  Select File / New / Project….  Under the list on the left, choose Installed / Templates / Visual C++. Then choose a Win32 Console Application.  Change the name to Tutorial7 or some suitable name and change the location to a hard drive located on the computer (typically C or D).  You will need to remember this location for steps 3 and 5.  Make sure the “Create folder for project” checkbox is not checked and that the “Add to source control” is not checked.  Click OK.

·         If the Chose Location window keeps popping up again, you aren’t allowed to save files at the location you chose.  Select a different location, such as the desktop.

·         Do not put your program on one of the external drives like H.  The connection is slow, and your program may not compile correctly.

2.      Click Next> or Application Settings (they go to the same place).  Check the “Empty Project” box.  Make sure the “Security development lifecycle check” is not checked.  Click Finish.

3.      Copy all your Tutorial 6 (or Tutorial 8) source files, header files, and files with names starting with glut or freeglut to your new project directory.  Rename the main6.cpp (or main8.cpp) file to main7.cpp.

4.      In Visual C++, go to Project/Add Existing Item… and change the drop-down box at the right that says All Files to choose Visual C++ Files.  There should be at least 12 files remaining (not counting folders), including main7.cpp.  Select them all and click Add.

·         Make sure that you add your copies of the files, not the original versions in your Tutorial 6 (or Tutorial 8).  If you add the wrong ones, things will get confusing very quickly and you will probably end up having to redo both tutorials.

5.      Ensure that Solution Explorer is visible (View / Solution Explorer). In the Solution Explorer, select Tutorial7 (the project name) and right click it.  Choose Add / New Filter.  A new folder icon should appear in the Solution Explorer.  Name it ObjLibrary.  (This creates an imaginary folder in the project, but it would not create a Windows folder on the computer).

6.      In the Solution Explorer, right click on the ObjLibrary folder icon and select Add / Existing item.  Go into the ObjLibrary folder on Windows and select all files.  The files should appear in the Solution Explorer inside the ObjLibrary folder.  You should get 26 files, although you will not be using most of them for this tutorial.

7.      Compile and run the program.  You should see 2 windows: a text console and a graphical window.  The graphical window should contain a copy of your Tutorial 6 (or Tutorial 8).  Exit the windows.  Closing the text window will close the graphical window, and usually vice versa.

·         You may see a pop-up window that says "This project is out of date:".  If so, click "Yes".  This tells Visual Studio to compile your program before trying to run it.

8.      For the remainder of the instructions, it is assumed that you compile and run regularly so that you can see what each change does.

9.      We will be editing the file main7.cpp, CoordinateSystem.h, and CoordinateSystem.cpp.  You do not need to change OrientationCube.h or OrientationCube.cpp.

Rotating a Teapot in its Coordinate System

10.  Add a global variable of type CoordinateSystem to main.cpp.  It will represent a coordinate system for the teapot.
CoordinateSystem teapot(Vector3(10.0, -5.0, 20.0),  // position
                        Vector3( 0.0,  1.0,  0.0),  // forward
                        Vector3( 0.0,  0.0,  1.0)); // up

where the three
Vector3s are the position, forward vector, and up vector.  You may have to add an initializing constructor to your CoordinateSystem class.  This orients the teapot in model space as if it were lying on its side.  Its lid is in its forward direction, its spout is in its right direction, and its side is up.  We do this so to match the (badly-oriented) OBJ models provided on the course website.


11.  Change the [R] key to turn the camera to look at the position obtained from the teapot coordinate system (instead of the position of the red ball).

12.  Add a function to the CoordinateSystem class named applyDrawTransformations.  It takes no parameters and will apply the coordinate system transformations appropriate for the local coordinate system.  This function should be declared as const.
void CoordinateSystem::applyDrawTransformations() const
  // code for translation will go here

  // code for rotation will go here

We will fill in the code later. Include a function prototype in the class header file.
  void applyDrawTransformations() const;

13.  Replace the commands in the display function that displays the red sphere with ones to display a teapot oriented according to the teapot coordinate system.
  glColor3f(0.75f, 0.25f, 0.25f);  // dull red
The glFrontFace commands change which side of triangles are drawn.  We need to do this because, for some reason, the GLUT model of the Utah/Newell Teapot is inside out.

14.  The program should now display a teapot at the origin surrounding the grey sphere.  However, the camera will start inside the teapot, so you will have to back up (using the [/] key) to see it.  The teapot has its lid pointing in the Y+ direction with the spout pointing in the X+ direction.

15.  In the applyDrawTransformations function, add a command to change the display position:
  glTranslated(position.x, position.y, position.z);
The teapot should now appear outside the colour cube, in the purple direction.

16.  Continuing in the applyDrawTransformations function, you will need a right vector. If your coordinate system does not include a right vector, calculate one using the getRight function:
  Vector3 right = getRight();

17.  Specify the orientation matrix for the local coordinate system.  There is no OpenGL command to do this, but it is not hard.  The orientation matrix is a 4x4 matix and is represented as a 16-element array.  For a model with Y+ as forward and Z+ as up, the maxtrix is:
  double a_matrix[16] =
  {  right.x,    right.y,    right.z,   0.0,
     forward.x,  forward.y,  forward.z, 0.0,
     up.x,       up.y,       up.z,      0.0,
     0.0,        0.0,        0.0,       1.0,  };

18.  Finally, apply the orientation matrix:
The teapot should appear to have the same orientation as before.
For the technical detail of how the orientation matrix is used, see

19.  Currently the teapot is upright with its spout pointing in the +X direction (red side of colour cube).  Rotate it around so that its spout points more-or-less away from the origin:
  teapot.rotateAroundForward(-1.0);  // radians
Remember that the local up vector for the teapot is pointing in the +Y direction (out of the teapot lid).

20.  Rotate the teapot so that its spout points down and the lid points away from the origin:
  teapot.rotateAroundUp(-2.0);  // radians
Remember that the local up vector for the teapot is pointing in the +Z direction (out the teapot side).

21.  Rotate the teapot so that the lid faces the origin again, while the spout still points down:
  teapot.rotateAroundRight(-3.0);  // radians
Remember that the local right vector for the teapot is pointing in the +X direction (below the teapot spout).

Exercises (Part 1)

22.  Convert your code to draw a teapot to a function that takes a CoordinateSystem as a parameter.  Remember to add a function prototype.

23.  Replace the single teapot with an array of 20 teapots.  Initialize each one to have a random position.  Set the position as
Vector3 position = Vector3::getRandomInRangeInclusive(
  Vector3(10.0, -50.0, 10.0),
  Vector3(50.0, -10.0, 50.0));

24.  If you have also completed Tutorial 8, initialize each teapot to have a random orientation.  Use the Vector3::getRandomUnitVector function to generate a random forward vector.  Then assign each teapot a random up vector.  The teapots should be a mix of all possible rotations.

Displaying a Billboard

25.  Our goal here is to draw a 2D triangle and display it always facing the camera.  Thus, the triangle will always appear undistorted, regardless of the camera's current orientation.  However, it will move around and change size on the screen depanding on the camera's position in the world.  Something displayed in this manner is called a billbopard, and the technique can be adopted to draw images or 2D geometry in a 3D world.  One example would be the glow around a lightbulb in an otherwise-dark room.

26.   Add a global variable of type Vector3 to main.cpp.  It will represent the position of the billboard.
Vector3 billboard(3.0, 3.0, 3.0);
We do not need a full coordinate system because a billboard does not have an orientation.

27.  Add commands to thr display function to display the billboard:

       glColor3f(0.25f, 0.25f, 0.25f);
            glVertex3d(-1.0, 0.0,  1.0);
            glVertex3d(-1.0, 0.0, -1.0);
            glVertex3d( 1.0, 0.0, -1.0);

The triangle should be displayed in dark grey with a fixed orientation.  It is in the direaction of the white corner and will only be visible from below (the -Y direction).

28.  Add a function to the CoordinateSystem class.  It should take an array of 16 doubles as a parameter and fill it with the orientation matrix for the cooridnate system.  The function should be const (or static) but publically available.  The orientation matrix is the same as the one used to display rotated models.  Unfortunately, C++ only allows array syntax for initialization, not assignment, so you will have to specify the matrix as 16 assignment statements.
void CoordinateSystem::calculateOrientationMatrix(double a_matrix[]) const
  Vector3 right = getRight();

  a_matrix[0]  = right.x;
  a_matrix[1]  = right.y;
  a_matrix[2]  = right.z;
  a_matrix[3]  = 0.0;
  a_matrix[4]  = forward.x;
  a_matrix[5]  = forward.y;
  a_matrix[6]  = forward.z;
  a_matrix[7]  = 0.0;
  a_matrix[8]  = up.x;
  a_matrix[9]  = up.y;
  a_matrix[10] = up.z;
  a_matrix[11] = 0.0;
  a_matrix[12] = 0.0;
  a_matrix[13] = 0.0;
  a_matrix[14] = 0.0;
  a_matrix[15] = 1.0;
Remember to include a function prototype in the header file.
  void calculateOrientationMatrix(double a_matrix[]) const;
I am not sure if C++11 allows you to assign arrays with initializer lists (CHECK THIS), but the syntax here always works.

29.  When drawing the billboard, apply the orientation matrix for the camera after the translation:
       double a_matrix[16];

30.  The triangle should now appear to face the camera from any direction.  The two flat sides should always face down and right on the screen.

Exercises (Part 2)

31.  Modify your applyDrawTransformations function to use the calculateOrientationMatrix function instead of calculating the matrix directly.

32.  Convert the code to draw a billboard to a function named drawBillboard that takes a position as a parameter.  Remember to add a function prototype.

33.  Change the billboard to display a yellow 4-pointed star.  One way to draw a star is as 2 overlapping diamonds.  Another is as a triangle fan with the first point at the center.  Remember that the vertexes need to be specified in counterclockwise order.

34.  Modify your function to draw a billboard to take an STL vector of positions as a parameter.  It should draw a billboard at each position, but only calculate the orientation matrix once.  Replace the single billbord position with an STL vector of 100 billboard positions.  Calculate the positions for the billboards similarly to the teapot positions.