Sign in to follow this  
Ainokea

Problem loading Quake 3 maps (*Screenshots*)

Recommended Posts

I am trying to load a BSP map and not follow any tutorials (as a test for myself to see if I can figure things out by myself (which atm I am failing), though I am using the file format specifications) and I am having problems. What it's supposed to look like (a screenshot from some random tutorials loader (I am using the same map)): Image hosted by Photobucket.com What it looks like when I am using it: Image hosted by Photobucket.com The texture mapping isnt the problem because I don't plan on starting on that until I finish loading the geometry correctly. Now to my code: (Before you read, I have this weird habit of whenever I try something new (loading a model, rendering terrain, etc.) I use intermediate mode, I plan to switch to vertex buffer objects afterwords.) Loading the map:
bool Q3Map::Load(std::string path)
{
    FILE *file;
    
    if((file = fopen(path.c_str(),"rb"))==NULL)
    {
        printf("Couldnt load BSP map");
        return false;
    }
    Q3Header header;
    
    fread(&header,1,sizeof(Q3Header),file);
    
    numVerts = header.entries[Vertcies].length / sizeof(Q3Vertex);
    verts    = new Q3Vertex[numVerts];
    
    numFaces = header.entries[Faces].length / sizeof(Q3Face);
    faces    = new Q3Face[numFaces];
    
    numIndex = header.entries[Indices].length / sizeof(Q3Index);
    indices  = new Q3Index[numIndex];
    
    numTextures = header.entries[Textures].length / sizeof(Q3Texture);
    textures    = new Q3Texture[numTextures];
    //
    fseek(file, header.entries[Vertcies].offset, SEEK_SET);
    fread(verts, numVerts, sizeof(Q3Vertex), file);
	                
    fseek(file, header.entries[Indices].offset, SEEK_SET);
    fread(indices, numIndex, sizeof(Q3Index), file);
	
    fseek(file, header.entries[Faces].offset, SEEK_SET);
    fread(faces, numFaces, sizeof(Q3Face), file);
	                
    fseek(file, header.entries[Textures].offset, SEEK_SET);
    fread(textures, numTextures, sizeof(Q3Texture), file);
	
	   // Must swap the axis for OpenGL.
    for(int i = 0; i < numVerts; i++)
    {
	float temp = verts[i].position[1];
	verts[i].position[1] = verts[i].position[2];
	verts[i].position[2] = -temp;
    }
	
    fclose(file);
    return true;
}




Rendering the map:
void Q3Map::Render()
{
    glBegin(GL_TRIANGLE_FAN);
    for(int i=0; i<numFaces; i++)
    {
        for(int j = 0; j < faces->startVertexIndex + numVerts; j++)
            {               glNormal3f(verts[j].normal[0],verts[j].normal[j],verts[j].normal[2]);   
      glColor4ub(verts[j].color[0],verts[j].color[1],verts[j].color[2],verts[j].color[3]);
               glVertex3f(verts[j].position[0],
                          verts[j].position[1],
                          verts[j].position[2]);
            }
    }
    glEnd();
}
[/source



[Edited by - Ainokea on May 25, 2005 2:15:23 AM]

Share this post


Link to post
Share on other sites
Let us see your structure definitions, it may be a simple case of your indices being too small or something so sizeof(Q3Index) gives the "wrong" value

hope that helps
-Dan

Share this post


Link to post
Share on other sites
Sure, here you go:

struct Q3Entry
{
int offset;
int length;
};

struct Q3Header
{
char idNumber[4];
int version;
Q3Entry entries[17];
};

enum Q3Lump
{
Entities,
Textures,
Planes,
Nodes,
Leafs,
Leaffaces,
Leafbrushes,
Models,
Brushes,
BrushSides,
Vertcies,
Indices,
Effects,
Faces,
Lightmaps,
Lightvols,
Visdata,
MaxLumpsOffset
};

struct Q3Entity
{
char *ents;
};

struct Q3Texture
{
char name[64];
int flags;
int contents;
};

struct Q3Model
{
float mins[3];
float maxs[3];
int face;
int n_faces;
int brush;
int n_brushes;
};

struct Q3Brush
{
int brushside;
int n_brushsides;
int texture;
};

struct Q3BrushSides
{
int plane;
int texture;
};

struct Q3Vertex
{
float position[3];
float texcoord[2][2];
float normal[3];
unsigned char color[4];
};

typedef unsigned char Q3Index;

struct Q3Effect
{
char name[64];
int brush;
int unknown;
};

struct Q3Face
{
int texID;
int effect;
int type;
int startVertexIndex;
int numVerts;
int meshVertexIndex;
int numMeshVerts;
int lightmapID;
int lightMapCorner[2];
int lightMapSize[2];
float lightMapPos[3];
float lightMapVectors[2][3];
float normal[3];//
int size[2];
};

struct Q3Lightmap
{
unsigned char imageBits[128][128][3];
};
//typedef WORD Q3Index;

class Q3Map
{
int numVerts; //numVerts will = header.entries[Vertcies].length / sizeof(Q3Vertex);
int numFaces; //numVerts will = header.entries[Faces].length / sizeof(Q3Vertex);
int numIndex; //numVerts will = header.entries[Indices].length / sizeof(Q3Index);
int numTextures; //numVerts will = header.entries[Textures].length / sizeof(Q3Texture);

Q3Index *indices;
Q3Vertex *verts;
Q3Face *faces;
Q3Texture *textures;



void Build();
public:
bool Load(std::string path);

void Render();
void Unload();
};





But about the indice thing, I am not even using indices to render anything and I don't know how I would use it if I were to.

[Edited by - Ainokea on May 25, 2005 2:00:04 AM]

Share this post


Link to post
Share on other sites
I see.

The vertex array is a container of unique points in 3d with no relation to another. The Indices contain the information how those unique points are connected.

Now in order to draw your geometry in immediate mode you would have to build your point container as follows:


Q3Vertex* PointContainer = new Q3Vertex[numIndex];
Q3Vertex* UniqueVertexContainer = new Q3Vertex[numVerts];
...
for(int index=0; index < numIndex; index++)
{
PointContainer[index].position[0] = UniqueVertexContainer[IndexContainer[index]].position[0];
PointContainer[index].position[1] = UniqueVertexContainer[IndexContainer[index]].position[1];
PointContainer[index].position[2] = UniqueVertexContainer[IndexContainer[index]].position[2];
}




...where PointContainer and UniqueVertexContainer are of type Q3Vertex and IndexContainer is of type Q3Index.


By the way.. are you sure Q3Index is of type unsigned char? Because geometry would be limited to 256/3 faces (should be numIndex = 3*numFaces ... when Faces are triangles).
IMO it should be either unsigned short(3ds) or unsigned int.

cheers

Share this post


Link to post
Share on other sites
I have switched to vertex arrays because it is simply to slow to even test if I render everything in intermidiate mode.

Now I am trying to sort everything with indices and I am having trouble.

I assumed I would have similiar code as you showed above (because when I don't use it I get the same messed up polygon soup as I first showed) to sort my verts/polygons and it doesn't work very well, I still get polygon soup, just diffrent.

Anyways here is the code I am using:
Pre-Render

void Q3Map::Build()
{
PointContainer= new Q3Vertex[numIndex];
for(int index=0; index < numIndex; index++)
{
PointContainer[index].position[0] = verts[indices[index]].position[0];
PointContainer[index].position[1] = verts[indices[index]].position[1];
PointContainer[index].position[2] = verts[indices[index]].position[2];
}
}



Render:

void Q3Map::Render()
{
glEnableClientState(GL_VERTEX_ARRAY);

glVertexPointer(3,GL_FLOAT,sizeof(Q3Vertex),PointContainer);
glDrawArrays(GL_TRIANGLE_FAN, 0, numIndex);

glDisableClientState(GL_VERTEX_ARRAY);
}




Oh yeah, you where right about it being short and not char.

Rating++;

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Your problem is that you use GL_TRIANGLE_FAN as primitive type. Don´t do that, render your models as indexed triangle lists using glDrawElements (or glDrawRangeElements).
Download the Q3 tool sources from id software and have a look into it. You´ll find the correct file format description and the correct naming of all lump elements there.
Also, please use the search function in this forum (or google) to find earlier threads with the same topic. There must be dozends of them.

Share this post


Link to post
Share on other sites
Quote:


void Q3Map::Build()
{
PointContainer= new Q3Vertex[numIndex];
for(int index=0; index < numIndex; index++)
{
PointContainer[index].position[0] = verts[indices[index]].position[0];
PointContainer[index].position[1] = verts[indices[index]].position[1];
PointContainer[index].position[2] = verts[indices[index]].position[2];
}
}



This was only for immediate mode because you have to build a container where 3 points make up a triangle, then the next three points make the next triangle and so on...


Triangle1 = (1., 2., 3.);
Triangle2 = (2., 3., 4.);
Triangle3 = (3., 4., 5.);
...

Pointcontainer:
(1,2,3, 2,3,4, 3,4,5, ...)



This is pretty memory intense because you have to store the same vertices more than once and every vertex is made of 3 components.
Indexed Vertex Arrays avoid this overhead because they use indices (1 int) into an array of unique vertices to build the primitives.
Quake maps are stored in this manner.

I would suggest that you use glDrawelements because glDrawArrays can't render indexed vertex arrays, only the method i told you about in my last post.

... and make additional arrays for vertices, normals, color, .. whatever.
GL can handle interleaved arrays but i find it alot easier and cleaner to have separate arrays (when you have to rebuild them for some reason.)

As AP pointed out you have to use GL_TRIANGLES because most 3d formats store their primitives as triangles and only some can handle triangle fans ( i don't know any :) )


void Q3Map::Build()
{
float *Vertices_fp = new float[3*numVerts];
float *Normals_fp = new float[3*numVerts];
float *TexCoords_fp = new float[4*numVerts];
unsigned char *Color_ucp = new unsigned char[4*numVerts];

for(int VertexIndex=0; VertexIndex<numVerts; VertexIndex++)
{
Vertices_fp[(3*VertexIndex )] = verts[VertexIndex].position[0];
Vertices_fp[(3*VertexIndex+1)] = verts[VertexIndex].position[1];
Vertices_fp[(3*VertexIndex+2)] = verts[VertexIndex].position[2];

Normals_fp[(3*VertexIndex )] = verts[VertexIndex].normal[0];
Normals_fp[(3*VertexIndex+1)] = verts[VertexIndex].normal[1];
Normals_fp[(3*VertexIndex+2)] = verts[VertexIndex].normal[2];

TexCoords_fp[(4*VertexIndex )] = verts[VertexIndex].texcoord[0][0];
TexCoords_fp[(4*VertexIndex+1)] = verts[VertexIndex].texcoord[0][1];
TexCoords_fp[(4*VertexIndex+2)] = verts[VertexIndex].texcoord[1][0];
TexCoords_fp[(4*VertexIndex+3)] = verts[VertexIndex].texcoord[1][1];

Color_ucp[(4*VertexIndex )] = verts[VertexIndex].color[0];
Color_ucp[(4*VertexIndex+1)] = verts[VertexIndex].color[1];
Color_ucp[(4*VertexIndex+2)] = verts[VertexIndex].color[2];
Color_ucp[(4*VertexIndex+3)] = verts[VertexIndex].color[3];
}






Rendering Routine:


void Q3Map::Render()
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEX_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

glVertexPointer(3, GL_FLOAT, 0, Vertices_fp);
glNormalPointer(GL_FLOAT, 0, Normals_fp);
glTexCoordPointer(2, GL_FLOAT, 0, TexCoords_fp);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, Color_ucp);

glDrawElements(GL_TRIANGLES, numIndex, GL_UNSIGNED_SHORT, indices);

glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEX_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}




I hope the code snippets don't contain many bugs :)

cya

by the way, .. thx for rating :)

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
You dont need to rebuild anything in a q3 map file.

The bsp face structure has a list of vertices anranged as triangle fans, and a list of indices referencing that vert array as a triangle list.

You can choose which ever method to renderer the actual data you wish. You dont need to rebuild the data in any way, just render it from the loaded data.

Just a couple of things: if your not making sure your data isn't paded you should be and your'll want to loop through every vertice correcting its orientation as q3 maps are stored in left-hand coordinate system (z-up,x-right,y-foward).

Share this post


Link to post
Share on other sites
just a throught, but have you tryed loading in the MeshVerts lump and using that to find the indices of the vertices to render for the face?

rather than looping through all the vertices of the face from faces->startVertexIndex to numVertsfaces->startVertexIndex + numVerts ( as they aren't always stored in the correct order for triangle strips)

sorry, this sounds a bit cryptic but i'm not sitting infront of my code so i can't give an example..

Share this post


Link to post
Share on other sites
Ok I am back.

@hoLogramm: The build function casues the program to crash because 3*VertexIndex+1 (same 3*VertexIndex+2) with will be more then the number of places in Vertices_fp and Normals_fp when VertexIndex=numVerts or is one less (or 2) less then numVerts.

@Anonymous Poster: I tried it without building anything and it didn't work.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I was the AP before, but now have forgotten my password :/.

Like I said you shouldn't need to do anything with the data you get out of the file. (apart from changing the coordinate system)

I've hacked together two rendering functions from an old gametutorials tutorial one for trilist and one for trifans, as Ive previously only done a q3bsp loader on ps2-linux.

[CODE]
void CQuake3BSP::RenderTriList()
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glClientActiveTextureARB(GL_TEXTURE0_ARB);

for(int i=0;i<

Share this post


Link to post
Share on other sites
Guest Anonymous Poster

sorry for that, it should have a CODE button for posting :/
I'll repost...

Like I said you shouldn't need to do anything with the data you get out of the file. (apart from changing the coordinate system)

I've hacked together two rendering functions from an old gametutorials tutorial one for trilist and one for trifans, as Ive previously only done a q3bsp loader on ps2-linux.


void CQuake3BSP::RenderTriList()
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glClientActiveTextureARB(GL_TEXTURE0_ARB);

for(int i=0;i

The code should be fairly obvious.

I would use the trilist one as the trifan one (in its current form) renders the same face mutliply times.

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