Modify the Model
class to draw objects with material texture maps.
Details
The Model
class should be updated so that:
The
void Load(const std::string &filepath)
method should:- Read Wavefront obj files that define vertex texture coordinates. It should read
vt
linesf
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).
- 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 withmap_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, theLoad
function should print an error and load the material as if no texture map file is specified.- Read Wavefront obj files that define vertex texture coordinates. It should read
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.