CPSC362A
Video Game Development

Project Part 3

Lights

Create a Light class and modify the Model class to draw objects with material properties.

Details

The Light class should:

  1. Inherit from the GameObject class so that moving and turning the light should affect the light’s position and spot direction.

  2. Define the default constructor Light() that initializes a light with the with the color white, i.e. rgb = (1, 1, 1).

  3. Define the constructor Light(float r, float g, float b) that initializes a light with the properties:

    1. Ambient light turned off.

    2. Diffuse and specular light set to the specified color.

    3. Constant, linear, and quadratic attenuation set to 1, 0, 0, respectively.

    4. Spot cutoff and exponent set to 180 and 1 respectively.

    5. Position set to the origin.

    6. Spot direction set down the negative z axis.

    7. Light is activate, or ‘on’.

  4. Define the setters:

    1. void SetColor(float r, float g, float b) that sets the light’s color to the specified rgb values. The light’s color should be used for its diffuse and specular color. The light’s ambient color should be black, rgb = (0, 0, 0). The method should assert that the input values define a valid color.

    2. void SetSpot(float cutoff, float exponent) that sets the light’s spotlight to the specified cutoff and and exponent values. The function should assert that cutoff is a valid angle.

    3. void SetAttenuation(float constant, float linear, float quadratic) that sets the light’s attenuation to the specified factors. The method should assert that the input values are a valid attenuations.

    4. void SetActivation(bool activate) that sets whether the light is ‘on’, that is, whether it is used in lighting calcualtions for subsequent drawing.

Note that there is a limit of 8 lights that can be created. Creating a light after 8 have already been created should print an error and create a light that is always off. Also note that it should be possible to destroy a light and have it no longer count towards the 8 light limit.

The Model class should be updated so that:

  1. The void Load(const std::string &filepath) method should:

    1. Read Wavefront obj files that define vertex normals. It should read vn lines f lines that specify vertices and normals.

      Assume that a obj file will only specify one type of face. Either vertex only lines (i.e. f 1 2 3) or vertex and normal lines (i.e. f 1//1 2//1 3//1).

    2. Read Wavefront obj files that use materials. It should handle mtllib and usemtl lines.

      The Load method should parse Phong reflectance components from mtl files. It can ignore all mtl lines that are not Ka, Kd, Ks, and Ns.

      If a face does not have a material specified or if the material name can not be found, or if there is an error reading an mtl file, the default material should be used:

      Ka: 0.2 0.2 0.2
      Kd: 0.8 0.8 0.8
      Ks: 0.0 0.0 0.0
      Ns: 0.0
      Any components of a material that are not specified should be set to their values in the default material.

      The the filename of an mtl file is relative to the obj file it is used in, not to the execution directory.

  2. The void Draw() const method should draw a model using materials and lights. If the model’s obj file specifies vertex only faces, then it should draw faces without lighting by using the materieal’s ambient component for the color. If the model’s obj file specifies vertex and normal faces, then it should draw faces using lighting.

Extra Credit: Drawing a model that specifies normals using glDrawElements may require duplicating vertices and/or normals to keep the vertex and normal arrays perfectly aligned. The easy way to do this is to duplicate all vertex normal pairs so that the vertex and normal lists have (num_faces * 3 * 3) elements in them. However, this will often waste memory. For extra credit, efficiently build the vertex, normal, and index arrays such that no vertex normal pairs are duplicated.

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
|     ├──light.cc
|     ├──light.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"
  #include "engine/light.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/model.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
    engine::Light light(1.0f, 1.0f, 1.0f);
    light.SetPosition(glm::vec3(0.0f, 0.0f, 10.0f));

    // 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 draw a model with vertex only faces and a material 5 points
2 draw a model with vertex and normal faces and an ambient material 5 points
3 draw a model with vertex and normal faces and a diffuse material 5 points
4 draw a model with vertex and normal faces and a specular material 5 points
5 draw a model with vertex and normal faces and a specular shiny material 5 points
6 draw a model with vertex and normal faces and a ambient, diffuse, and specular material 5 points
7 draw a model with multiple different materials 5 points
8 draw a model with default material 5 points
9 draw a model with default material components 5 points
10 draw a model with multiple material libraries 5 points
11 draw a model with the default light 5 points
12 draw a model with a colored light 5 points
13 draw a model with a light with a changed color 5 points
14 draw a model with a spot light 5 points
15 draw a model with a turned spot light 5 points
16 draw a model with an attenuated light 5 points
17 draw a model with an deactivated light 5 points
18 draw a model with multiple lights 5 points
19 draw a model with 9 lights 5 points
20 draw a model after destroying a light and creating 8 lights 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 / 21.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
├──light.cc
├──light.h
├──model.cc
├──model.h
├──rigid_body.cc
└──rigid_body.h

where username is your cpsc user name.

Submit your .zip file on Inquire on Friday, April 3rd.