Sign in to follow this  
Erondial

OBJ Loader - Strange Problem

Recommended Posts

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]

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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];
}
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this