OpenGL problem with .obj files

Discussion in 'Mac Programming' started by Soulstorm, Aug 16, 2008.

  1. Soulstorm macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #1
    I hope I find all the help I need here... This is the problem I have.

    I am trying to create a .obj model loader (from .obj files). Searching the internet, I found the specification, and I started programming based on that. here is what I have done so far:

    ObjLoaders.h
    Code:
    #ifndef OBJLOADERS_H
    #define OBJLOADERS_H
    
    
    #include <iostream>
    #include <vector>
    #include <string>
    #include <OpenGL/OpenGL.h>
    #include <OpenGL/glu.h>
    
    using namespace std;
    
    typedef float vector3f[3];
    
    class OBJFileInfo{
    public:
    	unsigned numVertices;
    	unsigned numTriangles;
    	unsigned numVertexNormals;
    	unsigned numTextureVertex;
    	
    	OBJFileInfo(){
    		numVertices = 0;
    		numTriangles = 0;
    		numVertexNormals = 0;
    		numTextureVertex = 0;
    	}
    };
    
    class Triangle{
    public:
    	unsigned TriangleEdges[3];
    	unsigned TriangleTextureCoords[3];
    	unsigned TriangleNormals[3];
    	
    	Triangle(const Triangle& otherTriangle){
    		for (int i=0; i<3; i++) {
    			TriangleEdges[i] = otherTriangle.TriangleEdges[i];
    			TriangleTextureCoords[i] = otherTriangle.TriangleTextureCoords[i];
    			TriangleNormals[i] = otherTriangle.TriangleNormals[i];
    		}
    	}
    	Triangle(){
    		reset();
    	}
    	void reset(){
    		for (int i=0; i<3; i++) {
    			TriangleEdges[i] = 0;
    			TriangleTextureCoords[i] = 0;
    			TriangleNormals[i] = 0;
    		}
    	}
    };
    
    class OBJModel{
    	OBJFileInfo fileinfo;
    public:
    	
    	vector3f* vertices;
    	vector3f* vertexNormals;
    	vector3f* textureVertices;
    	//Triangle* triangles;
    	vector<Triangle> triangles;
    	OBJModel(OBJFileInfo info){
    		fileinfo = info;
    		vertices	=		new vector3f[info.numVertices];
    		vertexNormals =		new vector3f[info.numVertexNormals];
    		textureVertices =		new vector3f[info.numTextureVertex];
    		//triangles =			new Triangle[info.numTriangles];
    	}
    	
    	void vertexAtIndex(unsigned index, vector3f resultBuffer){
    		resultBuffer[0] = vertices[index][0];
    		resultBuffer[1] = vertices[index][1];
    		resultBuffer[1] = vertices[index][1];
    	}
    	
    	void drawVertexAtIndex(unsigned index){
    		vector3f vertexToDraw;
    		vertexToDraw[0] = vertices[index][0];
    		vertexToDraw[1] = vertices[index][1];
    		vertexToDraw[1] = vertices[index][1];
    		
    		glVertex3f(vertexToDraw[0], vertexToDraw[1], vertexToDraw[2]);
    		
    	}
    	
    	//TO BE COMPLETED!!!
    	void drawFaceAtIndex(unsigned index){
    		unsigned triangleVertexPositions[3];
    		triangleVertexPositions[0] = triangles[index].TriangleEdges[0];
    		triangleVertexPositions[1] = triangles[index].TriangleEdges[1];
    		triangleVertexPositions[2] = triangles[index].TriangleEdges[2];
    		
    		//cout << triangles[index].TriangleEdges[0] << " " << triangles[index].TriangleEdges[0] << " " << triangles[index].TriangleEdges[0] << '\n';
    		
    		vector3f v1, v2, v3;
    		v1[0] = vertices[triangleVertexPositions[0]][0];
    		v1[1] = vertices[triangleVertexPositions[0]][1];
    		v1[2] = vertices[triangleVertexPositions[0]][2];
    		
    		v2[0] = vertices[triangleVertexPositions[1]][0];
    		v2[1] = vertices[triangleVertexPositions[1]][1];
    		v2[2] = vertices[triangleVertexPositions[1]][2];
    		
    		v3[0] = vertices[triangleVertexPositions[2]][0];
    		v3[1] = vertices[triangleVertexPositions[2]][1];
    		v3[2] = vertices[triangleVertexPositions[2]][2];
    		
    		cout << v1[0] << " " << v1[1] << " " << v1[2] << '\n';
    		cout << v2[0] << " " << v2[1] << " " << v2[2] << '\n';
    		cout << v3[0] << " " << v3[1] << " " << v3[2] << '\n';
    		
    		glBegin(GL_TRIANGLES);
    			glVertex3f(v1[0], v1[1], v1[2]);
    			glVertex3f(v2[0], v2[1], v2[2]);
    			glVertex3f(v3[0], v3[1], v3[2]);
    		glEnd();
    	}
    	
    	~OBJModel()
    	{
    		delete [] vertices;
    		delete [] vertexNormals;
    		delete [] textureVertices;
    		//delete [] triangles;
    	}
    	
    	unsigned numVertices(){return fileinfo.numVertices;}
    	unsigned numTriangles(){return fileinfo.numTriangles;}
    	unsigned numVertexNormals(){return fileinfo.numVertexNormals;}
    	unsigned numTextureVertex(){return fileinfo.numTextureVertex;}
    };
    
    
    
    
    void firstPass(char *filename, OBJFileInfo& info);
    void secondPass(char *filename, OBJModel *model);
    
    #endif
    ObjLoaders.cpp
    Code:
    #include "ObjLoaders.h"
    
    
    void firstPass(char *filename, OBJFileInfo& info)
    {
    	char buf[128];
    	FILE *filePointer = fopen(filename, "r");	
    	
    	while ( fscanf(filePointer, "%s", buf) != EOF){
    		switch (buf[0]) {
    			case 'v':
    				switch (buf[1]) {
    					case 't':
    						fgets(buf, sizeof(buf), filePointer);
    						info.numTextureVertex++;
    						break;
    					case '\0':
    						fgets(buf, sizeof(buf), filePointer);
    						info.numVertices++;
    						break;
    					case 'n':
    						fgets(buf, sizeof(buf), filePointer);
    						info.numVertexNormals++;
    						break;
    					default:
    						break;
    				}
    				break;
    			case '#':
    				fgets(buf, sizeof(buf), filePointer);
    				break;
    			case 'f':
    				info.numTriangles++;
    				//NSLog(@"not implemented yet! %i", info.numTriangles);
    				break;
    			default:
    				fgets(buf, sizeof(buf), filePointer);
    				break;
    		}
    	}
    	fclose(filePointer);
    	info.numTriangles--;
    }
    
    void secondPass(char *filename, OBJModel *model){
    	char buf[128];
    	FILE *filePointer = fopen(filename, "r");
    	
    	unsigned currentVertexIndex = 0;
    	unsigned currentvertexNormalIndex = 0;
    	unsigned currentTextureVertexIndex = 0;
    	unsigned currentTriangleIndex = 0;
    	
    	while ( fscanf(filePointer, "%s", buf) != EOF ) {
    		switch (buf[0]) {
    			case 'v':{
    				switch (buf[1]) {
    					case '\0':{
    						float a,b,c;
    						fscanf(filePointer, "%f %f %f", &a, &b, &c);						
    						model->vertices[currentVertexIndex][0] = a;
    						model->vertices[currentVertexIndex][1] = b;
    						model->vertices[currentVertexIndex][2] = c;
    						//cout << a << " " << b << " " << c << '\n';
    						currentVertexIndex++;
    					}
    						break;
    					case 'n':{
    						float a,b,c;
    						fscanf(filePointer, "%f %f %f", &a, &b, &c);
    						//cout << a << ' ' << b << ' ' << c << '\n';
    						model->vertexNormals[currentvertexNormalIndex][0] = a;
    						model->vertexNormals[currentvertexNormalIndex][1] = b;
    						model->vertexNormals[currentvertexNormalIndex][2] = c;
    						currentvertexNormalIndex++;
    					}
    						break;
    					case 't':{
    						float a,b,c;
    						fscanf(filePointer, "%f %f %f", &a, &b, &c);
    						model->textureVertices[currentTextureVertexIndex][0] = a;
    						model->textureVertices[currentTextureVertexIndex][1] = b;
    						model->textureVertices[currentTextureVertexIndex][2] = c;
    						currentTextureVertexIndex++;
    					}
    						break;
    					default:
    						break;
    				}
    			}
    				break;
    			case 'f':{
    				Triangle currentTriangle;
    				int v,t,n = 0;
    				fscanf(filePointer, "%s", buf);
    				if ( strstr(buf, "//") ) {
    					sscanf(buf, "%d//%d", &v, &n);
    					currentTriangle.TriangleEdges[0] = v;
    					currentTriangle.TriangleNormals[0] = n;
    					fscanf(filePointer, "%d//%d", &v, &n);
    					currentTriangle.TriangleEdges[1] = v;
    					currentTriangle.TriangleNormals[1] = n;
    					fscanf(filePointer, "%d//%d", &v, &n);
    					currentTriangle.TriangleEdges[2] = v;
    					currentTriangle.TriangleNormals[2] = n;
    				}
    				else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3){
    					currentTriangle.TriangleEdges[0] = v;
    					currentTriangle.TriangleTextureCoords[0] = t;
    					currentTriangle.TriangleNormals[0] = n;
    					fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleTextureCoords[1]);
    					fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleNormals[1]);
    				}
    				else if (sscanf(buf, "%d/%d", &v, &t) == 2){
    					currentTriangle.TriangleTextureCoords[0] = t;
    					currentTriangle.TriangleEdges[0] = v;
    					fscanf(filePointer, "%d/%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1]);
    					fscanf(filePointer, "%d/%d", &currentTriangle.TriangleEdges[2], &currentTriangle.TriangleTextureCoords[2]);
    				}
    				else {
    					sscanf(buf, "%d", &v);
    					currentTriangle.TriangleEdges[0] = v;
    					fscanf(filePointer, "%d", currentTriangle.TriangleEdges[1]);
    					fscanf(filePointer, "%d", currentTriangle.TriangleEdges[2]);
    				}
    				//cout << currentTriangleIndex << ": ";
    				//cout <<  "f " << currentTriangle.TriangleEdges[0] << "/" << currentTriangle.TriangleTextureCoords[0] << "/" << currentTriangle.TriangleNormals[0];
    				//cout << currentTriangle.TriangleEdges[1] << "/" << currentTriangle.TriangleTextureCoords[1] << "/" << currentTriangle.TriangleNormals[0];
    				//cout << currentTriangle.TriangleEdges[2] << "/" << currentTriangle.TriangleTextureCoords[2] << "/" << currentTriangle.TriangleNormals[2] << '\n';
    				
    				//cout <<  "f " << currentTriangle.TriangleEdges[0] << "//" << currentTriangle.TriangleNormals[0] << " ";
    				//cout << currentTriangle.TriangleEdges[1] << "//" << currentTriangle.TriangleNormals[1] << " ";
    				//cout << currentTriangle.TriangleEdges[2] << "//" << currentTriangle.TriangleNormals[2] << "\n";
    				
    				model->triangles.push_back(currentTriangle);
    				currentTriangleIndex++;
    			}
    				break;	
    			default:
    				fgets(buf, sizeof(buf), filePointer);
    				break;
    		}
    	}
    }
    
    the code to draw:
    Code:
    OBJFileInfo objFile;
    firstPass(FILENAME2, objFile);
    OBJModel model(objFile);
    secondPass(FILENAME2, &model);
    
    model.drawFaceAtIndex(1);
    
    Although something is drawn to the screen, I only see garbage! The file is parsed correctly, and all classes end up in having the correct variables stored in them. So it must be a logic error, and not a programming one (and logic errors are hard to spot!). For simplicity reasons I haven't implemented texture or material support, and I am trying to load a file that only contains vertex and vertex normals data.

    I am attaching the file, in case you want to run the code yourself.
    NOTE: What happened to my identation? Why is the code being displayed so badly? I strongly recommend you paste it into an editor...

    The result should be a lightcycle (remember the movie "Tron"?) or at least something that looks like it. But instead I see garbage... Can anyone help me?
     

    Attached Files:

  2. lazydog macrumors 6502a

    Joined:
    Sep 3, 2005
    Location:
    Cramlington, UK
    #2
    Could it be a big/small endian problem? You're reading things like floats directly from the obj file with the assumption that they were stored using the same architecture as your Mac.

    b e n
     
  3. ncl macrumors member

    Joined:
    Aug 16, 2008
    #3
    In a .obj file, the indices for vertices, normals, etc start at 1, instead of 0 as in C. As a small test, I added a "--v; --n" after the sscanf and ffscanf's in the if(strstr(buf, "//")){...}. The result is in the attached image.

    @lazydog:
    in a .obj file, floats (and actually, everything) are stored as string. As such, you don't have to deal with endianess issues.
     

    Attached Files:

  4. lazydog macrumors 6502a

    Joined:
    Sep 3, 2005
    Location:
    Cramlington, UK
  5. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #5
    Thanks, but...

    Thanks a lot for the tip. I placed the --v and --n where you said, but the result looks nothing like yours... here is what I have changed in the Objloaders.cpp file (I show in bold the added commands, and I only post the specific area of the code. I haven't changed anything else:

    Code:
    case 'f':{
    	Triangle currentTriangle;
    	int v,t,n = 0;
    	fscanf(filePointer, "%s", buf);
    	if ( strstr(buf, "//") ) {
    		sscanf(buf, "%d//%d", &v, &n);
    		[B]--v;--n;[/B]
    		currentTriangle.TriangleEdges[0] = v;
    		currentTriangle.TriangleNormals[0] = n;
    		fscanf(filePointer, "%d//%d", &v, &n);
    		[B]--v;--n;[/B]
    		currentTriangle.TriangleEdges[1] = v;
    		currentTriangle.TriangleNormals[1] = n;
    		fscanf(filePointer, "%d//%d", &v, &n);
    		[B]--v;--n;[/B]
    		currentTriangle.TriangleEdges[2] = v;
    		currentTriangle.TriangleNormals[2] = n;
    	}
    	else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3){
    	currentTriangle.TriangleEdges[0] = v;
    		currentTriangle.TriangleTextureCoords[0] = t;
    		currentTriangle.TriangleNormals[0] = n;
    	fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleTextureCoords[1]);
    		fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleNormals[1]);
    	}
    	else if (sscanf(buf, "%d/%d", &v, &t) == 2){
    		currentTriangle.TriangleTextureCoords[0] = t;
    		currentTriangle.TriangleEdges[0] = v;
    		fscanf(filePointer, "%d/%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1]);
    		fscanf(filePointer, "%d/%d", &currentTriangle.TriangleEdges[2], &currentTriangle.TriangleTextureCoords[2]);
    	}
    	else {
    		sscanf(buf, "%d", &v);
    		currentTriangle.TriangleEdges[0] = v;
    		fscanf(filePointer, "%d", currentTriangle.TriangleEdges[1]);
    		fscanf(filePointer, "%d", currentTriangle.TriangleEdges[2]);
    	}
    	
    	model->triangles.push_back(currentTriangle);
    	currentTriangleIndex++;
    	}
    	break;	
    
    Perhaps you could post the entire code so that I can see exactly how you did it, please?
     
  6. ncl macrumors member

    Joined:
    Aug 16, 2008
    #6
    Here is the code. Basically, I added the "--v; --n;" and also the code to use the normals for lighting. I also moved everything in a header file, but that was only to test it quickly in one of my projects.

    If you still have a problem, a screenshot of the result may help.

    Code:
    #ifndef OBJLOADERS_H
    #define OBJLOADERS_H
    
    #include <iostream>
    #include <vector>
    #include <string>
    #include <OpenGL/OpenGL.h>
    #include <OpenGL/glu.h>
    
    using namespace std;
    
    typedef float vector3f[3];
    
    class OBJFileInfo{
    public:
    	unsigned numVertices;
    	unsigned numTriangles;
    	unsigned numVertexNormals;
    	unsigned numTextureVertex;
    	
    	OBJFileInfo(){
    		numVertices = 0;
    		numTriangles = 0;
    		numVertexNormals = 0;
    		numTextureVertex = 0;
    	}
    };
    
    class Triangle{
    public:
    	unsigned TriangleEdges[3];
    	unsigned TriangleTextureCoords[3];
    	unsigned TriangleNormals[3];
    	
    	Triangle(const Triangle& otherTriangle){
    		for (int i=0; i<3; i++) {
    			TriangleEdges[i] = otherTriangle.TriangleEdges[i];
    			TriangleTextureCoords[i] = otherTriangle.TriangleTextureCoords[i];
    			TriangleNormals[i] = otherTriangle.TriangleNormals[i];
    		}
    	}
    	Triangle(){
    		reset();
    	}
    	void reset(){
    		for (int i=0; i<3; i++) {
    			TriangleEdges[i] = 0;
    			TriangleTextureCoords[i] = 0;
    			TriangleNormals[i] = 0;
    		}
    	}
    };
    
    class OBJModel{
    	OBJFileInfo fileinfo;
    public:
    	
    	vector3f* vertices;
    	vector3f* vertexNormals;
    	vector3f* textureVertices;
    //	Triangle* triangles;
    	vector<Triangle> triangles;
    
    	void setInfo(OBJFileInfo info){
    		fileinfo = info;
    		vertices	=		new vector3f[info.numVertices];
    		vertexNormals =		new vector3f[info.numVertexNormals];
    		textureVertices =		new vector3f[info.numTextureVertex];
    //		triangles =			new Triangle[info.numTriangles];
    	}
    	
    	void vertexAtIndex(unsigned index, vector3f resultBuffer){
    		resultBuffer[0] = vertices[index][0];
    		resultBuffer[1] = vertices[index][1];
    		resultBuffer[1] = vertices[index][1];
    	}
    	
    	void drawVertexAtIndex(unsigned index){
    		vector3f vertexToDraw;
    		vertexToDraw[0] = vertices[index][0];
    		vertexToDraw[1] = vertices[index][1];
    		vertexToDraw[1] = vertices[index][1];
    		
    		glVertex3f(vertexToDraw[0], vertexToDraw[1], vertexToDraw[2]);
    		
    	}
    	
    	//TO BE COMPLETED!!!
    	void drawFaceAtIndex(unsigned index){
    		unsigned triangleVertexPositions[3];
    		triangleVertexPositions[0] = triangles[index].TriangleEdges[0];
    		triangleVertexPositions[1] = triangles[index].TriangleEdges[1];
    		triangleVertexPositions[2] = triangles[index].TriangleEdges[2];
    		
    		unsigned triangleNormalPositions[3];
    		triangleNormalPositions[0] = triangles[index].TriangleNormals[0];
    		triangleNormalPositions[1] = triangles[index].TriangleNormals[1];
    		triangleNormalPositions[2] = triangles[index].TriangleNormals[2];
    		
    		//cout << triangles[index].TriangleEdges[0] << " " << triangles[index].TriangleEdges[0] << " " << triangles[index].TriangleEdges[0] << '\n';
    		
    		vector3f v1, v2, v3;
    		vector3f n1, n2, n3;
    		for(unsigned i=0; i<3; ++i){
    			v1[i] = vertices[triangleVertexPositions[0]][i];
    			v2[i] = vertices[triangleVertexPositions[1]][i];
    			v3[i] = vertices[triangleVertexPositions[2]][i];
    			n1[i] = vertexNormals[triangleNormalPositions[0]][i];
    			n2[i] = vertexNormals[triangleNormalPositions[1]][i];
    			n3[i] = vertexNormals[triangleNormalPositions[2]][i];
    		}		
    
    #define normalize(n) {float mag = sqrtf(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]); for(unsigned i=0; i<3;++i)n[i]/=mag;}
    		normalize(n1);
    		normalize(n2);
    		normalize(n3);
    
    		glBegin(GL_TRIANGLES);
    		glNormal3fv(n1);
    		glVertex3fv(v1);
    		glNormal3fv(n2);
    		glVertex3fv(v2);
    		glNormal3fv(n3);
    		glVertex3fv(v3);
    		glEnd();
    	}
    	
    	~OBJModel()
    	{
    		delete [] vertices;
    		delete [] vertexNormals;
    		delete [] textureVertices;
    //		delete [] triangles;
    	}
    	
    	unsigned numVertices(){return fileinfo.numVertices;}
    	unsigned numTriangles(){return fileinfo.numTriangles;}
    	unsigned numVertexNormals(){return fileinfo.numVertexNormals;}
    	unsigned numTextureVertex(){return fileinfo.numTextureVertex;}
    };
    
    
    
    
    //void firstPass(char *filename, OBJFileInfo& info);
    //void secondPass(char *filename, OBJModel *model);
    
    
    static inline void firstPass(char *filename, OBJFileInfo& info)
    {
    	char buf[128];
    	FILE *filePointer = fopen(filename, "r");	
    	
    	while ( fscanf(filePointer, "%s", buf) != EOF){
    		switch (buf[0]) {
    			case 'v':
    				switch (buf[1]) {
    					case 't':
    						fgets(buf, sizeof(buf), filePointer);
    						info.numTextureVertex++;
    						break;
    					case '\0':
    						fgets(buf, sizeof(buf), filePointer);
    						info.numVertices++;
    						break;
    					case 'n':
    						fgets(buf, sizeof(buf), filePointer);
    						info.numVertexNormals++;
    						break;
    					default:
    						break;
    				}
    				break;
    			case '#':
    				fgets(buf, sizeof(buf), filePointer);
    				break;
    			case 'f':
    				info.numTriangles++;
    				//NSLog(@"not implemented yet! %i", info.numTriangles);
    				break;
    			default:
    				fgets(buf, sizeof(buf), filePointer);
    				break;
    		}
    	}
    	fclose(filePointer);
    	info.numTriangles;//--;
    }
    
    static inline void secondPass(char *filename, OBJModel *model){
    	char buf[128];
    	FILE *filePointer = fopen(filename, "r");
    	
    	unsigned currentVertexIndex = 0;
    	unsigned currentvertexNormalIndex = 0;
    	unsigned currentTextureVertexIndex = 0;
    	unsigned currentTriangleIndex = 0;
    	
    	while ( fscanf(filePointer, "%s", buf) != EOF ) {
    		switch (buf[0]) {
    			case 'v':{
    				switch (buf[1]) {
    					case '\0':{
    						float a,b,c;
    						fscanf(filePointer, "%f %f %f", &a, &b, &c);						
    						model->vertices[currentVertexIndex][0] = a;
    						model->vertices[currentVertexIndex][1] = b;
    						model->vertices[currentVertexIndex][2] = c;
    						//cout << a << " " << b << " " << c << '\n';
    						currentVertexIndex++;
    					}
    						break;
    					case 'n':{
    						float a,b,c;
    						fscanf(filePointer, "%f %f %f", &a, &b, &c);
    						cout << a << ' ' << b << ' ' << c << '\n';
    						model->vertexNormals[currentvertexNormalIndex][0] = a;
    						model->vertexNormals[currentvertexNormalIndex][1] = b;
    						model->vertexNormals[currentvertexNormalIndex][2] = c;
    						currentvertexNormalIndex++;
    					}
    						break;
    					case 't':{
    						float a,b,c;
    						fscanf(filePointer, "%f %f %f", &a, &b, &c);
    						model->textureVertices[currentTextureVertexIndex][0] = a;
    						model->textureVertices[currentTextureVertexIndex][1] = b;
    						model->textureVertices[currentTextureVertexIndex][2] = c;
    						currentTextureVertexIndex++;
    					}
    						break;
    					default:
    						break;
    				}
    			}
    				break;
    			case 'f':{
    				Triangle currentTriangle;
    				int v,t,n = 0;
    				fscanf(filePointer, "%s", buf);
    				if ( strstr(buf, "//") ) {
    					sscanf(buf, "%d//%d", &v, &n);
    					printf("%d %d\n", v, n);
    					--v; --n;
    					currentTriangle.TriangleEdges[0] = v;
    					currentTriangle.TriangleNormals[0] = n;
    					fscanf(filePointer, "%d//%d", &v, &n);
    					printf("%d %d\n", v, n);
    					--v; --n;
    					currentTriangle.TriangleEdges[1] = v;
    					currentTriangle.TriangleNormals[1] = n;
    					fscanf(filePointer, "%d//%d", &v, &n);
    					printf("%d %d\n", v, n);
    					--v; --n;
    					currentTriangle.TriangleEdges[2] = v;
    					currentTriangle.TriangleNormals[2] = n;
    				}
    				else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3){
    					currentTriangle.TriangleEdges[0] = v;
    					currentTriangle.TriangleTextureCoords[0] = t;
    					currentTriangle.TriangleNormals[0] = n;
    					fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleTextureCoords[1]);
    					fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleNormals[1]);
    				}
    				else if (sscanf(buf, "%d/%d", &v, &t) == 2){
    					currentTriangle.TriangleTextureCoords[0] = t;
    					currentTriangle.TriangleEdges[0] = v;
    					fscanf(filePointer, "%d/%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1]);
    					fscanf(filePointer, "%d/%d", &currentTriangle.TriangleEdges[2], &currentTriangle.TriangleTextureCoords[2]);
    				}
    				else {
    					sscanf(buf, "%d", &v);
    					currentTriangle.TriangleEdges[0] = v;
    					fscanf(filePointer, "%d", currentTriangle.TriangleEdges[1]);
    					fscanf(filePointer, "%d", currentTriangle.TriangleEdges[2]);
    				}
    				//cout << currentTriangleIndex << ": ";
    				//cout <<  "f " << currentTriangle.TriangleEdges[0] << "/" << currentTriangle.TriangleTextureCoords[0] << "/" << currentTriangle.TriangleNormals[0];
    				//cout << currentTriangle.TriangleEdges[1] << "/" << currentTriangle.TriangleTextureCoords[1] << "/" << currentTriangle.TriangleNormals[0];
    				//cout << currentTriangle.TriangleEdges[2] << "/" << currentTriangle.TriangleTextureCoords[2] << "/" << currentTriangle.TriangleNormals[2] << '\n';
    				
    				//cout <<  "f " << currentTriangle.TriangleEdges[0] << "//" << currentTriangle.TriangleNormals[0] << " ";
    				//cout << currentTriangle.TriangleEdges[1] << "//" << currentTriangle.TriangleNormals[1] << " ";
    				//cout << currentTriangle.TriangleEdges[2] << "//" << currentTriangle.TriangleNormals[2] << "\n";
    //				model->triangles[currentTriangleIndex++] = currentTriangle;
    				model->triangles.push_back(currentTriangle);
    				currentTriangleIndex++;
    			}
    				break;	
    			default:
    				fgets(buf, sizeof(buf), filePointer);
    				break;
    		}
    	}
    }
    
    #endif
    
    
     
  7. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #7
    Nope. Still doesn't work.

    I am attaching a screenshot of the result, and my project file (XCode 3.1). To your help, I am attaching another file, one of a low-resolution cone, that is also being displayed wrong.... But this model is only shown misplaced on one side only. NOTE: I haven't used lighting.

    I hope you can figure out what is going wrong...

    EDIT:If it isn't much trouble, could you post your entire project file? That way I will be able to compare everything and see what I have done wrong.
     

    Attached Files:

  8. ncl macrumors member

    Joined:
    Aug 16, 2008
    #8
    Well, the project I used to test your code really is a giant mess :eek: So it's probably better if I don't post it.

    In your project, I changed 2 methods in MyOpenGLView.mm.

    First, in reshape, I used a perspective projection. It's usually better when displaying 3D objects than an orthographic projection. Moreover, your orthographic projection didn't respect the view's aspect ratio, so the model was compressed on the Y axis.

    Code:
    - (void)reshape
    {
    	[[self openGLContext]makeCurrentContext];
    	NSRect bounds = [self bounds];
    	//NSLog(@"reshaping: %f, %f", NSWidth(bounds), NSHeight(bounds));
    	glViewport(0, 0, NSWidth(bounds), NSHeight(bounds));
    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    	gluPerspective(45.0f, NSWidth(bounds)/NSHeight(bounds), 0.1f, 100.0f);
    //	glOrtho(-20, 20, -20, 20, -100, 100);
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    	[NSOpenGLContext clearCurrentContext];
    }
    
    Next, I added lighting (prettier), and translated the model away from the camera. Also, in the rendering loop, you used model.numVertices() as upper limit. I changed it to model.numTriangles():
    Code:
    - (void)drawRect:(NSRect)rect {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	
    	glLoadIdentity();
    	
    	GLfloat lightpos[4] = {0.0f, 0.2f, -1.0, 1.0f};
    	glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
    	glEnable(GL_LIGHTING);
    	glEnable(GL_LIGHT0);
    	
    	glTranslatef(0.0f, 0.0f, -20.0f);
    	glRotatef(xRot, 1.0f, 0.0f, 0.0f);
    	glRotatef(yRot, 0.0f, 1.0f, 0.0f);
    	
    	OBJFileInfo objFile;
    	firstPass(FILENAME, objFile);
    	OBJModel model(objFile);
    	secondPass(FILENAME, &model);
    	
    	for (int i=0; i<model.numTriangles(); i++) {
    		model.drawFaceAtIndex(i);
    	}
    	
    	[[self openGLContext]flushBuffer];
    }
    
    If this doesn't solve the problem, then I'll create a small project. However, my cocoa is quite old: I now use SDL+OpenGL all the time. So, It may take some time.

    Also:
    if you use the cone instead of the lightcycle with the code above, nothing will be displayed because the cone is too big. You'll have to change the distance from the camera (for example, -20.0f -> -90.0f). You may want to write a function to scale the models down to a known range to avoid that problem.
     
  9. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #9
    That solved my problem. I can't tell you how grateful I am. Thanks a lot.
     

    Attached Files:

  10. Cromulent macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #10
    Sorry to bump an old thread but did you incorporate a material file reader as well (Material File Format)? Been working on something like this myself and just wondered if you decided to do everything yourself or did you end up using a third party lib for that side of things?
     
  11. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #11
    I implemented a material file reader myself. The material reader is complete, but it has some minor problems I expect to solve in the next few days (damn semester exams!).
     
  12. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #12
    Another Problem Arises!

    I successfully implemented material support to my model.

    One last task remains, and that is the implementation of textures. In order to produce a simple object with textures, I opened Vue 6, and I made a cone. I made 2 exports: The first one is with a texture, the other one isn't.

    Problem 1: The output is completely different although I haven't changed anything else apart from the texture!
    Problem 2: I haven't implemented texture support in my code, yet. However, when I display the textured cone, I get garbage! The code works well with the untextured one. How can this be?

    I am attaching the cone object with and without textures. this is the actual code that loads the model and draws it:

    ObjLoaders.h
    Code:
    #ifndef OBJLOADERS_H
    #define OBJLOADERS_H
    
    #include <iostream>
    #include <vector>
    #include <string>
    #include <OpenGL/OpenGL.h>
    #include <OpenGL/glu.h>
    #include "MTLLoaders.h"
    #include "Routines.hh"
    #include <SFCocoa/SFCocoa.h>
    
    using namespace std;
    
    //class MTLMaterial;
    //typedef float vector3f[3];
    
    class vector3f{
    	float zeroFloat;
    public:
    	float x,y,z;
    	vector3f();
    	vector3f(float a, float b, float c);
    	
    	float& operator[](short index);
    	
    	float magnitude(){return sqrt(x*x + y*y + z*z);}
    	void normalize(){
    		float magSq = x*x + y*y + z*z;
    		if (magSq > 0.0f) {
    			float oneOverMag = 1.0f/ sqrt(magSq);
    			x *= oneOverMag;
    			y *= oneOverMag;
    			z *= oneOverMag;
    		}
    	}
    };
    
    class OBJFileInfo{
    public:
    	unsigned numVertices;
    	unsigned numTriangles;
    	unsigned numVertexNormals;
    	unsigned numTextureVertex;
    	unsigned numGroups;
    	OBJFileInfo();
    };
    
    
    class Triangle{
    public:
    	unsigned TriangleEdges[3];
    	unsigned TriangleTextureCoords[3];
    	unsigned TriangleNormals[3];
    	
    	MTLMaterial triangleMaterial;
    	
    	//unsigned int triangleMaterial; // triangle material index
    	
    	void showEdgesIndices(){
    		cout << TriangleEdges[0] << " " << TriangleEdges[1] << " " << TriangleEdges[2] << '\n';
    	}
    	Triangle(const Triangle& otherTriangle);
    	Triangle();
    	void reset();
    };
    
    class OBJGroup{
    public:
    	string name;
    	unsigned numTriangles;
    	vector<unsigned> triangleIndicesIndexes;
    	GLuint material; //index to material for group;
    	
    	void showGroupTriangleIndices(){
    		for (int i=0; i<triangleIndicesIndexes.size(); i++) {
    			cout << triangleIndicesIndexes[i] << '\n';
    		}
    	}
    	
    };
    
    class OBJModel{
    	bool hasTextures;
    	bool hasNormals;
    public:
    	float xMaxSize;
    	float xNegativeMaxSize;
    	float yMaxSize;
    	float yNegativeMaxSize;
    	float zMaxSize;
    	float zNegativeMaxSize;
    	
    	vector<vector3f> vertices;
    	vector<vector3f> vertexNormals;
    	vector<vector3f> textureVertices;
    	vector<Triangle> triangles;
    	vector<MTLMaterial> materials;
    	vector<TGALoaderCPP> textures;
    	vector<string> mtlFileLocations;
    	
    	//experimental
    	vector<OBJGroup> groups;
    	//unsigned currentGroupPosition;
    
    	OBJModel(){};
    	~OBJModel();
    	void allocUsingInfo(OBJFileInfo &info);
    	void vertexAtIndex(unsigned index, vector3f resultBuffer);
    	void drawVertexAtIndex(unsigned index);
    	void reset();
    	vector<Triangle> trianglesForGroupAtIndex(unsigned index);
    	
    	//TO BE COMPLETED!!!
    	void drawFaceAtIndex(unsigned index);
    	void showVertices();
    	void showNormals();
    	unsigned numVertices();
    	unsigned numTriangles();
    	unsigned numVertexNormals();
    	unsigned numTextureVertex();
    	
    	void loadMtlFromFile(const char *filename);
    	void loadOBJFile(const char *filename);
    	
    };
    
    #pragma mark -
    void firstPass(const char *filename, OBJFileInfo& info);
    
    #endif
    ObjLoaders.cpp
    Code:
    #include "ObjLoaders.h"
    
    #pragma mark -
    #pragma mark vector3f
    vector3f::vector3f(){x = 0; y = 0; z = 0; zeroFloat = 0.0f;}
    vector3f::vector3f(float a, float b, float c){x = a; y = b; z = c; zeroFloat = 0.0f;}
    
    float& vector3f::operator[](short index){
    	//if(index > 2 || index < 0)
    	//	return 0.0f;
    	switch (index) {
    		case 0:
    			return x;
    			break;
    		case 1:
    			return y;
    			break;
    		case 2:
    			return z;
    			break;
    		default:
    			break;
    	}
    	return zeroFloat;
    }
    
    #pragma mark -
    #pragma mark ObjFileInfo
    
    OBJFileInfo::OBJFileInfo(){
    	numVertices = 0;
    	numTriangles = 0;
    	numVertexNormals = 0;
    	numTextureVertex = 0;
    	numGroups = 0;
    }
    
    
    #pragma mark -
    #pragma mark Triangle
    
    Triangle::Triangle(const Triangle& otherTriangle){
    	for (int i=0; i<3; i++) {
    		TriangleEdges[i] = otherTriangle.TriangleEdges[i];
    		TriangleTextureCoords[i] = otherTriangle.TriangleTextureCoords[i];
    		TriangleNormals[i] = otherTriangle.TriangleNormals[i];
    	}
    	triangleMaterial = otherTriangle.triangleMaterial;
    }
    Triangle::Triangle(){
    	reset();
    }
    void Triangle::reset(){
    	for (int i=0; i<3; i++) {
    		TriangleEdges[i] = 0;
    		TriangleTextureCoords[i] = 0;
    		TriangleNormals[i] = 0;
    	}
    }
    
    
    #pragma mark -
    #pragma mark OBJModel
    void OBJModel::vertexAtIndex(unsigned index, vector3f resultBuffer){
    	resultBuffer[0] = vertices[index][0];
    	resultBuffer[1] = vertices[index][1];
    	resultBuffer[2] = vertices[index][2];
    }
    
    void OBJModel::drawVertexAtIndex(unsigned index){
    	vector3f vertexToDraw;
    	vertexToDraw[0] = vertices[index][0];
    	vertexToDraw[1] = vertices[index][1];
    	vertexToDraw[2] = vertices[index][2];
    	
    	glVertex3f(vertexToDraw[0], vertexToDraw[1], vertexToDraw[2]);
    	
    }
    
    vector<Triangle> OBJModel::trianglesForGroupAtIndex(unsigned index){
    	vector<Triangle> result;
    	for (int i=0; i<groups[index].triangleIndicesIndexes.size(); i++)
    		result.push_back(triangles[groups[index].triangleIndicesIndexes[i]]);
    	return result;
    }
    
    
    //TO BE COMPLETED!!!
    void OBJModel::drawFaceAtIndex(unsigned index){
    	unsigned triangleVertexPositions[3];
    	triangleVertexPositions[0] = triangles[index].TriangleEdges[0];
    	triangleVertexPositions[1] = triangles[index].TriangleEdges[1];
    	triangleVertexPositions[2] = triangles[index].TriangleEdges[2];
    	
    	unsigned triangleNormalPositions[3];
    	triangleNormalPositions[0] = triangles[index].TriangleNormals[0];
    	triangleNormalPositions[1] = triangles[index].TriangleNormals[1];
    	triangleNormalPositions[2] = triangles[index].TriangleNormals[2];
    	
    	unsigned vertexTexturePositions[3];
    	vertexTexturePositions[0] = triangles[index].TriangleTextureCoords[0];
    	vertexTexturePositions[1] = triangles[index].TriangleTextureCoords[1];
    	vertexTexturePositions[2] = triangles[index].TriangleTextureCoords[2];
    	
    	//cout << triangles[index].TriangleEdges[0] << " " << triangles[index].TriangleEdges[0] << " " << triangles[index].TriangleEdges[0] << '\n';
    	
    	vector3f v1, v2, v3;
    	vector3f n1, n2, n3;
    	vector3f t1, t2, t3;
    	
    	for(unsigned i=0; i<3; ++i){
    		v1[i] = vertices[triangleVertexPositions[0]][i];
    		v2[i] = vertices[triangleVertexPositions[1]][i];
    		v3[i] = vertices[triangleVertexPositions[2]][i];
    		n1[i] = vertexNormals[triangleNormalPositions[0]][i];
    		n2[i] = vertexNormals[triangleNormalPositions[1]][i];
    		n3[i] = vertexNormals[triangleNormalPositions[2]][i];
    		t1[i] = textureVertices[vertexTexturePositions[0]][i];
    		t2[i] = textureVertices[vertexTexturePositions[1]][i];
    		t3[i] = textureVertices[vertexTexturePositions[2]][i];
    	}		
    	
    	n1.normalize();
    	n2.normalize();
    	n3.normalize();
    	
    	MTLMaterial currentMaterial;
    	currentMaterial = triangles[index].triangleMaterial;
    	
    	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, currentMaterial.ambient);
    	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, currentMaterial.diffuse);
    	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, currentMaterial.specular);
    	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, currentMaterial.shininess);
    	
    	glBegin(GL_TRIANGLES);
    	
    	glNormal3f(n1[0],n1[1],n1[2]);
    	glTexCoord2f(<#GLfloat s#>, <#GLfloat t#>)
    	glVertex3f(v1[0],v1[1],v1[2]);
    	
    	glNormal3f(n2[0],n2[1],n2[2]);
    	glVertex3f(v2[0],v2[1],v2[2]);
    	
    	glNormal3f(n3[0],n3[1],n3[2]);
    	glVertex3f(v3[0],v3[1],v3[2]);
    	
    	glEnd();
    }
    
    void OBJModel::reset(){
    	this->vertices.clear();
    	this->vertexNormals.clear();
    	this->textureVertices.clear();
    	this->triangles.clear();
    	this->mtlFileLocations.clear();
    	this->materials.clear();
    	this->groups.clear();
    	this->textures.clear();
    	
    	hasTextures = false;
    	hasNormals = false;
    	
    	xMaxSize = 0.0f;
    	xNegativeMaxSize = 0.0f;
    	yMaxSize = 0.0f;
    	yNegativeMaxSize = 0.0f;
    	zMaxSize = 0.0f;
    	zNegativeMaxSize = 0.0f;
    }
    
    OBJModel::~OBJModel(){}
    
    void OBJModel::showVertices()
    {
    	for (int i=0; i<vertices.size(); i++) 
    		cout << vertices[i][0] << " " << vertices[i][1] << " " << vertices[i][2] << '\n';
    	cout << '\n';
    }
    
    void OBJModel::showNormals()
    {
    	for (int i=0; i<vertexNormals.size(); i++)
    		cout << vertexNormals[i][0] << " " << vertexNormals[i][1] << " " << vertexNormals[i][2] << '\n';
    	cout << '\n';
    }
    
    unsigned OBJModel::numVertices(){return vertices.size();}
    unsigned OBJModel::numTriangles(){return triangles.size();}
    unsigned OBJModel::numVertexNormals(){return vertexNormals.size();}
    unsigned OBJModel::numTextureVertex(){return textureVertices.size();}
    
    void OBJModel::loadOBJFile(const char *filename){
    	this->reset();
    	
    	OBJFileInfo currentFileInfo;
    	firstPass(filename, currentFileInfo);
    	groups.assign(currentFileInfo.numGroups, OBJGroup());
    	
    	MTLMaterial currentMaterial; //set the current material
    	
    	char buf[128];
    	FILE *filePointer = fopen(filename, "r");
    	
    	if (!filePointer) {
    		cout << "Specified File does not exist!\n";
    		return;
    	}
    	
    	unsigned currentVertexIndex = 0;
    	unsigned currentvertexNormalIndex = 0;
    	unsigned currentTextureVertexIndex = 0;
    	unsigned currentTriangleIndex = 0;
    	unsigned currentObjGroupIndex = 0;
    	bool shouldCurrentObjGroupIndexIncrease = false;
    	while ( fscanf(filePointer, "%s", buf) != EOF ) {
    		
    		switch (buf[0]) {
    			case 'm':{
    				cout << "mtl lib found:";
    				fgets(buf, sizeof(buf), filePointer);	//get the name of the mtl library to load
                    sscanf(buf, "%s %s", buf, buf);			//and fix the string
    				string newMTLFileLocation =  buf;
    				this->mtlFileLocations.push_back(newMTLFileLocation);
    				cout << newMTLFileLocation << '\n';
    				this->loadMtlFromFile(newMTLFileLocation.c_str()); // load the materials
    			}
    				break;
    			case 'v':{
    				vector3f vectorToAdd;
    				switch (buf[1]) {
    					case '\0':{
    						float a,b,c; //(x,y,z)
    						
    						//try to set the model's boundaries
    						if(a > this->xMaxSize) this->xMaxSize = a;
    						if(b > this->yMaxSize) this->yMaxSize = b;
    						if(c > this->zMaxSize) this->zMaxSize = c;
    						if(a < this->xNegativeMaxSize) this->xNegativeMaxSize = a;
    						if(b < this->yNegativeMaxSize) this->yNegativeMaxSize = b;
    						if(c < this->zNegativeMaxSize) this->zNegativeMaxSize = c;
    						
    						//add the vertex into the vector created just for vertices
    						fscanf(filePointer, "%f %f %f", &a, &b, &c);						
    						vectorToAdd[0] = a;
    						vectorToAdd[1] = b;
    						vectorToAdd[2] = c;
    						currentVertexIndex++;
    						this->vertices.push_back(vectorToAdd);
    					}
    						break;
    					case 'n':{
    						if (this->hasNormals != true) this->hasNormals = true;
    						float a,b,c;
    						fscanf(filePointer, "%f %f %f", &a, &b, &c);
    						vectorToAdd[0] = a;
    						vectorToAdd[1] = b;
    						vectorToAdd[2] = c;
    						this->vertexNormals.push_back(vectorToAdd);
    						currentvertexNormalIndex++;
    					}
    						break;
    					case 't':{
    						if (this->hasTextures != true) this->hasTextures = true;
    						float a,b;	//There is no C. The last element of the vector actually stays '0'
    						//It will be handled appropriately when the time for displaying comes...
    						fscanf(filePointer, "%f %f", &a, &b);
    						vectorToAdd[0] = a;
    						vectorToAdd[1] = b;
    						this->textureVertices.push_back(vectorToAdd);
    						currentTextureVertexIndex++;
    					}
    						break;
    					default:
    						break;
    				}
    			}
    				break;
    			case 'f':{ // make a triangle
    				Triangle currentTriangle;
    				int v,t,n = 0;
    				fscanf(filePointer, "%s", buf);
    				if ( strstr(buf, "//") ) {
    					sscanf(buf, "%d//%d", &v, &n);
    					//printf("%d %d\n", v, n);
    					--v; --n;
    					currentTriangle.TriangleEdges[0] = v;
    					currentTriangle.TriangleNormals[0] = n;
    					fscanf(filePointer, "%d//%d", &v, &n);
    					//printf("%d %d\n", v, n);
    					--v; --n;
    					currentTriangle.TriangleEdges[1] = v;
    					currentTriangle.TriangleNormals[1] = n;
    					fscanf(filePointer, "%d//%d", &v, &n);
    					//printf("%d %d\n", v, n);
    					--v; --n;
    					currentTriangle.TriangleEdges[2] = v;
    					currentTriangle.TriangleNormals[2] = n;
    				}
    				else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3){
    					currentTriangle.TriangleEdges[0] = v;
    					currentTriangle.TriangleTextureCoords[0] = t;
    					currentTriangle.TriangleNormals[0] = n;
    					fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleTextureCoords[1]);
    					fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleNormals[1]);
    				}
    				else if (sscanf(buf, "%d/%d", &v, &t) == 2){
    					currentTriangle.TriangleTextureCoords[0] = t;
    					currentTriangle.TriangleEdges[0] = v;
    					fscanf(filePointer, "%d/%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1]);
    					fscanf(filePointer, "%d/%d", &currentTriangle.TriangleEdges[2], &currentTriangle.TriangleTextureCoords[2]);
    				}
    				else {
    					sscanf(buf, "%d", &v);
    					currentTriangle.TriangleEdges[0] = v;
    					fscanf(filePointer, "%d", &currentTriangle.TriangleEdges[1]);
    					fscanf(filePointer, "%d", &currentTriangle.TriangleEdges[2]);
    				}
    				
    				groups[currentObjGroupIndex].triangleIndicesIndexes.push_back(currentTriangleIndex);
    				
    				currentTriangle.triangleMaterial = currentMaterial;
    				this->triangles.push_back(currentTriangle);
    				currentTriangleIndex++;
    				
    				//cout << currentTriangleIndex << ": ";
    				//cout <<  "f " << currentTriangle.TriangleEdges[0] << "/" << currentTriangle.TriangleTextureCoords[0] << "/" << currentTriangle.TriangleNormals[0];
    				//cout << currentTriangle.TriangleEdges[1] << "/" << currentTriangle.TriangleTextureCoords[1] << "/" << currentTriangle.TriangleNormals[0];
    				//cout << currentTriangle.TriangleEdges[2] << "/" << currentTriangle.TriangleTextureCoords[2] << "/" << currentTriangle.TriangleNormals[2] << '\n';
    				
    				//cout <<  "f " << currentTriangle.TriangleEdges[0] << "//" << currentTriangle.TriangleNormals[0] << " ";
    				//cout << currentTriangle.TriangleEdges[1] << "//" << currentTriangle.TriangleNormals[1] << " ";
    				//cout << currentTriangle.TriangleEdges[2] << "//" << currentTriangle.TriangleNormals[2] << " using material: " << currentTriangle.triangleMaterial.name << "\n";
    				//				model->triangles[currentTriangleIndex++] = currentTriangle;
    				
    			}
    				break;	
    				//TO BE FIXED!
    			case 'g':{
    				if ( (currentObjGroupIndex == 0) && (shouldCurrentObjGroupIndexIncrease == false) ) {
    					fgets(buf, sizeof(buf), filePointer);
    					groups[currentObjGroupIndex].name = buf;
    					shouldCurrentObjGroupIndexIncrease = true;
    				}
    				else{
    					fgets(buf, sizeof(buf), filePointer);
    					currentObjGroupIndex++;
    					groups[currentObjGroupIndex].name = buf;
    				}
    			}
    				break;
    			case 'u':{    //use material
    				fgets(buf, sizeof(buf), filePointer);
    				sscanf(buf, "%s", buf);
    				string nameOfMaterial = buf;
    				//cout << "now using material: " << nameOfMaterial << '\n';
    				for (int i=0; i < materials.size(); i++) {
    					if(materials[i].name == nameOfMaterial){
    						//cout << "this exists in library of materials\n";
    						currentMaterial = materials[i];
    					}
    				}
    			}
    				break;
    			default:
    				fgets(buf, sizeof(buf), filePointer);
    				break;
    		}
    	}
    	fclose(filePointer);
    }
    
    
    void OBJModel::loadMtlFromFile(const char *filename){
    	cout << "Attempting to load MTL file named: " << filename << '\n';
    	
    	bool shouldIncreaseMaterials = false;
    	unsigned currentMTLMaterialIndex = 0;
    	
    	FILE* filepointer = fopen(filename, "r");
    	
    	if (!filepointer) {
    		cout << "could not find the MTL file requested. returning function...\n";
    		return;
    	}
    	
    	MTLMaterial currentMaterial;
    	materials.push_back(currentMaterial);
    	
    	char buf[128];
    	while(fscanf(filepointer, "%s", buf) != EOF){
    		
    		switch (buf[0]) {
    			case '#':
    				fgets(buf, sizeof(buf), filepointer);
    				break;
    			case 'n':{
    				fgets(buf, sizeof(buf), filepointer);
    				sscanf(buf, "%s", buf);
    				if (shouldIncreaseMaterials == false) {
    					//currentMaterialRef.name = buf;
    					materials[currentMTLMaterialIndex].name = buf;
    					shouldIncreaseMaterials = true;
    				}
    				else{
    					MTLMaterial newMaterial;
    					newMaterial.name = buf;
    					
    					materials.push_back(newMaterial);
    					currentMTLMaterialIndex++;
    					//currentMaterialRef = materials[currentMTLMaterialIndex];
    					materials[currentMTLMaterialIndex].name = buf;
    				}
    				break;
    			case 'N':{
    				if (buf[1]!='s') break;
    				float newShininess = 0.0f;
    				fscanf(filepointer, "%f", &newShininess);
    				newShininess /= 1000.0f;
    				newShininess *= 128.0f;
    				materials[currentMTLMaterialIndex].shininess = newShininess;
    			}
    				break;
    			case 'K':
    				switch (buf[1]) {
    					case 'd':
    						fscanf(filepointer, "%f %f %f", &materials[currentMTLMaterialIndex].diffuse[0], &materials[currentMTLMaterialIndex].diffuse[1], &materials[currentMTLMaterialIndex].diffuse[2]);
    						break;
    					case 's':
    						fscanf(filepointer, "%f %f %f", &materials[currentMTLMaterialIndex].specular[0], &materials[currentMTLMaterialIndex].specular[1], &materials[currentMTLMaterialIndex].specular[2]);
    						break;
    					case 'a':
    						fscanf(filepointer, "%f %f %f", &materials[currentMTLMaterialIndex].ambient[0], &materials[currentMTLMaterialIndex].ambient[1], &materials[currentMTLMaterialIndex].ambient[2]);
    						break;
    					default:
    						break;
    				}
    				break;
    			}
    				break;
    			case 'm':{
    				char *textureFileName = (char *)malloc(FILENAME_MAX);
    				fgets(textureFileName, FILENAME_MAX, filepointer);
    				sscanf(textureFileName, "%s", textureFileName, textureFileName);
    				//string textureFileNameStringed = textureFileName;
    				//free(textureFileName);
    				if(strncmp(buf, "map_Kd", 6) == 0) 
    				{
    					cout << "texture found: " << textureFileName << '\n';
    					TGALoaderCPP alpha;
    					alpha.LoadTGA(textureFileName);
    					textures.push_back(alpha);
    				}
    				free(textureFileName);
    			}
    				break;
    			default:
    				fgets(buf, sizeof(buf), filepointer);
    				break;
    		}
    	}
    	fclose(filepointer);
    	
    }
    
    
    #pragma mark -
    #pragma mark Solo Functions
    
    void firstPass(const char *filename, OBJFileInfo& info)
    {
    	char buf[128];
    	FILE *filePointer = fopen(filename, "r");	
    	
    	while ( fscanf(filePointer, "%s", buf) != EOF){
    		switch (buf[0]) {
    			case 'v':
    				switch (buf[1]) {
    					case 't':
    						fgets(buf, sizeof(buf), filePointer);
    						info.numTextureVertex++;
    						break;
    					case '\0':
    						fgets(buf, sizeof(buf), filePointer);
    						info.numVertices++;
    						break;
    					case 'n':
    						fgets(buf, sizeof(buf), filePointer);
    						info.numVertexNormals++;
    						break;
    					default:
    						break;
    				}
    				break;
    			case '#':
    				fgets(buf, sizeof(buf), filePointer);
    				break;
    			case 'f':
    				info.numTriangles++;
    				//NSLog(@"not implemented yet! %i", info.numTriangles);
    				break;
    			case 'g':
    				info.numGroups++;
    				break;
    			default:
    				fgets(buf, sizeof(buf), filePointer);
    				break;
    		}
    	}
    	
    	if(info.numGroups == 0)		//if the file does not issue the group command, let's at least
    		info.numGroups++;		//explicitly define the default group
    	
    	fclose(filePointer);
    	info.numTriangles;//--;
    }
    
    
    << code continues in next post...>>
     

    Attached Files:

  13. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #13
    MTLLoaders.h
    Code:
    /*
     *  MTLLoaders.h
     *  OBJ_MODEL_LOADER_TEST
     *
     *  Created by Christos Sotiriou on 8/19/08.
     *  Copyright 2008 Tei of Pireus. All rights reserved.
     *
     */
    #ifndef MTLLOADERS_H
    #define MTLLOADERS_H
    
    #include <OpenGL/OpenGL.h>
    #include <OpenGL/glu.h>
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    
    class MTLMaterial{
    public:
    	string name;                   /* name of material */
    	float diffuse[4];           /* diffuse component */
    	float ambient[4];           /* ambient component */
    	float specular[4];          /* specular component */
    	float emmissive[4];         /* emmissive component */
    	float shininess;            /* specular exponent */
    	//this is for textures
    	GLuint IDTextura;		// ID-ul texturii difuze
    	
    	MTLMaterial();
    	MTLMaterial (const MTLMaterial &other);
    	
    	MTLMaterial& operator=(const MTLMaterial &other);
    	
    	void revertToDefault();
    	
    void showInfo();
    };
    #endif
    MTLLoaders.cpp
    Code:
    /*
     *  MTLLoaders.cpp
     *  OBJ_MODEL_LOADER_TEST
     *
     *  Created by Christos Sotiriou on 8/19/08.
     *  Copyright 2008 Tei of Pireus. All rights reserved.
     *
     */
    
    #include "MTLLoaders.h"
    
    MTLMaterial::MTLMaterial(){
    	revertToDefault();
    }
    
    MTLMaterial::MTLMaterial (const MTLMaterial &other){
    	for (int i=0; i<4; i++) {
    		diffuse[i] = other.diffuse[i];
    		ambient[i] = other.ambient[i];
    		specular[i] = other.specular[i];
    		emmissive[i] = other.emmissive[i];
    	}
    	name = other.name;
    	shininess = other.shininess;
    	IDTextura = other.IDTextura;
    }
    
    MTLMaterial& MTLMaterial::operator=(const MTLMaterial &other){
    	for (int i=0; i<4; i++) {
    		diffuse[i] = other.diffuse[i];
    		ambient[i] = other.ambient[i];
    		specular[i] = other.specular[i];
    		emmissive[i] = other.emmissive[i];
    	}
    	name = other.name;
    	shininess = other.shininess;
    	IDTextura = other.IDTextura;
    	return *this;
    }
    
    void MTLMaterial::revertToDefault(){
    	//name = "untitled SF Material";
    	shininess = 65.0;
    	diffuse[0] = 0.2;
    	diffuse[1] = 0.2;
    	diffuse[2] = 0.2;
    	diffuse[3] = 1.0;
    	ambient[0] = 0.1;
    	ambient[1] = 0.1;
    	ambient[2] = 0.1;
    	ambient[3] = 1.0;
    	specular[0] = 0.0;
    	specular[1] = 0.0;
    	specular[2] = 0.0;
    	specular[3] = 1.0;
    	IDTextura = -1;
    }
    
    void MTLMaterial::showInfo(){
    	cout << "name: " << name << '\n';
    	cout << "Diffuse: " << diffuse[0] << ',' << diffuse[1] << ',' << diffuse[2] << ',' << diffuse[3] << '\n';
    	cout << "ambient: " << ambient[0] << ',' << ambient[1] << ',' << ambient[2] << ',' << ambient[3] << '\n';
    	cout << "specular: " << specular[0] << ',' << specular[1] << ',' << specular[2] << ',' << specular[3] << '\n';
    	cout << "emmissive: " << emmissive[0] << ',' << emmissive[1] << ',' << emmissive[2] << ',' << emmissive[3] << '\n';
    }
    Any idea what is wrong?
     
  14. pailes macrumors newbie

    Joined:
    May 10, 2008
    #14
    Don't wanna be a bitch, but you should use initializer lists instead of direct initialization in the constructor body ;)
    (and while we're at it, you should also mark functions as const if they don't alter the instance :p)
     
  15. ncl macrumors member

    Joined:
    Aug 16, 2008
    #15
    At first glance, it seems there are some problems here:
    Code:
    	else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3){
    		currentTriangle.TriangleEdges[0] = v;
    		currentTriangle.TriangleTextureCoords[0] = t;
    		currentTriangle.TriangleNormals[0] = n;
    		fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleTextureCoords[1]);
    		fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleNormals[1]);
    	}
    
    
    It looks like you don't decrement the indices. You should add something like:
    Code:
    for(unsigned i=0; i<3; ++i){
    	--currentTriangle.TriangleEdges[i];
    	--currentTriangle.TriangleTextureCoords[i];
    	--currentTriangle.TriangleNormals[i];
    }
    
    For the same reason that previously, you had to add "--v; --n;".
    Also, the format for the fscanf's should be "%d/%d/%d".

    If that doesn't fix the problem, you should post a complete project, so we can test it and have a better idea of what's wrong.
     
  16. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #16
    You were right. I also found another error here:
    Code:
     fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleTextureCoords[1]);
    		fscanf(filePointer, "%d/%d%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleNormals[1]);
    
    That should be:

    Code:
    fscanf(filePointer, "%d/%d/%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleTextureCoords[1]);
    					fscanf(filePointer, "%d/%d/%d", &currentTriangle.TriangleEdges[2], &currentTriangle.TriangleTextureCoords[2], &currentTriangle.TriangleNormals[2]);
    Again, thanks.

    You are not a bitch, every suggestion is welcome. However, there are tons of suggestions you could make about my entire code, which is completely unoptimized. I will correct everything in my final version (when there is any).
     
  17. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #17
    I hope that this is my final problem...

    Well, I am proceeding with this, and I managed to load a texture and apply it onto my model. However, I have 2 problems:

    1. The texture is not aligned correctly. Many parts seem misplaced.
    2. The program is EXTREMELY slow (one frame each 6 seconds!!), although I am not doing anything interesting...

    I am posting the whole project, so that you can find out what's wrong without having to write any code... Note that inside the "build" directory there are files you need to load, including some sample objects with textures!

    Download the project here
     
  18. ncl macrumors member

    Joined:
    Aug 16, 2008
    #18
    The performance problem is related to the texture: you bind a new texture for each triangle. However, binding a texture is a very expensive operation.
    Moreover, it seems that you don't generate an id for the texture (with glGenTextures).
    I made the following changes (it's not exactly beautiful, but it works):
    Code:
    void OBJModel::drawFaceAtIndex(unsigned index){
    	static int lastTexUsed = -1;
    //	int currentTexturePosition = this->tgaTexturePositionWithTexID(triangles[index].textureID);
    
    (...)
    
    	if(this->hasTextures && lastTexUsed != this->triangles[index].textureID){
    		
    		printf("binding texture %d\n", this->triangles[index].textureID);
    				
    		if(this->triangles[index].textureID == -1)
    			glBindTexture(GL_TEXTURE_2D, 0);
    		else
    			glBindTexture(GL_TEXTURE_2D, this->triangles[index].textureID);
    		
    		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    		
    		lastTexUsed = this->triangles[index].textureID;
    	}
    (...)
    
    And, in loadMtlFromFile:
    Code:
    case 'm':{
    	char *textureFileName = (char *)malloc(FILENAME_MAX);
    	fgets(textureFileName, FILENAME_MAX, filepointer);
    	sscanf(textureFileName, "%s", textureFileName, textureFileName);
    	//string textureFileNameStringed = textureFileName;
    	//free(textureFileName);
    	if(strncmp(buf, "map_Kd", 6) == 0) 
    	{
    		cout << "texture found: " << textureFileName << '\n';
    		TGALoaderCPP alpha;
    		alpha.LoadTGA(textureFileName);
    		glEnable(GL_TEXTURE_2D);
    		glGenTextures(1, &(alpha.texture.texID));
    		glBindTexture(GL_TEXTURE_2D, alpha.texture.texID);
    		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, alpha.texture.width, alpha.texture.height, 0, alpha.texture.type, GL_UNSIGNED_BYTE, alpha.texture.imageData);
    		glBindTexture(GL_TEXTURE_2D, 0);
    		printf("texture id %d\n", alpha.texture.texID);
    		textures.push_back(alpha);
    	}
    	free(textureFileName);
    }
    	break;
    
    Also, normalizing the normals can be done when they are loaded, and not each time they are used.

    That solved the performance problem (at least for me). But the textures are still problematic. I haven't figured out why but I think there may be a problem with the tga loader, because I tried with another model and the texture's colors were wrong.

    EDIT: There is a small bug with the normals:
    Code:
    fscanf(filePointer, "%d/%d/%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleTextureCoords[1]);
    fscanf(filePointer, "%d/%d/%d", &currentTriangle.TriangleEdges[2], &currentTriangle.TriangleTextureCoords[2], &currentTriangle.TriangleNormals[2]);
    
    Should be:
    Code:
    fscanf(filePointer, "%d/%d/%d", &currentTriangle.TriangleEdges[1], &currentTriangle.TriangleTextureCoords[1], &currentTriangle.TriangleNormals[1]);
    fscanf(filePointer, "%d/%d/%d", &currentTriangle.TriangleEdges[2], &currentTriangle.TriangleTextureCoords[2], &currentTriangle.TriangleNormals[2]);
    
     
  19. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #19
    I tried following your suggestions, and indeed the performance increase was great. Also, there may be a problem with the loader, but certainly that's not the only one... I tried with the cone model, and apart from the different color of the texture, I also receive misplaced texture. Badly mapped.
     

    Attached Files:

  20. ncl macrumors member

    Joined:
    Aug 16, 2008
    #20
    It looks like the problem is in the tga file loader. Here are 2 pictures. The first one was made using your tga loader. The other one was made using sdl_image to load the texture.
     

    Attached Files:

  21. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #21
    OK, thanks. Since I am not using SDL, I must find a library that can load the textures using only OpenGL...
     
  22. ncl macrumors member

    Joined:
    Aug 16, 2008
    #22
    I think I found a "fix" for the tga loader. I had to change two things.
    First this:
    Code:
    texture->imageData[cswap] ^= texture->imageData[cswap+2] ^= texture->imageData[cswap] ^= texture->imageData[cswap+2];
    
    It doesn't work. Replace it by:
    Code:
    texture->imageData[cswap] ^= texture->imageData[cswap+2];
    texture->imageData[cswap+2] ^= texture->imageData[cswap];
    texture->imageData[cswap] ^= texture->imageData[cswap+2];
    
    Or by a more "usual" swap using a temp variable. That should fix the color problem.

    Next, I had to manually convert 24bpp RGB textures into 32bpp RGBA textures. It's not hard, and it's what GL does anyway, but I really don't understand why it doesn't work with a RGB texture. I'll look into that when I have more time because it is really strange. I must be missing something...

    If you need another lib to load textures, why not using Cocoa ?
     
  23. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #23
    I will try it later today and see what happens... I noticed something similar, too. Perhaps it has something to do with the tga loader not being able to determine if the tga bits are 24 or 32?

    You mean using NSImage? Or NSData?
     
  24. ncl macrumors member

    Joined:
    Aug 16, 2008
    #24
    I don't think so. If the tga loader couldn't determine whether the image is a 24 bpp or 32 bpp, we should see weird colors (if a 32 bpp image is interpreted as 24 bpp) or crashes (a 24 bpp image interpreted as 32 bpp). However, all I see is a distorted image. The colors are right and there is no crash.

    By using a NSBitmapImageRep. Something like this:
    Code:
    -(GLuint)loadTexture:(NSString*)imagePath{
    	NSImage *image = [[NSImage alloc] initWithContentsOfFile:imagePath];
    	NSBitmapImageRep *bitmap = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]];
    	if (bitmap == nil){
    		NSLog([@"LoadGLTextures : could not load " stringByAppendingString:imagePath]);
    		return 0;
    	}
    	GLuint tex;
    	glGenTextures(1, &tex);
    	glBindTexture(GL_TEXTURE_2D, tex);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA8, [bitmap size].width, [bitmap size].height, [bitmap hasAlpha] ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, [bitmap bitmapData]);
    	
    	[image release];
    
    	return tex;
    }
    
    I am not sure if it is 100% correct: the last time I did this was over a year ago. But it's the idea.
     
  25. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #25
    That proved invaluable, thanks. I will experiment with this and I will see how it goes. But it seems this method can load virtually any bitmap image data. I find it hard to understand the role of NSBitmapRep and NSBitmapImageRep. I can't understand how does this code converts the image data from tga or jpg or anything else to plain unsigned bytes that OpenGL needs. I am searching documentation on Apple's website and they seem to have forgotten to fill some blanks...
     

Share This Page