Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
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?
 

Attachments

  • lightcycle(low_quality).obj.zip
    4.4 KB · Views: 174

lazydog

macrumors 6502a
Sep 3, 2005
709
6
Cramlington, UK
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
 

ncl

macrumors member
Aug 16, 2008
58
0
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.
 

Attachments

  • Picture 1.png
    Picture 1.png
    53.2 KB · Views: 190

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
Thanks, but...

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.

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?
 

ncl

macrumors member
Aug 16, 2008
58
0
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
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
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.
 

Attachments

  • Picture 1.png
    Picture 1.png
    1.8 KB · Views: 144
  • Cone.obj.zip
    1.8 KB · Views: 126
  • Picture 2.png
    Picture 2.png
    9.9 KB · Views: 126
  • OBJ_MODEL_LOADER_TEST.zip
    116.3 KB · Views: 172

ncl

macrumors member
Aug 16, 2008
58
0
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.
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
That solved my problem. I can't tell you how grateful I am. Thanks a lot.
 

Attachments

  • Picture 1.png
    Picture 1.png
    29 KB · Views: 140

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
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?

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!).
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
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...>>
 

Attachments

  • without texture.zip
    4.3 KB · Views: 126
  • with texture.zip
    112.9 KB · Views: 146

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
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?
 

pailes

macrumors newbie
May 10, 2008
2
0
Code:
OBJFileInfo::OBJFileInfo(){
	numVertices = 0;
	numTriangles = 0;
	numVertexNormals = 0;
	numTextureVertex = 0;
	numGroups = 0;
}

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)
 

ncl

macrumors member
Aug 16, 2008
58
0
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.
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
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.

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.

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 )
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).
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
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
 

ncl

macrumors member
Aug 16, 2008
58
0
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]);
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
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.
 

Attachments

  • Picture 1.png
    Picture 1.png
    27.9 KB · Views: 129

ncl

macrumors member
Aug 16, 2008
58
0
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.
 

Attachments

  • Picture 1.png
    Picture 1.png
    66.9 KB · Views: 133
  • Picture 2.png
    Picture 2.png
    80.2 KB · Views: 139

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
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.

OK, thanks. Since I am not using SDL, I must find a library that can load the textures using only OpenGL...
 

ncl

macrumors member
Aug 16, 2008
58
0
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 ?
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
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...

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?

If you need another lib to load textures, why not using Cocoa ?

You mean using NSImage? Or NSData?
 

ncl

macrumors member
Aug 16, 2008
58
0
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?
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.

You mean using NSImage? Or NSData?
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.
 

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
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...
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.