Write an OpenGL program in C++ to read and display a 3D mesh (in .obj format). 1
ID: 3743346 • Letter: W
Question
Write an OpenGL program in C++ to read and display a 3D mesh (in .obj format).
1. Write a function, “bool ReadOBJFile(const char filename[])”, to read in an obj file and store the elements into a vertex list and face list.
2. Implement a “ComputeBoundingBox()” function, to compute the bounding box of the object, its diagonal axis length, and its center, then put the camera at:
x_cam = x_BCenter;
y_cam = y_BCenter;
z_cam = z_BCenter + 1.5 * BoundingBoxDiagonalAxisLength;
and look towards the bounding box center.
3. Implement the “Render_Mesh()” function to finish the OpenGL rendering.
4. Control the rendered mesh using keyboard+mouse-controlled rotations (R, mouse left button), panning (T, mouse left button), and zooming (Z, mouse left button) functions.
You only need to consider the simplest OBJ format. It contains a list of vertices (each row starts with a keyword “v”, e.g., v x y z), and a list of faces (each row starts with a keyword “f”, e.g., f vInd1 vInd2 vInd3). Note that the vertex index vInd starts at 1. So, if you have store vertex positions in an array, the first vertex is at position 0, and each vertex’s location is vInd-1.
Explanation / Answer
#include <iostream>
#include <fstream>
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <cmath>
struct Vertex
{
double x, y, z;
};
std::vector<Vertex> vertexList;
std::vector<Vertex> faceList;
Vertex boxCenter;
double minX,minY,minZ,maxX,maxY,maxZ,diagLength;
char keyControl;
int mouseButton;
/* Some variables to measure mouse movement */
int mousePositionX0 = 0, mousePositionY0 = 0;
float obj1_angle_x = 0.0f;
float obj1_angle_y = 0.0f;
float obj1_trans[2] = { 0.0f, 0.0f };
float camera_zoom = 0.0f;
bool ReadOBJFile(const char filename[]){
std::ifstream file(filename);
std::vector<std::string> fileContents;
//check file existance
if(!file)
{
std::cout << "Failure opening file at "" << filename <<"".";
return false;
}
//file is located
std::string buffer;
while(std:: getline(file, buffer))
{
// Add the buffer contents to our fileContents vector if it's not a comment
// (Doing the check now reduces memory usage :D
if(buffer[0] != '#' || buffer[0] != ' ')
{
fileContents.push_back(buffer);
}
}
if(fileContents.size() == 0)
{
std::cout << "File "" << filename << "" Was empty... Failure to load ";
return false;
}
//If the whole file wasn't all comments ...
//initialize min and max values
if(fileContents[0].c_str()[0] == 'v' && fileContents[0].c_str()[1] == ' ')
{
sscanf(fileContents[0].c_str(), "v %lf %lf %lf" ,&minX,&minY,&minZ);
maxX = minX;
maxY = minY;
maxZ = minZ;
}
//save the vertices and faces in the structure
for(unsigned int n = 0; n < fileContents.size(); n++){
if(fileContents[n].c_str()[0] == 'v' && fileContents[n].c_str()[1] == ' ')
{
//std::cout << "Vertex ";
double tmpx, tmpy, tmpz;
sscanf(fileContents[n].c_str(), "v %lf %lf %lf" ,&tmpx,&tmpy,&tmpz);
Vertex tmpVert = {tmpx, tmpy, tmpz};
//checking for min values
if(tmpx < minX)minX = tmpx;
if(tmpy < minY)minY = tmpy;
if(tmpz < minZ)minZ = tmpz;
//checking for max values
if(tmpx > maxX)maxX = tmpx;
if(tmpy > maxY)maxY = tmpy;
if(tmpz > maxZ)maxZ = tmpz;
vertexList.push_back(tmpVert);
}
else if(fileContents[n].c_str()[0] == 'f' && fileContents[n].c_str()[1] == ' '){
// std::cout << "Face ";
double tmpx, tmpy, tmpz;
sscanf(fileContents[n].c_str(), "f %lf %lf %lf" ,&tmpx,&tmpy,&tmpz);
Vertex tmpFace = {tmpx, tmpy, tmpz};
faceList.push_back(tmpFace);
}
}
diagLength = sqrt(pow((minX - maxX), 2) + pow((minY - maxY), 2) + pow((minZ - maxZ), 2));
boxCenter = {(minX+maxX)/2,(minY+maxY)/2, (minZ+maxZ)/2};
//std::cout<<vertexList[1].x;std::cout<<" ";
//std::cout<<vertexList[1].y;std::cout<<" ";
//std::cout<<vertexList[1].z;std::cout<<" ";
//std::cout<<"-------------- ";
//std::cout<<faceList[1].x;std::cout<<" ";
//std::cout<<faceList[1].y;std::cout<<" ";
//std::cout<<faceList[1].z;std::cout<<" ";
return true;
}
void computeBoundingBox(){
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
glPushMatrix();
glColor3f (0.0, 0.0, 0.0);
glBegin(GL_QUADS);
//FRONT
glVertex3f(minX,minY,minZ);
glVertex3f(maxX,minY,minZ);
glVertex3f(maxX,maxY,minZ);
glVertex3f(minX,maxY,minZ);
//RIGHT
glVertex3f(maxX,minY,minZ);
glVertex3f(maxX,minY,maxZ);
glVertex3f(maxX,maxY,maxZ);
glVertex3f(maxX,maxY,minZ);
//BACK
glVertex3f(maxX,maxY,maxZ);
glVertex3f(minX,maxY,maxZ);
glColor3f(1.0,0.0,0.0);
glVertex3f(minX,minY,maxZ);
glVertex3f(maxX,minY,maxZ);
glColor3f(0.0,0.0,0.0);
//LEFT
glColor3f (0.0, 0.0, 1.0);
glVertex3f(minX,minY,minZ);
glVertex3f(minX,minY,maxZ);
glColor3f (0.0, 0.0, 0.0);
glVertex3f(minX,maxY,maxZ);
glVertex3f(minX,maxY,minZ);
//TOP
glVertex3f(minX,maxY,minZ);
glVertex3f(minX,maxY,maxZ);
glVertex3f(maxX,maxY,maxZ);
glVertex3f(maxX,maxY,minZ);
//BOTTOM
glVertex3f(minX,minY,minZ);
glVertex3f(maxX,minY,minZ);
glVertex3f(maxX,minY,maxZ);
glVertex3f(minX,minY,maxZ);
glEnd();
glPopMatrix();
glPushAttrib(GL_CURRENT_BIT);
glLineWidth(3);
glColor3f (0.0, 0.0, 1.0);
glBegin(GL_LINES);
glVertex3f(minX,minY,minZ);
glVertex3f(minX,minY,maxZ);
glEnd();
glPopAttrib();
glLineWidth(1);
glPushAttrib(GL_CURRENT_BIT);
glLineWidth(3);
glColor3f (1.0, 0.0, 0.0);
glBegin(GL_LINES);
glVertex3f(minX,minY,maxZ);
glVertex3f(maxX,minY,maxZ);
glEnd();
glPopAttrib();
glLineWidth(1);
glPushAttrib(GL_CURRENT_BIT);
glLineWidth(3);
glColor3f (0.0, 1.0, 0.0);
glBegin(GL_LINES);
glVertex3f(minX,minY,maxZ);
glVertex3f(minX,maxY,maxZ);
glEnd();
glPopAttrib();
glLineWidth(1);
}
void Render_Mesh(){
glPushMatrix();
glTranslatef(obj1_trans[0], obj1_trans[1], 0);
glRotatef(obj1_angle_x, 1.0f, 0.0f, 0.0f);
glRotatef(obj1_angle_y, 0.0f, 1.0f, 0.0f);
glColor3f(1.0, 1.0, 0.0);
glBegin(GL_TRIANGLES);
for(unsigned int i = 0; i < faceList.size() -1; i++){
long firstIndex =faceList[i].x;
long secondIndex = faceList[i].y;
long thirdIndex = faceList[i].z;
//std::cout<<vertexList[firstIndex].x;std::cout<<" ";
//std::cout<<vertexList[firstIndex].y;std::cout<<" ";
//std::cout<<vertexList[firstIndex].z;std::cout<<" ";
glVertex3f(vertexList[firstIndex].x, vertexList[firstIndex].y, vertexList[firstIndex].z);
glVertex3f(vertexList[secondIndex].x, vertexList[secondIndex].y, vertexList[secondIndex].z);
glVertex3f(vertexList[thirdIndex].x, vertexList[thirdIndex].y, vertexList[thirdIndex].z);
}
glEnd();
glColor3f(0.0, 0.0, 0.0);
glPopMatrix();
glutSwapBuffers();
}
//Initializes 3D rendering
void initRendering() {
glEnable(GL_DEPTH_TEST);
glClearColor(0.7f, 0.9f, 1.0f, 1.0f); //Change the background to sky blue
keyControl = 0;
}
void drawScene(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//glShadeModel(GL_FLAT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt((GLdouble)boxCenter.x, (GLdouble)boxCenter.y, (GLdouble)boxCenter.z +1.5 * diagLength, (GLdouble)boxCenter.x, (GLdouble)boxCenter.y, (GLdouble)boxCenter.z, 0, 1, 0);
glTranslatef(0.0f, 0.0f, camera_zoom);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, 1, 0.2, 6);
glMatrixMode(GL_MODELVIEW);
computeBoundingBox();
Render_Mesh();
}
//Called when a key is pressed
void handleKeypress(unsigned char key, int x, int y) {
switch (key) {
case 'r':
keyControl = 'r';
break;
case 't':
keyControl = 't';
break;
case 'z':
keyControl = 'z';
break;
case 27: //Escape key
exit(0);
}
}
void mouseClick(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
mouseButton = GLUT_LEFT_BUTTON;
else if (button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN)
mouseButton = GLUT_MIDDLE_BUTTON;
else if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
mouseButton = GLUT_RIGHT_BUTTON;
mousePositionX0 = x;
mousePositionY0 = y;
return;
}
void mouseMove(int x, int y)
{
float frictionFactor = 0.02f; // just a scaling factor to make the mouse moving not too sensitive
/* rotation*/
if (mouseButton == GLUT_LEFT_BUTTON)
{//Rotation
if (keyControl == 'r'){
int delta_x = x - mousePositionX0;
int delta_y = y - mousePositionY0;
obj1_angle_y += delta_x;
obj1_angle_x += delta_y;
}
else if (keyControl == 't'){
obj1_trans[0] += frictionFactor * (x - mousePositionX0);
obj1_trans[1] += frictionFactor * (mousePositionY0 - y);
}
else if (keyControl == 'z'){
camera_zoom += frictionFactor * (y - mousePositionY0);
// std::cout<<camera_zoom;
}
}
if (mouseButton == GLUT_MIDDLE_BUTTON)
{
////////////do something ////////////////
}
/* zoom in and out */
if (mouseButton == GLUT_RIGHT_BUTTON)
{
////
}
mousePositionX0 = x;
mousePositionY0 = y;
glutPostRedisplay();
}
void update(int value) {
glutPostRedisplay();
glutTimerFunc(25, update, 0);
}
int main(int argc, char** argv) {
//Initialize GLUT
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(500, 500);
//Create the window
glutCreateWindow("Homework 1 - Simron Thapa");
//Read object file
if(ReadOBJFile("camel.obj")){
initRendering();
glutDisplayFunc(drawScene);
glutKeyboardFunc(handleKeypress);
// glutReshapeFunc(handleResize);
glutMouseFunc(mouseClick);
glutMotionFunc(mouseMove);
glutTimerFunc(25, update, 0); //Add a timer
glutMainLoop();
}
return 0;
}
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.