CS40 Project 2: OpenGL Intro

Due 11:59pm Monday 11 February 2013
For this lab, you will write some small C++ classes that extend a virtual drawable base class to support functionality similar to Zelle's graphics python module. The goals of this lab are to understand the basics of creating shapes using Vertex Buffer Objects, and glDrawArrays. Additionally, you will learn or review C++ topics of inheritance, virtual methods, typedef, copy constructors, and destructors. Of course, we can always use more practice with git.
Getting started
Get a clean copy of the code from the origin/master branch and create a local and remote project2 branch by following these steps.
git fetch origin
git checkout -b project2 origin/master
git push -u private project2
The above commands assume that the default class code is on the origin remote, and your personal remote is named private. Furthermore, your working directory must be clean (no uncommitted changes to files under version control), before checking out a new branch. If this is not the case, add and commit you changes locally before switching to a project2 branch.

Once you and your partner have pushed the project2 branches, you can each checkout a shared2 branch to follow your partner's changes

git fetch partner
git checkout -b shared2 partner/project2
Note you should not merge into either the master, shared, or shared2 branches as you will later be unable to push your changes. Make sure you are on either the working or project2 branch before merging.

Later, once we get this pattern down, we can delete old local shared or project branches and just reuse the name shared to refer to our partner's current project. But I am waiting to do this until I verify that your submitted project1 correctly.

Code Overview
Starter code should appear in the projects/02 folder. The setup is similar to the w02-opengl/gtogl example from last week. The default shaders have been tweaked slightly. The fragment shader accepts a uniform color as input and uses a non-deprecated fragColor output variable instead of gl_FragColor.

The mypanelopengl class is pretty much the same too, except I have added, but not used, a Triangle* object tri. An initial goal is to finish the implementation of the Triangle class so that we could replace the current VBO, and vertices, and glDrawArrays call in mypanelopengl with tri->draw(shaderProgram);. Once we can draw one triangle this way, we can create lists of triangles, circles, rectangles, and other Drawable things and draw them all in the PaintGL method by looping over this list.

In the end, your code should support the geometric types shown below with a default constructor. Each class must have a void draw(QGLShaderProgram* prog) method that draws the object on the current display using the appropriate OpenGL commands. There may be more than one way to draw a particular type of object.

Each class above should inherit from the Drawable base class. Multiple inheritance is supported in C++ if you want a richer class hierarchy, but this is not required.

Each class must have a void move(GLfloat dx, GLfloat dy) method that translates the object by an amount dx in the horizontal direction and dy in the vertical direction. In our OpenGL context, a positive dx/dy should move the shape right/up, respectively.

Each class must have a copy constructor that can create a copy or clone of a given object. The cloned copy should initially have the same geometry and color, but can later be moved independently of the original object. Triangles can only clone other Triangles. It does not make sense to have a copy constructor for Triangles that accept Circles.

Additional geometries (points, polygons, ellipses) may be added, or you can add additional features (line thickness, outline color, etc.) if you would like.

Implementing Features
The files drawable.h, drawable.cpp and triangle.h are complete. You do not need to modify them, but you could if you want. Start your implementation by completing triangle.cpp. The constructor currently copies the input vertices into pts array. Since each shape inherits from a the Drawable base class, each shape has its own VBO, m_vbo. Following the example code in mypanelopengl.cpp add the code to initialize the VBO and copy the pts array into it.

Next, follow the outline in Triangle::draw to implement the drawing of the triangle. Once you think you have it working, modify mypanelopengl.cpp to draw the triangle tri instead of the default triangle. Once you are sure you are drawing tri correctly, you can remove code in mypanelopengl related to createVBOs, destroyVBOs, vboVertices, numVertices and vertices.

Instead of using tri, create a QList<Drawable*> list to store multiple drawable things. Test that you can draw two Triangles by looping through this list

Implement and test the move and copy constructor Triangle methods. Use the write method (instead of allocate) in the QGLBuffer class to implement the move method.

Add other shapes. Note you will need to update the CMakeLists.txt file to include/compile the new files. Don't forget to add new files to version control with git add

Read the documentation for glDrawArrays. You probably don't want to use GL_TRIANGLES as the first option for all shapes. See also page 58-60 in the text.

Add one feature at a time and go back and test. Lather, rinse, repeat until all features are implemented.

Modify mypanelopengl.cpp to create some geometric features in the initializeGL method and render. All of your objects should be stored in an QT QList<Drawable*> such that drawing the scene can be done by simply looping over the list and calling the draw method on each object.

If you program dynamically allocates memory (it probably should) using new, be sure to free the memory by calling delete in the appropriate place.

If a class dynamically allocates memory, be sure to write an appropriate destructor.

Verifying CCW orientation
Once your classes are drawing shapes, verify that all your shapes are oriented in CCW order regardless of the order specified when calling the constructor. For this phase, it would help to have a leftOf or cross product test.

I did this by creating helper functions in a geomfun.h and geomfun.cpp. My geomfun.h has the following form.

#ifndef _GEOMFUN_H
#define _GEOMFUN_H

#include <QtOpenGL>

typedef QVector2D vec2;
typedef QVector3D vec3;
typedef QVector4D vec4;

namespace cs40{

  /* return true if p1 is left of line from p2 to p3 */
  bool leftOf(const vec2& p1, const vec2& p2, const vec2& p3);

} //namespace

#endif //GEOMFUN_H
The QVector3D class can automatically upcast QVector2D objects, and there is a static method QVector3D::crossProduct
OpenGL does not have a circle primitive, so you will have to approximate a circle as a polygon. It may be helpful to use the following parametric definition of a circle with center xc,yc and radius r:
For (axis-aligned) ellipses, instead of one radius, there are two semi-major axes lengths denoted a and b in the horizontal and vertical directions. It is often convenient to specify an ellipse by opposite corners of the rectangle bounding the ellipse. Computing the semi-major axes and the parametric form of an ellipse are left as an optional exercise.
You should regularly commit your changes to the project2 branch and occasionally push to your private remote. Note you must push to your remote to share updates with your partner. Ideally you should commit changes at the end of every session of working on the project. You will be graded on work that appears in your remote project2 branch by the project deadline.