OBJ Loader - Strange Problem

Started by
8 comments, last by Erondial 16 years ago
I'm having an incredibly strange problem with my wavefront OBJ loader. It loads everything fine, all the vertices are in proper order, normals are fine, but when it comes to texture coordinates, things are a little screwed up. When I make a cube in Blender, for example, with the numbers 1 through 6 on each side, and load it into my game, the cube shows up fine, it's lit properly, but the 1 through 6 are all over the place, clipped in some areas, not even there in others. I have no idea why this might happen. The UVs are there, I can import the model back into Blender, no problems, so the data is THERE, it's just not getting used properly. I'm going to dump my code here. It's not too long, I'm only interested in getting the most basic parts working first. As I say, I have absolutely no idea as to why it's doing this, everything seems fine to me. If anyone has any ideas, please post, and help me out. Vertices, TexCoords, and Normals are all arrays of simple structures that hold 3, 2 and 3 floats respectively. I draw them using glDrawArrays(GL_TRIANGLES,0,Triangles*3);
	bool Static::Load(char* Buffer, int Size)
	{
        	unsigned int Count = 0, VC = 0, VTC = 0, VNC = 0, FC = 0, VI[3],TI[3],NI[3];
		char Line[256]; memset(Line,0,sizeof(Line));
		Vertex3D* UVertices; Normal3D *UNormals; Point2D* UTexCoords;
		Clear();
		// First Parse : Counts Vertices, Normals, Texture Coordinates and Faces
		for (Count = 0;; Count += sgets(Line,256,&Buffer[Count])+1){
            		if (strncmp("v ",Line,2) == 0) VC++;
            		if (strncmp("vn ",Line,3) == 0) VNC++;
            		if (strncmp("vt ",Line,3) == 0) VTC++;
            		if (strncmp("f ",Line,2) == 0) FC++;
            		if (Count >= Size) break;
        	}
        	if (VNC != 0) HasNormals = true; if (VTC != 0) HasTexture = true;
        	UVertices = new Vertex3D[VC]; if (HasTexture) UTexCoords = new Point2D[VTC]; if (HasNormals) UNormals = new Point3D[VNC];
        	Vertices = new Vertex3D[FC*3]; if (HasTexture) TexCoords = new Point2D[FC*3]; if (HasNormals) Normals = new Normal3D[FC*3];
        	// Second Parse : Read Vertices, Normals, Texture Coordinates and Faces into Organized Arrays
        	memset(Line,0,sizeof(Line));
        	for (Count = 0, VC = 0, VNC = 0, VTC = 0, FC = 0;; Count += sgets(Line,256,&Buffer[Count])+1){
            		if (strncmp("v ",Line,2) == 0){ sscanf(Line,"%*c %f %f %f",&UVertices[VC].X,&UVertices[VC].Y, &UVertices[VC].Z); VC++; }
            		if (strncmp("vn ",Line,3) == 0){ sscanf(Line,"%*s %f %f %f",&UNormals[VNC].X,&UNormals[VNC].Y, &UNormals[VNC].Z); VNC++; }
            		if (strncmp("vt ",Line,3) == 0){ sscanf(Line,"%*s %f %f",&UTexCoords[VTC].X,&UTexCoords[VTC].Y); VTC++;}
            		if (strncmp("f ",Line,2) == 0){
                		if (HasTexture && HasNormals)
                    			sscanf(Line,"%*c %d %*c %d %*c %d %d %*c %d %*c %d %d %*c %d %*c %d",&VI[0],&TI[0],&NI[0],&VI[1],&TI[1],&NI[1],&VI[2],&TI[2],&NI[2]);
                		else if (HasTexture && !HasNormals)
                   			sscanf(Line,"%*c %d %*c %d %*c %*d %d %*c %d %*c %*d %d %*c %d %*c %*d",&VI[0],&TI[0],&VI[1],&TI[1],&VI[2],&TI[2]);
                		else if (!HasTexture && HasNormals)
                    			sscanf(Line,"%*c %d %*c %*c %d %d %*c %*c %d %d %*c %*c %d",&VI[0],&NI[0],&VI[1],&NI[1],&VI[2],&NI[2]);
                		else if (!HasTexture && !HasNormals)
                    			sscanf(Line,"%*c %d %*c %*d %*c %*d %d %*c %*d %*c %*d %d %*c %*d %*c %*d",&VI[0],&VI[1],&VI[2]);
                		Vertices[FC*3+0] = UVertices[(VI[0]-1)]; Vertices[FC*3+1] = UVertices[(VI[1]-1)]; Vertices[FC*3+2] = UVertices[(VI[2]-1)];
                		if (HasNormals){ Normals[FC*3+0] = UNormals[NI[0]-1]; Normals[FC*3+1] = UNormals[NI[1]-1]; Normals[FC*3+2] = UNormals[NI[2]-1]; }
                		if (HasTexture){ TexCoords[FC*3+0] = UTexCoords[TI[0]-1]; TexCoords[FC*3+1] = UTexCoords[TI[1]-1]; TexCoords[FC*3+2] = UTexCoords[(TI[2]-1)]; }
                		FC++;
            		}
            		if (Count >= Size) break;
        	}
        	Triangles = FC;
        	delete[] UVertices; delete[] UTexCoords; delete[] UNormals;
        	return true;
	}
[Edited by - Erondial on April 20, 2008 1:53:18 PM]
Advertisement
I looked at the code yesterday, and I really can't find anything wrong...

Could you post more code? Specifically, the part where you set up your arrays before drawing them?

Or maybe post working code in a zip file, so we can toy around with it?
Well, uh, it's easier for me to post just the class source than the entire thing, since to work, right now, the engine needs several hard coded data files that are kinda large, so I'll just post the Static class, and it's header.

CaeStatic.h
#ifndef CAESTATIC_H#define CAESTATIC_H#include "CaeModel.h"namespace Caelus{    class Static : public Model {        public:            Static();            ~Static();            bool Load(char* Buffer, int Size);            bool Load(const char* Path);            void Draw();            void Clear();    };}#endif


CaeStatic.cpp
#include <GL/gl.h>#include <cstdlib>#include <cstdio>#include <cstring>#include "CaeStatic.h"#include "CaeMisc.h"using namespace std;namespace Caelus{    Static::Static()    {    }    Static::~Static()    {        Clear();    }    void Static::Draw()	{		glVertexPointer(3,GL_FLOAT,0,Vertices);		if (HasNormals){            glEnableClientState(GL_NORMAL_ARRAY);            glNormalPointer(GL_FLOAT,0,Normals);        }        else            glDisableClientState(GL_NORMAL_ARRAY);		if (HasTexture){            glEnableClientState(GL_TEXTURE_COORD_ARRAY);		    glTexCoordPointer(2,GL_FLOAT,0,TexCoords);        }		else            glDisableClientState(GL_TEXTURE_COORD_ARRAY);		glDrawArrays(GL_TRIANGLES,0,Triangles*3);	}	bool Static::Load(char* Buffer, int Size)	{        unsigned int Count = 0, VC = 0, VTC = 0, VNC = 0, FC = 0, VI[3],TI[3],NI[3];		char Line[256];		Vertex3D* UVertices; Normal3D *UNormals; Point2D* UTexCoords;		Clear();		// First Parse : Counts Vertices, Normals, Texture Coordinates and Faces		for (Count = 0, memset(Line,0,sizeof(Line));; Count += sgets(Line,256,&Buffer[Count])+1){            if (strncmp("v ",Line,2) == 0) VC++;            if (strncmp("vn ",Line,3) == 0) VNC++;            if (strncmp("vt ",Line,3) == 0) VTC++;            if (strncmp("f ",Line,2) == 0) FC++;            if (Count >= Size) break;        }        if (VNC != 0) HasNormals = true; if (VTC != 0) HasTexture = true;        UVertices = new Vertex3D[VC]; if (HasTexture) UTexCoords = new Point2D[VTC]; if (HasNormals) UNormals = new Point3D[VNC];        Vertices = new Vertex3D[FC*3]; if (HasTexture) TexCoords = new Point2D[FC*3]; if (HasNormals) Normals = new Normal3D[FC*3];        // Second Parse : Read Vertices, Normals, Texture Coordinates and Faces into Organized Arrays        for (Count = 0, VC = 0, VNC = 0, VTC = 0, FC = 0, memset(Line,0,sizeof(Line));; Count += sgets(Line,256,&Buffer[Count])+1){            if (strncmp("v ",Line,2) == 0){ sscanf(Line,"%*c %f %f %f",&UVertices[VC].X,&UVertices[VC].Y, &UVertices[VC].Z); VC++; }            if (strncmp("vn ",Line,3) == 0){ sscanf(Line,"%*s %f %f %f",&UNormals[VNC].X,&UNormals[VNC].Y, &UNormals[VNC].Z); VNC++; }            if (strncmp("vt ",Line,3) == 0){ sscanf(Line,"%*s %f %f",&UTexCoords[VTC].X,&UTexCoords[VTC].Y); VTC++;}            if (strncmp("f ",Line,2) == 0){                if (HasTexture && HasNormals)                    sscanf(Line,"%*c %d %*c %d %*c %d %d %*c %d %*c %d %d %*c %d %*c %d",&VI[0],&TI[0],&NI[0],&VI[1],&TI[1],&NI[1],&VI[2],&TI[2],&NI[2]);                else if (HasTexture && !HasNormals)                    sscanf(Line,"%*c %d %*c %d %*c %*d %d %*c %d %*c %*d %d %*c %d %*c %*d",&VI[0],&TI[0],&VI[1],&TI[1],&VI[2],&TI[2]);                else if (!HasTexture && HasNormals)                    sscanf(Line,"%*c %d %*c %*c %d %d %*c %*c %d %d %*c %*c %d",&VI[0],&NI[0],&VI[1],&NI[1],&VI[2],&NI[2]);                else if (!HasTexture && !HasNormals)                    sscanf(Line,"%*c %d %*c %*d %*c %*d %d %*c %*d %*c %*d %d %*c %*d %*c %*d",&VI[0],&VI[1],&VI[2]);                Vertices[FC*3+0] = UVertices[(VI[0]-1)]; Vertices[FC*3+1] = UVertices[(VI[1]-1)]; Vertices[FC*3+2] = UVertices[(VI[2]-1)];                for (int C = 0; C < 3; C++){                    if (Vertices[FC*3+C].X > Box.MaxPoints.X) Box.MaxPoints.X = Vertices[FC*3+C].X;                    if (Vertices[FC*3+C].X < Box.MinPoints.X) Box.MinPoints.X = Vertices[FC*3+C].X;                    if (Vertices[FC*3+C].Y > Box.MaxPoints.Y) Box.MaxPoints.Y = Vertices[FC*3+C].Y;                    if (Vertices[FC*3+C].Y < Box.MinPoints.Y) Box.MinPoints.Y = Vertices[FC*3+C].Y;                    if (Vertices[FC*3+C].Z > Box.MaxPoints.Z) Box.MaxPoints.Z = Vertices[FC*3+C].Z;                    if (Vertices[FC*3+C].Z < Box.MinPoints.Z) Box.MinPoints.Z = Vertices[FC*3+C].Z;                }                if (HasNormals){ Normals[FC*3+0] = UNormals[NI[0]-1]; Normals[FC*3+1] = UNormals[NI[1]-1]; Normals[FC*3+2] = UNormals[NI[2]-1]; }                if (HasTexture){ TexCoords[FC*3+0] = UTexCoords[TI[0]-1]; TexCoords[FC*3+1] = UTexCoords[TI[1]-1]; TexCoords[FC*3+2] = UTexCoords[(TI[2]-1)]; }                FC++;            }            if (Count >= Size) break;        }        Triangles = FC;        if (Box.MaxPoints.X >= Box.MaxPoints.Y && Box.MaxPoints.X >= Box.MaxPoints.Z) Radius = Box.MaxPoints.X;        if (Box.MaxPoints.Y >= Box.MaxPoints.X && Box.MaxPoints.Y >= Box.MaxPoints.Z) Radius = Box.MaxPoints.Y;        if (Box.MaxPoints.Z >= Box.MaxPoints.X && Box.MaxPoints.Z >= Box.MaxPoints.Y) Radius = Box.MaxPoints.Z;        delete[] UVertices; delete[] UTexCoords; delete[] UNormals;        return true;	}	bool Static::Load(const char* Path)	{        unsigned int Size;        FILE* File;        char* Buffer;        File = fopen(Path,"rb");        if (File == NULL)            return false;        fseek(File,0,SEEK_END);        Size = ftell(File);        fseek(File,0,SEEK_SET);        Buffer = new char[Size];        fread(Buffer,1,Size,File);        fclose(File);        Load(Buffer,Size);        delete[] Buffer;        return true;    }	void Static::Clear()	{        HasTexture = false;        HasNormals = false;        if (Vertices != NULL)            delete[] Vertices;        Vertices = NULL;        if (Normals != NULL)            delete[] Normals;        Normals = NULL;        if (TexCoords != NULL)            delete[] TexCoords;        TexCoords = NULL;        Radius = 0;        Triangles = 0;        Box.MaxPoints = Point3DG(0,0,0);        Box.MinPoints = Point3DG(0,0,0);    }}


CaeModel.h
#ifndef CAEMODEL_H#define CAEMODEL_H#include "CaeMath.h"#include "CaeCollision.h"namespace Caelus{	class Model	{		public:            bool HasTexture;            bool HasNormals;            Vertex3D* Vertices;            Point2D* TexCoords;            Normal3D* Normals;            unsigned int Triangles;            float Radius;            Box3D Box;			Model();			~Model();						virtual void Draw();			virtual bool Load(char* Buffer, int BufferSize);			virtual bool Load(const char* Name);			virtual void Clear();			void CalculateNormals();	};}#endif


CaeModel.cpp
#include <cstdlib>#include "CaeModel.h"namespace Caelus{	Model::Model()	{        HasTexture = false;        HasNormals = false;		Vertices = NULL;		Normals = NULL;		TexCoords = NULL;		Triangles = 0;		Radius = 0;		Box.MaxPoints = Point3DG(0,0,0);		Box.MinPoints = Point3DG(0,0,0);	}		Model::~Model()	{		Clear();	}		void Model::Draw()	{			}		bool Model::Load(char* Buffer, int BufferSize)	{            }		bool Model::Load(const char* Path)	{			}		void Model::Clear()	{        HasTexture = false;        HasNormals = false;        if (Vertices != NULL)            delete[] Vertices;        Vertices = NULL;        if (Normals != NULL)            delete[] Normals;        Normals = NULL;        if (TexCoords != NULL)            delete[] TexCoords;        TexCoords = NULL;        Triangles = 0;        Radius = 0;        Box.MaxPoints = Point3DG(0,0,0);		Box.MinPoints = Point3DG(0,0,0);    }        void Model::CalculateNormals()    {        if (Normals != NULL)            delete[] Normals;        Normals = new Normal3D[Triangles*3];    }}
Hrmm... Well, I'm completely stumped... How about the obj file? Did blender output the right texcoords?
Instead of numbers, try using a texture that just colors each face a different color? That might make it easier to see exactly how the uv coords are being messed up. Allows you to see exactly where the faces meet on the texture, as well as on the model it's applied to.
This code looks fine to me... Are the texture coordinates in the .obj file using the same range that your program expects (ie. 0 to 1)?
Any chance you could edit your posts to use [ source ] [ / source ] tags?

I don't see anything in particular wrong with your code, but why on earth are you manually manipulating fixed-length pascal-style strings?

Also note that many OBJ files contain lines longer than 256 characters - I have encountered lines over 1024 characters before, so you may want to use std::string and read up until a specific newline character. Or even better, since you are receiving a char array as an argument to your function, save yourself the overhead of copying portions, and just use strcmp and sscanf directly on the buffer.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Thank you all. I found the problem. It turns out for some reason that blender is inverting the Y portion of the texture coordiantes. Maybe my texture matrix is screwed up, who knows. But Spoonbender's idea was a good. Got me to see what the hell was going on. Thanks. And thank you Kiwibonga for actually go through the code. Most people wouldn't.

And swiftcoder, I'll use the source tags next time. Thanks. I thought code would do it. Anyyway I'm already reading from a buffer, I'm not copying anything. And I'm using pascal style strings... well... because. :P. No reason. I'm just used to them.

Anyway, thank you all! I really appreciate the help.
Quote:Original post by Erondial
And swiftcoder, I'll use the source tags next time. Thanks. I thought code would do it. Anyyway I'm already reading from a buffer, I'm not copying anything. And I'm using pascal style strings... well... because. :P. No reason. I'm just used to them.

source tags scroll, and provide code colouring - very handy for this amount of code ;)

I am pretty sure you are copying strings all over the place - what does your sgets() function do if it doesn't copy?

Limited size pascal strings are really passé in C/C++ - and even Pascal and D now use a 2 or 4 byte length field to get long strings.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Oh, yes, well THAT... That's an incredibly good point. I have no idea why I'm copying things. Thanks for pointing that out ^_^

This topic is closed to new replies.

Advertisement