Skip to main content
gamedev.net
🔒 Locked

OBJ Import problems

Started by Patroclos Dec 18, 2005 at 3:01 AM 13 replies 1.9k views
Original Post
Patroclos
Patroclos
hi, I have been trying to write a 3D asteroid hunting space simulation, however I have serious problems with importing my spaceship. I have modeled it with Maya and exported it with polytrans plug-in. I have tried open-source obj import libraries but my ship looked a little bit deformed once I displayed it. Then I have written my importer code, and for simplicity I have only written code for importing vertices, faces and vertex normals. But it again displayed deformed, as if some faces are defined wrong. But I don't think my library or open-source library I have used have problems, and also I don't think Maya and the plug-in have problems with exporting the obj file. Does anyone know where the problem should be? is there a process that I should do after exporting the file? btw, here is my simple obj importer code:

[/source
int LoadObjFileToClass(char* filename, object* ObjClass) {

	std::ifstream ObjFile(filename);

	if(!ObjFile.is_open())
		return NULL;
			
			//yuzey, vertex vs. sayilarini say
	std::string line, sub;	//bir satir maksimum 80 karakter
	int numVertex = 0, numFace = 0, numNormal = 0;
	while(getline(ObjFile, line)) {
		
		sub = line.substr(0,2);
		if(sub == "v ")
			numVertex++;
		else if(sub == "f ")
			numFace++;
		else if(sub == "vn")
			numNormal++;
	}
			//gerekli yerleri tahsis et atamalari yap
	ObjClass->LocalVertices = new Vertex3D[numVertex];
	ObjClass->Faces			= new Facet[numFace];
	ObjClass->Normals		= new Normal[numNormal];

	ObjClass->NumVertices	= numVertex;
	ObjClass->NumFaces		= numFace;
	ObjClass->NumNormals	= numNormal;

 	ObjFile.close();

	std::ifstream ObjFile2(filename);
	
	int vertex = 0, face = 0, normal = 0;
	int vi1, vi2, vi3, ni1, ni2, ni3, vt1, vt2, vt3;
	float x, y, z;

	while(getline(ObjFile2, line)) {
		const char* FormattedLine = line.c_str();
		sub = line.substr(0,2);
		std::string empty;

		if(sub == "v ") {
			//string vertex tanimliyor
			sscanf(FormattedLine, "%s %f %f %f", ∅, &x, &y, &z);
			ObjClass->LocalVertices[vertex].x = x;
			ObjClass->LocalVertices[vertex].y = y;
			ObjClass->LocalVertices[vertex].z = z;
			vertex++;
		}
		else if(sub == "f ") {
            sscanf(FormattedLine, "%s %d/%d/%d %d/%d/%d %d/%d/%d", ∅, 
				&vi1, &vt1, &ni1, 
					&vi2, &vt2, &ni2, 
						&vi3, &vt3, &ni3);
			ObjClass->Faces[face].VertexIndices[0] = vi1;
			ObjClass->Faces[face].VertexIndices[1] = vi2;
			ObjClass->Faces[face].VertexIndices[2] = vi3;

			ObjClass->Faces[face].Normals[0] = ni1;
			ObjClass->Faces[face].Normals[1] = ni2;
			ObjClass->Faces[face].Normals[2] = ni3;
			face++;
		}
		else if(sub == "vn") {
			sscanf(FormattedLine, "%s %f %f %f", ∅, &x, &y, &z);
			ObjClass->LocalVertices[vertex].x = x;
			ObjClass->LocalVertices[vertex].y = y;
			ObjClass->LocalVertices[vertex].z = z;
			normal++;
		}
	}

	return true;

}]


Dave Hunt
Dave Hunt
Are any of the face indices negative? A positive value indexes from the beginning of the vertex list. A negative value indexes from the end of the list. I don't see any check in your import code for negative indices.
ajas95
ajas95
.obj files use indices that are 1-based instead of 0-based, so you have to subtract 1 when you build your lists.

Twice I've written an obj importer, and twice I've had that same bug. Looks like it could be your problem also.
Patroclos
Patroclos
Dave I ckecked my obj file but there doesn't seem to be any negative indices.
I think the odd character in sscanf occured while copying code into forum. Because there is no such a character in the source code.
while checking obj file I think I have found a bug. There are 4-5 faces that are defined by 18 vertices. but I have triangulated all polygons in maya and I have used only quads to model the ship. this seems to be strange problem
Dave Hunt
Dave Hunt
Quote:
Original post by Anonymous Poster
Dave Hunt, you are sure about the negative indexes? I never saw such a .obj file


Yep. Check the OBJ Format. Look at the section headed "Referencing vertex data".

Also, ajas95 has made a significant point - the indices are 1-based, not 0-based. So, all your indices are going to be off by one unless you subtract 1 from each index.
JakeM
JakeM
I think most errors occur when people make assumptions about spacing and formatting.

I see you are making an assumption that every "f" line is formatted in some certain way, and that's not correct.
Index parameters on that line can be optional, and you're assuming every face is a triangle, which is wrong too.
I think you need another way other than "sscanf" to process a "f" line. Need to step through it, token by token.

[Edited by - JakeM on December 18, 2005 8:31:32 PM]
Patroclos
Patroclos
I have triangulated all polygons in Maya.. That's why I written code for only reading triangles. However I thought I have turned all polygons into triangles, but I have missed some polygons as I see.. I think it happened because I didn't make triangulation in object mode.. anyway I have solved the problem, now I can draw the ship however there is still a small problem. The details of the ship are lost, I mean there were some spacings on the ship but they are all filled.. as if there has been a big data loss during conversion from double to float.

By the way, JakeM you're right but I just wanted to write a specific obj loader. I mean I just wanted to use it in that game not in general that's why I don't make so many format checkings. Actually I wanted to use open-source libraries for importing but when I encountered problems I decided to write my own importer..
RobTheBloke
RobTheBloke
normally the problem occurs because a bit of maya geometry has removed the UV's, do a find in the obj for //

If you find one, thats your problem.

obj loader that shouldn't break...
bluntman
bluntman
Heres my code for importing OBJs exported from Maya, it will handle most OBJ formatting
bool TGeoForm::LoadOBJFile(AnsiString filename, mesh *msh, float scale){    FILE *fp = fopen(filename.c_str(), "rt");    if(fp == NULL)        return(false);    int i;    try    {        char s[2049];        msh->DeleteAll();        point *pt;        polygon *poly;        bool hasuvs = false, hasnormals = false, haspts = false;        int ccount = 0;        bool namefound = false;        while(fgets(s, 2048, fp) != NULL)        {            if(toupper(s[0]) == 'G')            {                //if(namefound)                //    return(false);                namefound = true;            }            else if(toupper(s[0]) == 'V')            {                if(toupper(s[1]) == 'N')                {                    pt = new point;                    if(sizeof(GEOFLOAT) == 4)                        sscanf(&s[3], "%f %f %f", &pt->x, &pt->y, &pt->z);                    else                        sscanf(&s[3], "%lf %lf %lf", &pt->x, &pt->y, &pt->z);                    pt->w = 0.0f;                    msh->AddNormal(pt);                                        hasnormals = true;                }                else if(toupper(s[1]) == 'T')                {                    pt = new point;                    if(sizeof(GEOFLOAT) == 4)                        sscanf(&s[3], "%f %f", &pt->x, &pt->y);                    else                        sscanf(&s[3], "%lf %lf", &pt->x, &pt->y);                    pt->z = pt->w = 0.0f;                    msh->AddUV(pt);                    hasuvs = true;                }                else                {                    pt = new point;                    if(sizeof(GEOFLOAT) == 4)                        sscanf(&s[2], "%f %f %f", &pt->x, &pt->y, &pt->z);                    else                        sscanf(&s[2], "%lf %lf %lf", &pt->x, &pt->y, &pt->z);                    pt->w = 0.0f;                    *pt *= scale;                    msh->AddPoint(pt);                    haspts = true;                }            }            else if(toupper(s[0]) == 'F')            {                poly = new polygon;                char *ps = &s[2];                char num[256], *pnum;                poly->sides = 0;                for(i=0; i<4; i++)                {                    poly->points = poly->normals = poly->uvs = -1;                }                bool done = false;                int val;                while(!done)                {                    while(*ps == ' ')                        ps ++;                    // points                    pnum = num;                    *pnum = 0;                    while((*ps != '/' && *ps != ' ') && !done)                    {                        *pnum = *ps;                        pnum ++; ps ++;                        if(*ps == 0)                            done = true;                    }                    *pnum = 0;                    if(sscanf(num, "%d", &val) == 1)                    {                        if(val < 0)                            poly->points[poly->sides] = msh->NormalCount()+val;                        else                            poly->points[poly->sides] = val - 1;                    }                    if(*ps != ' ')                    {                        ps ++;                        // uv                        pnum = num;                        *pnum = 0;                        while((*ps != '/' && *ps != ' ') && !done)                        {                            *pnum = *ps;                            pnum ++; ps ++;                            if(*ps == 0)                                done = true;                        }                        *pnum = 0;                        if(sscanf(num, "%d", &val) == 1)                        {                            if(hasuvs)                            {                                if(val < 0)                                    poly->uvs[poly->sides] = msh->UVCount()+val;                                else                                    poly->uvs[poly->sides] = val - 1;                            }                            else if(hasnormals)                            {                                if(val < 0)                                    poly->normals[poly->sides] = msh->NormalCount()+val;                                else                                    poly->normals[poly->sides] = val - 1;                            }                            else                            {                                ShowMessage("More cv indicies than data types in obj (there are no normals "                                    "or uvs but there are poly indicies for them)");                                return(false);                            }                        }                        if(*ps != ' ')                        {                            ps ++;                                                        // normal                            pnum = num;                            *pnum = 0;                            while((*ps != '/' && *ps != ' ') && !done)                            {                                *pnum = *ps;                                pnum ++; ps ++;                                if(*ps == 0)                                    done = true;                            }                            *pnum = 0;                            if(sscanf(num, "%d", &val) == 1)                            {                                if(val < 0)                                    poly->normals[poly->sides] = msh->NormalCount()+val;                                else                                    poly->normals[poly->sides] = val - 1;                            }                        }                    }                    poly->sides ++;                    if(poly->sides>4)                    {                        ShowMessage("Object contains polygons with more than 4 sides! Ouch..");                        return(false);                    }                }                msh->AddPolygon(poly);            }        }    }    __finally    {        fclose(fp);    }    return(true);}
RobTheBloke
RobTheBloke
code i use to read the face 'f' flags and triangulate the polygons on input (it assumes that 'f' has been read from the file). Yes it uses goto, but it works so there :p

	//----------------------------------------------------------------------	File :: ReadFace	void File::ReadFace(std::istream& ifs) {		char c;		std::vector<std::string> VertInfo;		// store all strings		do {			// strip white spaces			c = ifs.get();			if (ifs.eof()) {				goto vinf;			}			while(c==' ' || c=='\t') {				c=ifs.get();				if (ifs.eof()) {					goto vinf;				}			}			std::string s;			// read vertex info			while(c!=' '&&c!='\t'&&c!='\n') {				s+=c;				c=ifs.get();				if (ifs.eof()) {					goto vinf;				}			}			// store string			VertInfo.push_back(s);		} 		while(c!='\n'); // loop till end of linevinf: ;		std::vector<int> verts;		std::vector<int> norms;		std::vector<int> uvs;		// split strings into individual indices		std::vector<std::string>::const_iterator it = VertInfo.begin();		for( ; it != VertInfo.end(); ++it ) {			int v, n=0, t=0;						if(HasOnlyVertex(*it)) 				sscanf(it->c_str(),"%d",&v);			else			if(MissingUv(*it))				sscanf(it->c_str(),"%d//%d",&v,&n);			else 			if(MissingNormal(*it)) 				sscanf(it->c_str(),"%d/%d/",&v,&t);			else				sscanf(it->c_str(),"%d/%d/%d",&v,&t,&n);			if (v<0) {				v=static_cast<int>(m_Vertices.size())+v+1;			}			if (n<0) {				n=static_cast<int>(m_Normals.size())+n+1;			}			if (t<0) {				t=static_cast<int>(m_TexCoords.size())+t+1;			}			// obj indices are 1 based, change them to zero based indices			--v; --n; --t;			verts.push_back(v);			norms.push_back(n);			uvs.push_back(t);		}				// construct triangles from indices		for(unsigned i=2;i<verts.size();++i) {			Face f;						// construct triangle			f.v[0] = verts[0];			f.n[0] = norms[0];			f.t[0] = uvs[0];			f.v[1] = verts[i-1];			f.n[1] = norms[i-1];			f.t[1] = uvs[i-1];			f.v[2] = verts;			f.n[2] = norms;			f.t[2] = uvs;			// append to list			m_Triangles.push_back(f);		}		}
RobTheBloke
RobTheBloke
Quote:
Original post by Anonymous Poster
Dave Hunt, you are sure about the negative indexes? I never saw such a .obj file

Patroclos, what does "∅" in sscanf means? I have no idea what this should do
sscanf(FormattedLine, "%s %f %f %f", ∅, &x, &y, &z);


yes, the obj file supports negative indices. if it uses positive indices, then the vertex reference is relative to the start of the file (maya only does +ve). If it is a negative index it is relative to it's current position (not as posted before, from the end of the list). ie, -1 indicates the last vertex read.
Dave Hunt
Dave Hunt
Quote:
Original post by RobTheBloke
If it is a negative index it is relative to it's current position (not as posted before, from the end of the list). ie, -1 indicates the last vertex read.


That's actually what I meant - the end of the list at the time the negative reference is made. But you're right. My statement was misleading.
Patroclos
Patroclos
Quote:
Original post by ajas95
.obj files use indices that are 1-based instead of 0-based, so you have to subtract 1 when you build your lists.

Twice I've written an obj importer, and twice I've had that same bug. Looks like it could be your problem also.


ajas95 you were right :) I have subtracted 1 from each index and it drew the ship correctly.. thanks very much too all for help

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.