Thanks for the replies! Wiury123, I took your advice and moved the glBindTexture call out from between the begin and end calls. this didn't do much. An error was also pointed out in the method where I was building the textures, but it didn't seem to be a problem. When my scene renders, the ground is now yellow when lighting is enabled, but no other textures are appearing. Suggestions are highly appreciated, as I am trying to learn this well. I have included my entire source code. Other files, mtl file, obj file, etc can be sent by email if you would like to have a look.
/* ***********************************************************
Glut Object Veiwer
Programmed by W. Stephen Murphy
Fall 2006
ITCS 3050-002: Game Engine Design
This program reads in *.obj files and renders the contents
*********************************************************** */
#include <iostream> //Needed for standard I\O (cin, cout)
#include <fstream> //Needed for file streams
#include <vector> //Needed to create vectors
#include <sstream> //Needed for string streams
#include <stdio.h>
#include <GL/gl.h> // OpenGL itself.
#include <GL/glu.h> // GLU support library.
#include <GL/glut.h> // GLUT support library.
using namespace std;
typedef struct faceGroup
{
vector<int> face; //indexes into a vertex list
vector<int> texVerts; //indexes into the textureVertices list
int material; //name of material
} FACE;
typedef struct vert
{
float a;
float b;
float c;
} VERT;
typedef struct texVert
{
float u;
float v;
} TEXVERT;
typedef struct material
{
string name;
float ambient[3];
float diffuse[3];
float specular[3];
int illum;
float shinyness;
string map;
} MTL;
vector<FACE> objList;
vector<VERT> vertices;
vector<TEXVERT> textureVertices;
vector<MTL> mtlList;
float zVal = 0.0;
float lookZ = 0.0;
int rightPress = 0;
static GLuint texName[10];
int M_NONE;
/* String tokenizer breaks a string into words by whitespace, and adds each token to a vector
Tokenizer fuction adapted from http://www.oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html */
vector<string> tokenizer(string fileLine, vector<string> tokenBag)
{
string buf; // Have a buffer string
stringstream ss(fileLine); // Insert the string into a stream
tokenBag.clear();
while (ss >> buf)
tokenBag.push_back(buf);
return tokenBag;
}
/* Converts a string into a float. Returns an error if unable to convert */
float stringConverter(string text)
{
float number;
istringstream iss(text);
if (iss >> number) // If conversion is successful, do nothing.
{
return number;
}
else
{
cout << "Error occured while converting string to float\n";
cout << "Text to convert was : " << text << endl;
return -1;
}
}
/* Converts an integer into a string. */
string intConverter(int number)
{
ostringstream oss;
oss << number << flush;
return(oss.str());
}
void setMaterial(MTL material)
{
}
/* readMaterials method opens a *.mtl file, reads it's contents,
and creates a vector of MTL objects. */
void readMaterials(string mtlFileName)
{
fstream mtlFile;
string fileBuffer;
int count = 0;
vector<string> tokens;
try
{
mtlFile.open(mtlFileName.c_str(), ios::in);
if (!mtlFile) // If file name not found, and exception is thrown
{
cout << "Material file could not be opened\n";
throw;
}
}
catch(...) //If any exception is thrown, exit program
{
exit(1);
}
MTL newMtl;
while(!getline(mtlFile, fileBuffer).eof())
{
while(fileBuffer.empty() && !mtlFile.eof()) //Checks for empty line
continue;
tokens = tokenizer(fileBuffer, tokens);
string firstToken = tokens.at(0);
if(firstToken == "newmtl")
{
mtlList.push_back(newMtl); //first time will push on an empty object.
newMtl.name = tokens.at(1);
newMtl.map = ""; //initializes map
}
else if (firstToken =="Ka")
{
newMtl.ambient[0] = stringConverter(tokens.at(1));
newMtl.ambient[1] = stringConverter(tokens.at(2));
newMtl.ambient[2] = stringConverter(tokens.at(3));
}
else if (firstToken =="Kd")
{
newMtl.diffuse[0] = stringConverter(tokens.at(1));
newMtl.diffuse[1] = stringConverter(tokens.at(2));
newMtl.diffuse[2] = stringConverter(tokens.at(3));
}
else if (firstToken =="Ks")
{
newMtl.specular[0] = stringConverter(tokens.at(1));
newMtl.specular[1] = stringConverter(tokens.at(2));
newMtl.specular[2] = stringConverter(tokens.at(3));
}
else if (firstToken =="illum")
{
newMtl.illum = (int)stringConverter(tokens.at(1));
}
else if (firstToken =="Ns")
{
newMtl.shinyness = stringConverter(tokens.at(1));
}
else if (firstToken == "map_Kd")
{
newMtl.map = tokens.at(1);
}
else if(firstToken == "#")
{
//line flagged as comment
}
else //if flag is of unknown type
{
// cout << "Unknown line : " << fileBuffer << endl;
}
}
mtlList.push_back(newMtl);
}
/* readFile method opens a *.obj file, reads it's contents,
and creates vectors of objects to hold each type of data(vertices, faces, etc).
Data is delimited based on flags in the file (v, f, #, etc) */
void readFile()
{
fstream sceneFile;
char sceneFileName[81];
string fileBuffer;
int count = 0;
vector<string> tokens;
vector<int> face;
vector<int> faceTex; //holds the index into the texture vertex list
int currentMaterial = 0;
try
{
cout << "Enter name of file to render : ";
cin.getline(sceneFileName, 81);
sceneFile.open(sceneFileName, ios::in);
if (!sceneFile) // If file name not found, and exception is thrown
{
cout << "File could not be opened\n";
throw;
}
}
catch(...) //If any exception is thrown, exit program
{
exit(1);
}
while(!getline(sceneFile, fileBuffer).eof())
{
if(fileBuffer.empty() && !sceneFile.eof()) //Checks for empty line
continue;
tokens = tokenizer(fileBuffer, tokens);
string firstToken = tokens.at(0);
if(firstToken == "#") //if flag is for comment
{
// cout << "This is a Comment : " << fileBuffer << endl;
//Do Nothing
}
else if (firstToken == "v") //if flag is for vertex
{
VERT newVert;
newVert.a = stringConverter(tokens.at(1));
newVert.b = stringConverter(tokens.at(2));
newVert.c = stringConverter(tokens.at(3));
vertices.push_back(newVert);
}
else if (firstToken == "f") //if flag is for face
{
FACE newFace;
faceTex.clear();
face.clear();
int tokenCount = 1;
bool more = true;
while(tokens.size() > tokenCount)
{
//Create vector.
string tok = tokens.at(tokenCount); //At will throw an exception if vector overrun
int delPos = tok.length();
delPos = tok.find('/');
string remainder = tok.substr(delPos + 1, tok.length() - 1);
tok = tok.substr(0, delPos);
int addTo = (int)stringConverter(tok);
face.push_back(addTo);
if(remainder != "")
{
addTo = (int)stringConverter(remainder);
faceTex.push_back(addTo);
}
//Test for more tokens
tokenCount = tokenCount + 1;
if( !(tokens.size() >= tokenCount))
more = false;
}
newFace.texVerts = faceTex; //Set texVerts vector in FACE struc as this facetex vector
newFace.face = face; //Set face vector in FACE struc as this face vector
newFace.material = currentMaterial; //Sets the material in the FACE struc as the current index into the material array
objList.push_back(newFace);
}
else if (firstToken == "usemtl")
{
for(int i = 0; i < mtlList.size(); i++)
{
if(tokens.at(1) == mtlList.at(i).name)
{
currentMaterial = i;
}
}
}
else if (firstToken == "g") //if flag is for grouping
{
//cout << "This is a group : " << fileBuffer << endl;
//Do nothing
}
else if (firstToken =="vn") //if flag is for vertex normal
{
//cout << "This is a vertex normal : " << fileBuffer << endl;
//Do Nothing
}
else if (firstToken == "vt") //if flag is for vertex texture
{
TEXVERT newTexVert;
newTexVert.u = stringConverter(tokens.at(1));
newTexVert.v = stringConverter(tokens.at(2));
textureVertices.push_back(newTexVert);
}
else if (firstToken == "s") //if flag is for smoothing group
{
//cout << "This is a smoothing group" << fileBuffer << endl;
//Do Nothing
}
else if (firstToken == "mtllib")
{
//Open material file
readMaterials(tokens.at(1));
}
else //if flag is of unknown type
{
// cout << "Unknown line : " << fileBuffer << endl;
}
}
}
void init()
{
//Chooses color to clear buffer to
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glShadeModel (GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// FOV, Aspect, Z Near, Z Far
gluPerspective(100.0, 1.0, 0.1, 500.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* Defines an ambient, a diffuse, and a positional light source */
GLfloat light_position0[] = {0.0, 200.0, 50.0, 0.0};
GLfloat light_ambient0[] = {1.0, 1.0, 1.0, 1.0};
GLfloat light_diffuse0[] = {1.0, 1.0, 1.0, 1.0};
GLfloat light_position1[] = {0.0, 200.0, -50.0, 0.0};
GLfloat light_ambient1[] = {1.0, 1.0, 1.0, 1.0};
GLfloat light_diffuse1[] = {1.0, 1.0, 1.0, 1.0};
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient0);
glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse0);
glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient1);
glLightfv(GL_LIGHT1, GL_POSITION, light_position1);
glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse1);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);
zVal = 100.0;
lookZ = 0.0;
gluLookAt(0.0, 6.0, zVal, 0.0, 0.0, lookZ, 0.0, 1.0, 0.0);
}
void makeTextures()
{
FILE *file;
string fileBuffer;
vector<string> tokens;
unsigned char* image;
int height;
int width;
int maxValue;
glEnable(GL_TEXTURE_2D);
glGenTextures((GLuint)mtlList.size(), texName);
for(int i = 0; i < mtlList.size(); i++)
{
glBindTexture(GL_TEXTURE_2D, texName[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
if(mtlList.at(i).map != "")
{
FILE * fp = fopen(mtlList.at(i).map.c_str(), "r");
char ch = ' ';
for(;ch != '\n';)
{
ch = getc(fp);
}
ch = ' ';
for(;ch != '\n';)
{
ch = getc(fp);
}
fscanf(fp, "%d%d%d\n", &width, &height, &maxValue);
image = new unsigned char[width * height * 3];
fread ((unsigned char*) image, width*height, 3, fp);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
delete[] (unsigned char*) image;
}
}
}
void draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
vector<int> tempFace;
vector<int> tempTex;
int corner;
int texCorner;
VERT tempVert;
TEXVERT tempVT;
int currentMaterial = -1;
for(int objLoop = 0; objLoop < objList.size(); objLoop++) //Loops through the list of face structures
{
tempFace = objList[objLoop].face;
tempTex = objList[objLoop].texVerts;
if(currentMaterial != texName[objList[objLoop].material -1])
{
glBindTexture(GL_TEXTURE_2D, texName[objList[objLoop].material - 1]);
glMaterialfv(GL_FRONT, GL_AMBIENT, mtlList[objList[objLoop].material].ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mtlList[objList[objLoop].material].diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mtlList[objList[objLoop].material].specular);
glMaterialf(GL_FRONT, GL_SHININESS, mtlList[objList[objLoop].material].shinyness);
currentMaterial = texName[objList[objLoop].material - 1];
}
if(tempTex.size() != 0)
{
if(tempFace.size() == 3)
glBegin(GL_TRIANGLES);
else
glBegin(GL_POLYGON);
for(int facePos = 0; facePos < tempFace.size(); facePos++) //Loops through the vertices of a face
{
// glColor3f(0.0, 0.0, 1.0);
corner = tempFace[facePos] - 1; //need to decrement by 1, since array indicies start at 0
texCorner = tempTex[facePos] -1;
tempVert = vertices[corner];
tempVT = textureVertices[texCorner];
glTexCoord2f(tempVT.u, tempVT.v);
glVertex3f(tempVert.a, tempVert.b, tempVert.c);
}
glEnd();
}
else
{
// glColor3f(1.0, 0.0, 1.0);
glBegin(GL_POLYGON);
for(int facePos = 0; facePos < tempFace.size(); facePos++) //Loops through the vertices of a face
{
int corner = tempFace.at(facePos) - 1; //need to decrement by 1, since array indicies start at 0
tempVert = vertices.at(corner);
glVertex3f(tempVert.a, tempVert.b, tempVert.c);
}
glEnd();
}
}
glutSwapBuffers();
}
void keys(unsigned char key, int x, int y) //Any key pressed calls init to reset the scene, except...
{
if(key == 27) //The excape key exits aborts the program
{
exit(0);
}
else if(key == 'l') //The 'l' key turns lighting on
{
glEnable(GL_LIGHTING);
glutPostRedisplay();
}
else if(key == 'o')
{
glDisable(GL_LIGHTING);
glutPostRedisplay();
}
else if(key == 'b')
{
glEnable(GL_BLEND);
glutPostRedisplay();
}
else if(key == 'n')
{
glDisable(GL_BLEND);
glutPostRedisplay();
}
else
{
init();
glutPostRedisplay();
}
}
/* Mouse function listens for mouse clicks */
void mouse(int button, int state, int x, int y)
{
if (button == GLUT_RIGHT_BUTTON)
{
if (state == GLUT_DOWN)
{
rightPress = 1;
//button pressed
}
if (state == GLUT_UP)
{
rightPress = 0;
}
}
}
/* mouseMotion listens for active motion (when button is pressed)*/
void mouseMotion(int x, int y)
{
glMatrixMode(GL_MODELVIEW);
if((rightPress == 1))
{
if(y > ((glutGet(GLUT_WINDOW_HEIGHT))/2)) // If downward motion is detected while shift is held, camera moves backward
{
zVal += 1.0;
lookZ += 1.0;
glLoadIdentity();
gluLookAt(0.0, 6.0, zVal, 0.0, 0.0, lookZ, 0.0, 1.0, 0.0);
glutPostRedisplay();
}
else //If upward motion is detected while shift is held, camera moves forward
{
zVal -= 1.0;
lookZ -= 1.0;
glLoadIdentity();
gluLookAt(0.0, 6.0, zVal, 0.0, 0.0, lookZ, 0.0, 1.0, 0.0);
glutPostRedisplay();
}
}
else
{
if(x > ((glutGet(GLUT_WINDOW_WIDTH))/2)) // If Rightward motion is detected, object is rotated 5.625 degrees to the positive side
{
glRotatef(5.625, 0.0, 1.0, 0.0);
glutPostRedisplay();
// Motion on right side of window
}
else //If leftward motion is detected, object is rotated 5.625 degrees to the negitive side
{
glRotatef(-5.625, 0.0, 1.0, 0.0);
glutPostRedisplay();
// Motion on left of window
}
}
}
void passiveMouseMotion(int x, int y)
{
//this function can be used to detect passive mouse motion
}
void menuFunc(int value)
{
//Do Nothing
}
void makeMenu()
{
glutCreateMenu(menuFunc);
glutAddMenuEntry("b key enables blending ", M_NONE);
glutAddMenuEntry("n key disables blending", M_NONE);
glutAddMenuEntry("l key enables lighting ", M_NONE);
glutAddMenuEntry("o key disables lighting", M_NONE);
glutAddMenuEntry("ESC key exits", M_NONE);
glutAddMenuEntry("Any other key resets scene", M_NONE);
glutAttachMenu(GLUT_MIDDLE_BUTTON);
}
/*Handles resizing the window. Uses the w/h ratio as aspect */
void resize(int w, int h)
{
if(h == 0) //prevents divide by 0
h = 1;
float ratio = 1.0 * w / h;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Set the viewport to be the entire window
glViewport(0, 0, w, h);
// Set perspective.
gluPerspective(100.0, ratio, 0.1, 500.0);
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("OBJ Renderer");
makeMenu();
init(); //Clears the window and sets the projection
readFile();
makeTextures(); //Registers texures in glut
glutDisplayFunc(draw);
glutMouseFunc(mouse);
glutMotionFunc(mouseMotion);
glutPassiveMotionFunc(passiveMouseMotion);
glutKeyboardFunc(keys);
glutReshapeFunc(resize);
glutMainLoop();
return EXIT_SUCCESS;
}
Thanks so much.
Stephen