I've got pretty much everything to work fine, I've got it rendering with frustum culling / material sorting and even lightmaps. But the PVS (Potentially visible set?) is being a real pain.
From what I understand, a cluster is a group of leaves, and each cluster has visibility information for each other cluster?
So we can take two leaves, get the cluster index A and cluster index B and check if A is visible from B.
I've found a few guides documenting the Q3's BSP format, and here is what I've got so far:
Loading the PVS:
Code: Select all
fseek(file, lumps[LMP_VISDATA].offset, SEEK_SET);
if ( lumps[LMP_VISDATA].size > 0 ) {
VisData = new bsp_visdata_t;
fread(&VisData->numVecs, sizeof(int), 1, file);
fread(&VisData->vecSize, sizeof(int), 1, file);
VisData->bytes = new unsigned char[VisData->numVecs * VisData->vecSize];
fread(VisData->bytes, sizeof(unsigned char) * (VisData->numVecs * VisData->vecSize), 1, file);
}
To get the current leaf the camera is located within, I use this function:
Code: Select all
int Eternal::Scene::BspMap::FindLeaf(Core::Vector3 &v) const
{
int index = 0;
bsp_plane_t *plane;
bsp_node_t *node;
while(index >= 0) {
node = &Nodes[index];
plane = &Planes[node->plane];
const float dist = plane->normal[0] * v.x +
plane->normal[1] * v.y +
plane->normal[2] * v.z - plane->d;
if ( dist >= 0.0f ) {
index = node->front;
}
else {
index = node->back;
}
}
return -index - 1;
}
Also, one more function of relevance:
Code: Select all
int Eternal::Scene::BspMap::ClusterVisible(int a, int b) const
{
// a < 0 (camera out of map)
// VisData = NULL ( No visibility information)
if(VisData == NULL || a < 0) {
return 1;
}
int i = (a * VisData->vecSize) + (b >> 3);
unsigned char visSet = VisData->bytes[i];
return (visSet & (1 << (b & 7))) != 0;
}
This checks if the cluster b is visible from cluster a.
I checked the if statement, VisData is definitely not NULL and a is sometimes -1 but I'm sure that's not supposed to happen.
And I render the map like so:
Code: Select all
// Take a copy of the camera position
Core::Vector3 pos = *Camera->GetPosition();
// Find the current leaf we're in
const int curLeaf = FindLeaf(pos);
for ( unsigned int l = 0;l < i_NumLeaves;l++ ) {
/* This doesn't work- It's only ever said EVERYTHING is visible or NOTHING is visible. */
if ( !ClusterVisible(Leaves[curLeaf].cluster, Leaves[l].cluster ) ) {
continue;
}
/* Checks if the leaf is in the frustum */
if ( !Frustum->BoxInFrustum((float)Leaves[l].min[0], (float)Leaves[l].min[1], (float)Leaves[l].min[2],
(float)Leaves[l].max[0], (float)Leaves[l].max[1], (float)Leaves[l].max[2]) )
{
continue;
}
numFaces = Leaves[l].numleaffaces;
while(numFaces--) {
/* This just adds indices to the faces needed to be drawn */
n = LeafFaces[Leaves[l].leafface + numFaces];
if ( drawnFaces[n] == true ) {
continue;
}
const int c = listIndices[Faces[n].glTexID].count;
listIndices[Faces[n].glTexID].Indices[c] = n;
listIndices[Faces[n].glTexID].count++;
drawnFaces[n] = true;
}
}
Also, I know Quake III uses X/Z/Y coords instead of X/Y/Z coords, this gets handled when the map is loaded.
And it's also worth mentioning, I know Quake III is GPL'd now, but the code is so unclear I can barely make any sense out of it.
Thanks!