• Advertisement
Sign in to follow this  

Quake 3 BSP

This topic is 3868 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I'm writing a Quake 3 BSP loader/renderer, and it's going on pretty well. Right now I'm having a hard time understanding the difference between Polygon faces and Mesh faces. Specifically, how the indices are stored for each, and where. If someone could explain to me how those work I'd really appreciate! Thanks, Jedd [Edited by - bronxbomber92 on July 17, 2007 4:04:42 PM]

Share this post


Link to post
Share on other sites
Advertisement
Well, after researching a little more, it seems that the Mesh faces and polygon faces are the same. But I'm not sure about this.

This is how I was thinking I would render a single polygon face:
     Q3BspFace currentFace = m_pFaces[faceIndex];

glVertexPointer( 3, GL_FLOAT, sizeof(Q3BspVertex), &m_pVertices[currentFace.iVertex].fPosition );

glEnableClientState(GL_VERTEX_ARRAY);
glDrawElements( GL_TRIANGLE_FAN, currentFace.iNumVertices, GL_UNSIGNED_INT, &m_pMeshVerts[currentFace.iVertex] )
Where Q3BspFace is
struct Q3BspFace
{
int iTexture; // Texture index
int iEffect;
int iType; // 1 = polygon, 2 = patch, 3 = mesh, 4 = billboard
int iVertex; // Index of first vertex
int iNumVertices;
int iMeshVert; // Index of first MeshVert
int iNumMeshVerts;
int iLightMap; // Lightmap index
int iLightMapCorner[2]; // Corner of this face's lightmap image in lightmap
int iLightMapSize[2]; // Size of the fac'es lightmap
float fLightMapOrigin[3]; // World space origin of the lightmap
float fLightMapVec[2][3]; // World space s & t unit vectors
float fNormal[3];
int iSize[2]; // Patch dimension
};


I don't know if the Q3BspFace.iVertex is a index into the m_pMeshVerts arrays (http://graphics.stanford.edu/~kekoa/q3/#Meshverts), or in the the actual array of Vertices (http://graphics.stanford.edu/~kekoa/q3/#Vertexes). T

The same questions goes for Mesh faces and the iMeshVert index.

Thanks!

Share this post


Link to post
Share on other sites
If memory serves, one is triangles and one is polygons. It's been a while though.

Share this post


Link to post
Share on other sites
Meshes and polygons use the same method for rendering:

glVertexPointer( 3, GL_FLOAT, sizeof( Q3BspVertex ), &Vertices[ surf->iVertex ].Pos );
glDrawElements( GL_TRIANGLES, surf->iNumIndices, GL_UNSIGNED_INT, Indices + surf->iIndex );

Where Vertices is the contents of lump 10, and Indices is the contents of lump 11.

Where you've got iMeshVert and iNumMeshVerts, I've called iIndex and iNumIndex, as they're references into the index table.

That should work. Like Promit, it's been a while. :-)

Meshes are what models compiled into the map are turned into, whereas Polygons are the brush surfaces. The compiler decomposes both into a list of indexed triangles, so you treat them the same.

Share this post


Link to post
Share on other sites
Thanks a lot. My problem is that now everything crashes or freezes. Here's how I'm drawing it:
void Q3Bsp::DrawMeshFace( int faceIndex )
{
/*glVertexPointer( 3, GL_FLOAT, sizeof(Q3BspVertex), &m_pVertices[m_pFaces[faceIndex].iVertex].fPosition );

glEnableClientState(GL_VERTEX_ARRAY);
glDrawElements( GL_TRIANGLES, m_pFaces[faceIndex].iNumMeshVerts, GL_UNSIGNED_INT, &m_pMeshVerts[m_pFaces[faceIndex].iMeshVert] );*/

printf( "Test\n" );
Q3BspFace *pFace = &m_pFaces[faceIndex];
glVertexPointer(3, GL_FLOAT, sizeof(Q3BspVertex), &(m_pVertices[pFace->iVertex].fPosition));
glEnableClientState(GL_VERTEX_ARRAY);
glDrawElements(GL_TRIANGLES, pFace->iNumMeshVerts, GL_UNSIGNED_INT, m_pMeshVerts + pFace->iMeshVert );
}

void Q3Bsp::Draw( CFrustum frustum, Camera camera )
{
frustum.CalculateFrustum();
int currentLeaf = FindCurrentLeaf( camera.Position() );

for( int i = 0; i < m_iNumLeafs; i++ )
{
if( IsClusterVisible( currentLeaf, m_pLeafs.iCluster ) )
{
if( frustum.BoxInFrustum( (float)m_pLeafs.iAABBMin[0], (float)m_pLeafs.iAABBMin[1], (float)m_pLeafs.iAABBMin[2],
(float)m_pLeafs.iAABBMax[0], (float)m_pLeafs.iAABBMax[1], (float)m_pLeafs.iAABBMax[2] ) )
{
for( int index = m_pLeafs.iLeafFace; index < m_pLeafs.iLeafFace + m_pLeafs.iNumLeafFaces; index++ )
{
DrawMeshFace( m_pLeafFaces[index] );
}
}
}
}
}

The DrawMeshFace() does execute (it does print "Test" repeatedly). But it randomly either crashes, freezes totally (and brings up the debugger), or freeze to point where nothing displays and part of the screen is white while te rest is black, yet the windows and printing statements remain functional.

I don't understand why it isn't drawing. I've gone over my loading function several times and I can't find anything.. Maybe it's worth someone else reading it.

// Add texture loading of textures and lightmaps
void Q3Bsp::LoadBsp( const char* filename )
{
FILE* pFile = fopen( filename, "rb" );

if( !pFile )
{
printf( "error opening BSP map!\n Exit...\n" );
exit(1);
}

fread( &m_BspHeader, 1, sizeof(Q3BspHeader), pFile );

// Allocate memory
m_iNumFaces = m_BspHeader.Lumps[Faces].iLength / sizeof(Q3BspFace);
m_pFaces = new Q3BspFace[m_iNumFaces];

m_iNumVertices = m_BspHeader.Lumps[Vertices].iLength / sizeof(Q3BspVertex);
m_pVertices = new Q3BspVertex[m_iNumFaces];

m_iNumMeshVerts = m_BspHeader.Lumps[MeshVerts].iLength / sizeof(int);
m_pMeshVerts = new int[m_iNumMeshVerts];

m_iNumLeafs = m_BspHeader.Lumps[Leafs].iLength / sizeof(Q3BspLeaf);
m_pLeafs = new Q3BspLeaf[m_iNumLeafs];

m_iNumLeafFaces = m_BspHeader.Lumps[LeafFaces].iLength / sizeof(int);
m_pLeafFaces = new int[m_iNumLeafFaces];

m_iNumLeafBrushes = m_BspHeader.Lumps[LeafBrushes].iLength / sizeof(int);
m_pLeafBrushes = new int[m_iNumLeafBrushes];

m_iNumTexs = m_BspHeader.Lumps[Textures].iLength / sizeof(Q3BspTexture);
m_pTextures = new Q3BspTexture[m_iNumTexs];

m_iNumPlanes = m_BspHeader.Lumps[Planes].iLength / sizeof(Q3BspPlane);
m_pPlanes = new Q3BspPlane[m_iNumPlanes];

m_iNumNodes = m_BspHeader.Lumps[Nodes].iLength / sizeof(Q3BspNode);
m_pNodes = new Q3BspNode[m_iNumNodes];

m_iNumModels = m_BspHeader.Lumps[Models].iLength / sizeof(Q3BspModel);
m_pModels = new Q3BspModel[m_iNumModels];

m_iNumLightMaps = m_BspHeader.Lumps[LightMaps].iLength / sizeof(Q3BspLightMap);
m_pLightMaps = new Q3BspLightMap[m_iNumLightMaps];

m_iNumBrushes = m_BspHeader.Lumps[Brushes].iLength / sizeof(Q3BspBrush);
m_pBrushes = new Q3BspBrush[m_iNumBrushes];

m_iNumBrushSides = m_BspHeader.Lumps[BrushSides].iLength / sizeof(Q3BspBrushSides);
m_pBrushSides = new Q3BspBrushSides[m_iNumBrushSides];

m_iNumEffects = m_BspHeader.Lumps[Effects].iLength / sizeof(Q3BspEffect);
m_pEffects = new Q3BspEffect[m_iNumEffects];

m_pImages = new BDTexture[m_iNumTexs];

// Read in data
fseek( pFile, m_BspHeader.Lumps[VisData].iOffset, SEEK_SET );
//if( m_BspHeader.Lumps[VisData].iLength )
fread( &m_VisData, 1, sizeof( Q3BspVisData ), pFile );

fseek( pFile, m_BspHeader.Lumps[Faces].iOffset, SEEK_SET );
fread( m_pFaces, m_iNumFaces, sizeof( Q3BspFace ), pFile );

fseek( pFile, m_BspHeader.Lumps[Vertices].iOffset, SEEK_SET );
fread( m_pVertices, m_iNumVertices, sizeof( Q3BspVertex ), pFile );

fseek( pFile, m_BspHeader.Lumps[MeshVerts].iOffset, SEEK_SET );
fread( m_pMeshVerts, m_iNumMeshVerts, sizeof( int ), pFile );

fseek( pFile, m_BspHeader.Lumps[Leafs].iOffset, SEEK_SET );
fread( m_pLeafs, m_iNumLeafs, sizeof( Q3BspLeaf ), pFile );

fseek( pFile, m_BspHeader.Lumps[LeafFaces].iOffset, SEEK_SET );
fread( m_pLeafFaces, m_iNumLeafFaces, sizeof( int ), pFile );

fseek( pFile, m_BspHeader.Lumps[LeafBrushes].iOffset, SEEK_SET );
fread( m_pLeafBrushes, m_iNumLeafBrushes, sizeof( int ), pFile );

fseek( pFile, m_BspHeader.Lumps[Textures].iOffset, SEEK_SET );
fread( m_pTextures, m_iNumTexs, sizeof( Q3BspTexture ), pFile );

fseek( pFile, m_BspHeader.Lumps[Planes].iOffset, SEEK_SET );
fread( m_pPlanes, m_iNumPlanes, sizeof( Q3BspPlane ), pFile );

fseek( pFile, m_BspHeader.Lumps[Nodes].iOffset, SEEK_SET );
fread( m_pNodes, m_iNumNodes, sizeof( Q3BspNode ), pFile );

fseek( pFile, m_BspHeader.Lumps[Models].iOffset, SEEK_SET );
fread( m_pModels, m_iNumModels, sizeof( Q3BspModel ), pFile );

fseek( pFile, m_BspHeader.Lumps[LightMaps].iOffset, SEEK_SET );
fread( m_pLightMaps, m_iNumLightMaps, sizeof( Q3BspLightMap ), pFile );

fseek( pFile, m_BspHeader.Lumps[Brushes].iOffset, SEEK_SET );
fread( m_pBrushes, m_iNumBrushes, sizeof( Q3BspBrush ), pFile );

fseek( pFile, m_BspHeader.Lumps[BrushSides].iOffset, SEEK_SET );
fread( m_pBrushSides, m_iNumBrushSides, sizeof( Q3BspBrushSides ), pFile );

fseek( pFile, m_BspHeader.Lumps[Effects].iOffset, SEEK_SET );
fread( m_pEffects, m_iNumEffects, sizeof( Q3BspEffect ), pFile );

// Load each texture
/*for( int i = 0; i < m_iNumTexs; i++ )
{
FILE *test;
char *strTest = m_pTextures.cName
if( (test = fopen( strcat(strTest, ".jpg" ) != NULL) || (test = fopen( strcat(strTest, ".jpeg" ) != NULL) )
m_pImages.Load( (const char*)strTest );
else
{
strTest = {0};
strTest = m_pTextures.cName;
m_pImages.Load( (const char*)strcat(strTest, ".png") );
}
}*/


// Swizzle all vertex positions, vertex normals, plane normals,
// and all bounding box min and max vectors to correct coordinate system
// **MIGHT HAVE TO SWIZZEL TEXTURE COORDS**
for( int i = 0; i < m_iNumPlanes; i++ )
{
//SwizzleVertex( m_pPlanes.fNormal );

float temp = m_pPlanes.fNormal[1];
m_pPlanes.fNormal[1] = m_pPlanes.fNormal[2];
m_pPlanes.fNormal[2] = -temp;
}

for( int i = 0; i < m_iNumNodes; i++ )
{
//SwizzleVertex( m_pNodes.iAABBMin );
//SwizzleVertex( m_pNodes.iAABBMax );

int temp = m_pNodes.iAABBMin[1];
m_pNodes.iAABBMin[1] = m_pNodes.iAABBMin[2];
m_pNodes.iAABBMin[2] = -temp;

temp = m_pNodes.iAABBMax[1];
m_pNodes.iAABBMax[1] = m_pNodes.iAABBMax[2];
m_pNodes.iAABBMax[2] = -temp;
}

for( int i = 0; i < m_iNumLeafs; i++ )
{
//SwizzleVertex( m_pLeafs.iAABBMin );
//SwizzleVertex( m_pLeafs.iAABBMax );

int temp = m_pLeafs.iAABBMin[1];
m_pLeafs.iAABBMin[1] = m_pLeafs.iAABBMin[2];
m_pLeafs.iAABBMin[2] = -temp;

temp = m_pLeafs.iAABBMax[1];
m_pLeafs.iAABBMax[1] = m_pLeafs.iAABBMax[2];
m_pLeafs.iAABBMax[2] = -temp;
}

for( int i = 0; i < m_iNumModels; i++ )
{
//SwizzleVertex( m_pModels.fAABBMin );
//SwizzleVertex( m_pModels.fAABBMax );

float temp = m_pModels.fAABBMin[1];
m_pModels.fAABBMin[1] = m_pModels.fAABBMin[2];
m_pModels.fAABBMin[2] = -temp;

temp = m_pModels.fAABBMax[1];
m_pModels.fAABBMax[1] = m_pModels.fAABBMax[2];
m_pModels.fAABBMax[2] = -temp;
}

for( int i = 0; i < m_iNumVertices; i++ )
{
//SwizzleVertex( m_pVertices.fPosition );
//SwizzleVertex( m_pVertices.fNormal );

float temp = m_pVertices.fPosition[1];
m_pVertices.fPosition[1] = m_pVertices.fPosition[2];
m_pVertices.fPosition[2] = -temp;

temp = m_pVertices.fNormal[1];
m_pVertices.fNormal[1] = m_pVertices.fNormal[2];
m_pVertices.fNormal[2] = -temp;
}

fclose( pFile );
}


Thanks for all the help!

Share this post


Link to post
Share on other sites

// Allocate memory
m_iNumFaces = m_BspHeader.Lumps[Faces].iLength / sizeof(Q3BspFace);
m_pFaces = new Q3BspFace[m_iNumFaces];

m_iNumVertices = m_BspHeader.Lumps[Vertices].iLength / sizeof(Q3BspVertex);
m_pVertices = new Q3BspVertex[m_iNumFaces];


You're using m_iNumFaces to allocate your Q3BspVertex array.

Share this post


Link to post
Share on other sites
Nice catch! I can't believe I didn't see that.
Unfortunately, it still freezes (but doesn't crash anymore). As you can see my the folllowing screenshot, the bottom of the screen is just white.
http://img79.imageshack.us/img79/8971/picture1ng7.png

edit - Nevermind! I figured it out... Just another dumb mistake on my side >.<

Thanks for all your help!

[Edited by - bronxbomber92 on July 14, 2007 11:29:14 AM]

Share this post


Link to post
Share on other sites
I have another question, this time about Billboards. Is there any documents about billboards, or any info you guys know about it? All I've been able to find is that the vertex index is its world position.

Share this post


Link to post
Share on other sites
Quote:
Original post by bronxbomber92
I have another question, this time about Billboards. Is there any documents about billboards, or any info you guys know about it? All I've been able to find is that the vertex index is its world position.



billboards are 2d objects in 3d space that are constantly oriented to face the camera.

If you have a camera matrix you can create the quad off of that.

Get a vector from the center of the billboard quad/triangle to the camera.
normalize it. call it the z vector.
cross the world up vector(0,1,0) with the z vector you just got.
normalize that one and call it the xvector
cross the zvecotr and the xvector and call that result, after normalizing it, the yvector.

The x, y and z vectors you have now are the x,y and z axis local to your billboarded quad. You can build the quad from there, or set it's orientation etc what ever you have to do to render the object with those axis.

particles for instance are generally billboarded. Duke3D had billboarded enemies and objects.

Share this post


Link to post
Share on other sites
Quote:
Original post by bronxbomber92
I have another question, this time about Billboards. Is there any documents about billboards, or any info you guys know about it? All I've been able to find is that the vertex index is its world position.


I haven't dealt with billboards in Q3 maps, so can't help you there. The Q3 source code is easy reading though. You could look up how the engine uses them.

Share this post


Link to post
Share on other sites
Holy crap wall of text incoming...

Polygons refer to faces that a part of the world geometry. Models/Meshes refer to faces that are part of the detail geometry (usually imported MD3s for high-detail objects, like statues, skulls, etc). The only real difference is that world geometry has light maps and detail geometry doesn't.

The reference guide you're using for the file format has some of the lumps named wrong. Below is a bit of an omigod code dump from a very, very old project (I rewrote a bit of it for this post so be sure to check it over). It has:

o The correct names for the lumps
o The 4 face types (only the code for rendering 2 of them)
o Correct loading of the verts and indices
o A very neat trick to cut down on rendering calls
o How to draw a triangle face.

Good luck decyphering it - my coding standards have gotten better over the years, I swear :P

typedef unsigned char uint8;
typedef unsigned int uint32;

const uint32 BSP_ID = ('I') + ('B' << 8) + ('S' << 16) + ('P' << 24);
const uint32 BSP_VERSION = 46;

enum Lumps
{
LUMP_ENTITIES = 0,
LUMP_SHADERS, // aka Materials
LUMP_PLANES,
LUMP_NODES,
LUMP_LEAVES,
LUMP_LEAFFACES,
LUMP_LEAFBRUSHES,
LUMP_MODELS,
LUMP_BRUSHES,
LUMP_BRUSHSIDES,
LUMP_VERTICES, // ALL level vertices
LUMP_INDICES, // ALL level indices
LUMP_EFFECTS, // Special effect shaders such as Fog
LUMP_FACES,
LUMP_LIGHTMAPS,
LUMP_LIGHTVOLUMES,
LUMP_VISDATA,

MAX_LUMPS
};

enum FaceType
{
FACE_INVALID = 0,
FACE_POLYGON,
FACE_PATCH,
FACE_MODEL,
FACE_BILLBOARD
};

struct Face
{
int32 shader_index;
int32 effect_index;

int32 face_type;

int32 vertices_start;
int32 vertex_count;

int32 indices_start;
int32 index_count;

int32 lightmap_index;
Vector2i lightmap_corner;
Vector2i lightmap_size;

Vector3 lightmap_position;
Vector3 lightmap_dir[2];

Vector3 normal;

Vector2i patch_size;
};

typedef uint32 VertIndex;

struct Vertex
{
Vector3 position;
Vector2 texcoord;
Vector2 lightcoord;
Vector3 normal;
uint8 colour[4];
};

void Load ()
{
...during file loading code...

m_index_count = header.lumps[LUMP_INDICES].size / sizeof (VertIndex);
m_indices = new VertIndex[m_index_count];

fseek (file, header.lumps[LUMP_INDICES].offset, SEEK_SET);
fread (m_indices, sizeof (VertIndex), m_index_count, file);

m_vertex_count = header.lumps[LUMP_VERTICES].size / sizeof (Vertex);
m_vertices = new Vertex[m_vertex_count];

fseek (file, header.lumps[LUMP_VERTICES].offset, SEEK_SET);
fread (m_vertices, sizeof (Vertex), m_vertex_count, file);

...after the file is fully loaded...

// This allows us to make a single call to each of the gl*Pointer functions for the entire BSP. We modify the indicies so they
// are in relation to the start of the vertex list as opposed to offsets from face.vertex_index.
for (uint32 face_index = 0; face_index < m_face_count; ++face_index)
{
Face &face = m_faces[face_index];

for (uint32 indices_index = face.indices_start; indices_index < face.indices_start + face.index_count; ++indices_index)
{
m_indices[indices_index] += face.vertex_start;
}
}

...blah blah blah cleanup...
}

void Render ()
{
glNormalPointer (GL_FLOAT, sizeof (Vertex), &m_vertices[0].normal.x);
glTexCoordPointer (2, GL_FLOAT, sizeof (Vertex), &m_vertices[0].texcoord.x);
//glTexCoordPointer (2, GL_FLOAT, sizeof (Vertex), &vertices[0].lightcoord.x); // Do proper multitex setup first
glVertexPointer (3, GL_FLOAT, sizeof (Vertex), &vertices[0].position.x);

...bsp face visibility crap...

switch (face.face_type)
{
case FACE_POLYGON:
case FACE_MODEL:

DrawTriangleFace (face);
break;
}
}

void DrawTriangleFace (Face &face)
{
if (face.face_type == FACE_POLYGON)
{
// Enable lightmaps
}

else
{
// Disable lightmaps
}

glDrawElements (GL_TRIANGLES, face.index_count, GL_UNSIGNED_INT, &m_indices[face.indices_start])
}

Share this post


Link to post
Share on other sites
Thanks a lot guys! I guess it's time to implement collision! Though, I don't think I'll be using the collision info provided with brushes, rather I'll just use my own collision and compare it with the polygons of the current leafs that are visible from the leaf I'm in (ie, can be not in my frustum).

Edit - Or just check for collision with the leaf I'm in... :P

Share this post


Link to post
Share on other sites
Quote:
Original post by MENTALPolygons refer to faces that a part of the world geometry. Models/Meshes refer to faces that are part of the detail geometry (usually imported MD3s for high-detail objects, like statues, skulls, etc). The only real difference is that world geometry has light maps and detail geometry doesn't.


Not entirely accurate. Q3's definition of detail geometry is brushes that are added to the BSP after the structural brushes. Meshes are something else again (and referred to in the Q3 source code as triangle soups). Maps compiled with Q3MAP2, the update of the map compiler built by Splash Damage for Castle Wolfenstein, enables the option of lightmapping imported meshes. Most maps made in the last few years used Q3MAP2.

Course all of that is useless information anyway... :-)

Share this post


Link to post
Share on other sites
Quote:
Original post by bronxbomber92
Thanks a lot guys! I guess it's time to implement collision! Though, I don't think I'll be using the collision info provided with brushes, rather I'll just use my own collision and compare it with the polygons of the current leafs that are visible from the leaf I'm in (ie, can be not in my frustum).


There are some benefits to using the brushes to collide with. They're represented as a set of intersecting infinite planes so your collision checks only need to do object-to-plane calculations. When colliding against polygons then you need to check the edges as well and that adds a whole new load of computations. The collision brushes also are beveled to avoid certain problems with corners.

Share this post


Link to post
Share on other sites
Hmm. It's just that the tutorial on Quake 3 BSP collision is confusing, and I can actually write my own implementation of AABB/Sphere to Polygon collision easily (which I all ready have).

I may be wrong about it though. Is it as simple as AABB-plane collision with each leafbrush's plane?

Share this post


Link to post
Share on other sites
Using Q3's own data structures will be more efficient, and AABB/plane collision is a lot simpler and more robust than AABB/polygon collision, but if you're comfortable with going the AABB/polygon route then go for it. It's the result that counts, right?

I found the collision tutorial quite good myself (I assume we're talking about the one here: http://www.devmaster.net/articles/quake3collision/). He starts with the basics and builds on the code as he adds the more complex stuff.

Share this post


Link to post
Share on other sites
Well, I'm looking at the Q3 BSP code form gametutorials (which I'm following for collision only, since it's a copy of the article at devmaster) and it calls TraceBox() like this:
CVector3 vNewPosition = g_Level.TraceBox(vOldPosition, vCurrentPosition,
CVector3(-20, -50, -20), CVector3(20, 50, 20));

The actual min and max of the AABB never change... This makes no sense to me. Would the min and max of the AABB change each frame? Is the min and max here local the center point (ie, the position of the camera)? Wouldn't it make more sense for the min and max vectors to be in world space?

Share this post


Link to post
Share on other sites
Well, I've just added the AABB collision in the tutorial, and I must say that the collision is shaky at best. It does stop when go straight into a wall, but going backward or into a corner is really screwy.

Share this post


Link to post
Share on other sites
Usually providing the AABB box like that is a convenience for the caller. A lot of models have their centre-of-rotation at their feet as it makes positioning them on surfaces easier. So the bounding box coordinates would be asymmetric unless you force the caller to find the real centre of the bounding box and supply that.

Supplying them in world coordinates is also possible, but again you're forcing the caller to do extra calculations it shouldn't really need to do.

As to your collision shakiness, I don't know. Could be anything. :-) It sounds a bit strange you're having problems moving backwards.

Corner collision is a bit screwy if you don't compensate for it because you're colliding against multiple surfaces simultaneously and it's easy to get into a situation where you're bouncing between them rapidly.

But compensating for collision is a whole new ball-game. There's more code in Q3 to handle moving with collisions than there is code to detect collisions.

Share this post


Link to post
Share on other sites
Well, it's odd because sometimes when I collide my view will look down, causing me not to move forward, or after I collide several times the rate at which I move forward/backward increases dramatically. I think I may just trying my own AABB/plane collision or AABB/polygon collision.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement