Problem loading Quake 3 maps (*Screenshots*)

Started by
11 comments, last by GameDev.net 18 years, 10 months ago
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.position[1];
	verts.position[1] = verts.position[2];
	verts.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]
______________________________________________________________________________________With the flesh of a cow.
Advertisement
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
When General Patton died after World War 2 he went to the gates of Heaven to talk to St. Peter. The first thing he asked is if there were any Marines in heaven. St. Peter told him no, Marines are too rowdy for heaven. He then asked why Patton wanted to know. Patton told him he was sick of the Marines overshadowing the Army because they did more with less and were all hard-core sons of bitches. St. Peter reassured him there were no Marines so Patton went into Heaven. As he was checking out his new home he rounded a corner and saw someone in Marine Dress Blues. He ran back to St. Peter and yelled "You lied to me! There are Marines in heaven!" St. Peter said "Who him? That's just God. He wishes he were a Marine."
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]
______________________________________________________________________________________With the flesh of a cow.
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
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++;
______________________________________________________________________________________With the flesh of a cow.
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.
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 :)
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).
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..

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.
______________________________________________________________________________________With the flesh of a cow.

This topic is closed to new replies.

Advertisement