diff --git a/world.c b/world.c index 3a83b28..f329c90 100644 --- a/world.c +++ b/world.c @@ -194,6 +194,66 @@ static void world_drawFaces(const world_t *world, const ps1bsp_face_t *firstFace } } +// Simplified BSP subtree traversal specifically for sorting faces of brush models. +// This skips the frustum culling and PVS culling stages, as this will have already been done on the leafs containing the model. +static ps1bsp_face_t *world_sortModelFaces(const world_t* world, const ps1bsp_model_t* model, u_long frameNum, ps1bsp_face_t **lastFace) +{ + short *nodeStack = (short*)(scratchpad + 256); + u_char stackPtr = 0; + + // Push the start node onto the stack + nodeStack[stackPtr++] = model->nodeId0; + + ps1bsp_face_t *firstFace = NULL; + while (stackPtr > 0) + { + // Pop a node off the stack + short nodeIdx = nodeStack[--stackPtr]; + + if (nodeIdx < 0) // Leaf node + { + u_short leafIdx = ~nodeIdx; + if (leafIdx == 0) // Leaf 0 should not be drawn + continue; + + const ps1bsp_leaf_t *leaf = &world->leaves[leafIdx]; + const u_short *leafFaces = &world->leafFaces[leaf->firstLeafFace]; + for (u_short leafFaceIdx = 0; leafFaceIdx < leaf->numLeafFaces; ++leafFaceIdx) + { + ps1bsp_face_t *face = &world->faces[leafFaces[leafFaceIdx]]; + + // Make sure we draw each face only once per frame + if (face->drawFrame == frameNum) + continue; + + face->drawFrame = frameNum; + + // Cull faces that are facing away from the camera + if (!world_cull_backface(world, face)) + continue; + + if (!firstFace) + *lastFace = face; + + face->nextFace = firstFace; + firstFace = face; + } + continue; + } + + const ps1bsp_node_t *node = &world->nodes[nodeIdx]; + const ps1bsp_plane_t *plane = &world->planes[node->planeId]; + short dist = world_pointPlaneDist(&cam_pos, plane); // TODO: add/subtract model origin from camera to sort faces in model space + + // Traverse the subtree in back-to-front order + char order = dist < 0; + nodeStack[stackPtr++] = node->children[order]; + nodeStack[stackPtr++] = node->children[order ^ 1]; + } + + return firstFace; +} + static ps1bsp_face_t *world_sortFaces(const world_t *world, const ps1bsp_leaf_t *firstLeaf) { ps1bsp_face_t *firstFace = NULL; @@ -233,24 +293,17 @@ static ps1bsp_face_t *world_sortFaces(const world_t *world, const ps1bsp_leaf_t firstFace = face; } - // Draw models in this leaf + // Sort faces for the models in this leaf for (const ps1bsp_model_t* model = leaf->models; model != NULL; model = model->nextModel) { - // Hmmm... either just draw all the faces in this model, or do the whole BSP sorting thing - // Guess we could call sortLeafs from here - // If we don't, things like tech doors which are concave might render improperly - ps1bsp_face_t* face = &world->faces[model->firstFace]; - for (u_short faceIdx = 0; faceIdx < model->numFaces; ++faceIdx, ++face) + // This successfully sorts the faces within a single model. + // However, several models occupying the same leaf will still clip through each other + // and since we're looking at only a single leaf per model, large brush models will often have neighbouring leafs drawing over them. + ps1bsp_face_t* lastFace; + ps1bsp_face_t* face = world_sortModelFaces(world, model, frameNum, &lastFace); + if (face != NULL) { - if (face->drawFrame == frameNum) - continue; - - face->drawFrame = frameNum; - - if (!world_cull_backface(world, face)) - continue; - - face->nextFace = firstFace; + lastFace->nextFace = firstFace; firstFace = face; } }