CPSC362A
Video Game Development

Project Part 2

Game Objects

Create classes for game objects Camera and RigidBody.

Details

The Camera and RigidBody classes have functionality in common, so they should both inherit from an abstract class GameObject.

The GameObject class should:

  1. Have protected member data:

    1. glm::vec3 position the position of the game object stored as a 3D point.

    2. glm::quat orientation the orientation of the game object stored as a 3D quaternion.

  2. Define the default constructor, GameObject() that sets the position to the origin and the orientation to the identity.

  3. Define setters for the member data:

    1. void SetPosition(glm::vec3 position) set the game object’s current position to the specified position point.

    2. void SetOrientation(glm::quat orientation) set the game object’s current orientation to the specified quaternion orientation.

    3. void SetOrientation(float angle, glm::vec3 axis) set the game object’s current orientation to the specified axis-angle orientation. The parameter angle is in degrees.

  4. Define getters for the member data:

    1. glm::vec3 GetPosition() const return the game object’s current position as a point.

    2. glm::quat GetOrientation() const return the game object’s current orientation as a quaternion.

  5. Define mutators for the member data:

    1. void Move(glm::vec3 distance) that changes the game object’s current position by moving it relative to its current position and orientation by the specified vector.

    2. void Turn(GLfloat angle, glm::vec3 axis) that changes the game object’s current orientation by turning it relative to its current orientation by the specified angle, in degrees, around the specified vector.

    3. void LookAt(glm::vec3 eye, glm::vec3 center, glm::vec3 up) that changes the game object’s current position and orientation. The position should be set to the specified eye position. The orientation should be set so that it “looks at” the specified center position with the specified up vector. A game object “looks at” a point by orienting so that the negative z-axis points from the eye position to the center position. Use the glm::lookAt function to compute the orientation, but note that glm::lookAt is meant for camera transformations so it computes the inverse or the desired orientation.

The Camera class should:

  1. Inherit from the GameObject class.

  2. Define a constructor Camera(GLfloat fovy, GLfloat zNear, GLfloat zFar) that creates a camera with the specified view frustum. The parameter fovy is the degrees of the full field of view in the y dimension. It should assert that all three parameters are positive.

  3. Define the mutator void Zoom(GLfloat angle) that changes the camera’s field of view by the specified angle in degrees.

  4. Define the method void MultProjectionMatrix(int width, int height) const that multiplies the camera’s projection matrix, calcluated using it’s current view frustum and the specified window width and height in pixels, with the current matrix.

  5. Define the method void MultViewMatrix() const that multiplies the camera’s current view matrix, calculated usings it’s current position and orientation, with the current matrix.

The RigidBody class should:

  1. Inherit from the `GameObject class.

  2. Define a constructor RigidBody(Model const *model) that creates a rigid body with the specified model.

  3. Define a method void Draw() const that draws the rigid body’s model with it’s current position and orientation. Note, this will require making the model’ Draw function a const function as well.

Your code can use any library in the C++ Standard Library and the OpenGL Mathematics Library.

Your code should work on the lab computers. Test to verify.

Your code should be clean, readable, and efficient. Follow the Google C++ Style Guide for help with style. Use the cpplint tool to help check your code.

All classes should be declared inside of a namespace called engine.

Your project should have the following directory structure:

project
├──data
|  ├──model1.obj
|  ├──model2.obj
|  ├──model3.obj
|  └──...
├──makefile
├──lib
|  └──glm
|     ├──gtc
|     ├──gtx
|     └──vec3.hpp
├──src
|  └──engine
|     ├──camera.cc
|     ├──camera.h
|     ├──game_object.cc
|     ├──game_object.h
|     ├──model.cc
|     ├──model.h
|     ├──rigid_body.cc
|     └──rigid_body.h
└──test
   ├──test1.cc
   ├──test2.cc
   ├──test3.cc
   └──...

Your project should compile and run with this makefile.

Example

#include <cstdlib>
#include <cstdio>

#include "GLFW/glfw3.h"

#include "engine/model.h"
#include "engine/rigid_body.h"
#include "engine/camera.h"

int main() {
  // Initialize GLFW
  if (!glfwInit()) {
    fprintf(stderr, "Failed to initialize GLFW\n");
    exit(EXIT_FAILURE);
  }

  // Create window
  GLFWwindow* window = glfwCreateWindow(640, 480, "Test", NULL, NULL);
  if (!window) {
    glfwTerminate();
    exit(EXIT_FAILURE);
  }
  glfwMakeContextCurrent(window);

  // Initialize OpenGL
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glEnable(GL_DEPTH_TEST);

  // create rigid body and camera
  engine::Model model("data/model1.obj");
  engine::RigidBody rigidBody(&model);
  engine::Camera camera(35.0f, 0.1f, 10.0f);
  camera.LookAt(glm::vec3(0.0f, 0.0f, 5.0f),   // eye
                glm::vec3(0.0f, 0.0f, 0.0f),   // center
                glm::vec3(0.0f, 1.0f, 0.0f));  // up

  // Loop until the user closes the window
  while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
         !glfwWindowShouldClose(window)) {
    // Set the rendering viewport
    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0, 0, width, height);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Select and setup the projection matrix
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    camera.MultProjectionMatrix(width, height);

    // Select and setup the view matrix
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    camera.MultViewMatrix();

    // Rotate and draw rigid body
    rigidBody.Turn(10.0f, glm::vec3(0.0f, 1.0f, 0.0f));  // 10 deg around y axis
    rigidBody.Draw();

    // Prepare for next frame
    glPopMatrix();
    glfwSwapBuffers(window);
    glfwPollEvents();
  }

  // Clean up
  glfwDestroyWindow(window);
  glfwTerminate();
  exit(EXIT_SUCCESS);
}

Grading

The functionality of your code will be graded according to the following point scale:

1 construct default GameObject and get position 5 points
2 construct default GameObject and get orientation 5 points
3 set GameObject position to point 5 points
4 set GameObject orientation to quaternion orientation 5 points
5 set GameObject orientation to axis-angle orientation 5 points
6 move GameObject from the origin point 5 points
7 move GameObject from arbitrary point 5 points
8 turn GameObject from identity orientation 5 points
9 turn GameObject from arbitrary orientation 5 points
10 move and turn GameObject from origin and identity orientation with lookAt 5 points
11 move and turn GameObject from arbitrary point and orientation with lookAt 5 points
12 construct Camera and multiply the projection matrix 5 points
13 zoom Camera and multiply the projection matrix 5 points
14 construct Camera and multiply the view matrix 5 points
15 move Camera and multiply the view matrix 5 points
16 turn Camera and multiply the view matrix 5 points
17 construct a RigidBody and draw 5 points
18 move RigidBody and draw 5 points
19 turn RigidBody and draw 5 points
20 move and turn 2 RigidBody objects sharing a model and draw 5 points


The final grade for this part of the project will be computed as:

const double final_score = fmin(fmax(functionality_score - (days_late / 28.0 * 100.0), 0.0), 125.0);

Submission

Submit a .zip file with the following structure and contents:

username
├──camera.cc
├──camera.h
├──game_object.cc
├──game_object.h
├──model.cc
├──model.h
├──rigid_body.cc
└──rigid_body.h

where username is your cpsc user name.

Submit your .zip file on Inquire before class on Friday, March 27th.