CPSC362A
Video Game Development

Project Part 4

Textures

Modify the Model class to draw objects with material texture maps.

Details

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 texture coordinates. It should read vt lines f lines that specify texture coordinate indices, with and without vertex normals. Assume that texture coordinates are two dimensional.

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

    1. Read Wavefront obj files that use textured materials. The Load method should read mtl files that specify texture map image files as binary ppm files with map_Ka lines. Note that the draw function uses modulation to apply lighting to texture map materials so even though only an ambient texture map property is specified it will have both ambient and diffuse lighting if the material specifies Ka and Kd properties.

    It can ignore all mtl texture lines that are not map_Ka. It can assume that no other texture options are specified. The filename of a texture map image is relative to the mtl file it is used in, not to the execution directory or the obj file.

    The Load function should handle image files that are binary ppm files, files with a P6 magic number. The image dimensions must be a power of two, and the maximum color value must be 255. If a ppm file is not valid, the Load function should print an error and load the material as if no texture map file is specified.

  2. The void Draw() const method should draw a model using texture maps. A model that does not specify a texture map image, should be drawn using the material properties that are specified, or the default material if no material is specified. A model that does specify a texture map image but not vertex normals should draw the texture map using decal mode. A model that does specify a texture map image and vertex normals should render the texture map using modulate mode with material properties and lighting.

Extra Credit: It is possible to create more realistic lighting by specifying the normals of a surface using an image. Normal map images are specified using the map_Kn line in an mtl file. The rgb values of each pixel in the image are used as the xyz values of a surface normal. For extra credit, add normal mapping to your game engine.

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

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(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 / texture coordinate faces and a texture map material 44 points
2 draw a model with vertex / texture coordinate / normal faces and a texture map material 44 points
3 draw a model with a texture map material with an ppm file with header comments 2 points
4 draw a model with a texture map material with an invalid ppm file (invalid file name) 2 points
5 draw a model with a texture map material with an invalid ppm file (invalid file type) 2 points
6 draw a model with a texture map material with an invalid ppm file (invalid dimensions) 2 points
7 draw a model with a texture map material with an invalid ppm file (invalid maximum value) 2 points
8 draw a model with a texture map material with an invalid ppm file (invalid number of color values) 2 points


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

const double final_score = fmin(fmax(functionality_score - (days_late / 14.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 10th.