From 4d138b41995f5ab33776c4ed729fb498f128a432 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Mon, 30 Jan 2023 14:01:04 +0100 Subject: [PATCH] Added an extra face sorting step so we can draw faces in front-to-back order: - Nodes are traversed front-to-back, leafs are sorted back-to-front - Leaf faces are collected back-to-front to ensure faces shared between leaves are drawn in the correct order - Faces to be drawn are then sorted front-to-back and drawn in that order - This obsoletes the need for any kind of depth calculations, and makes the primitive buffer cutoff work as intended --- memory.h | 2 +- ps1bsp.h | 8 ++++---- test.ps1bsp | Bin 441499 -> 447459 bytes world.c | 40 ++++++++++++++++++++++++---------------- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/memory.h b/memory.h index 52e7753..04cc457 100644 --- a/memory.h +++ b/memory.h @@ -4,7 +4,7 @@ #define SCRATCHLEN 1024 extern u_char* const scratchpad; // Starting address of scratchpad memory, i.e. the 1kB of data cache usable as fast RAM -#define PRIMBUFLEN 65536*4 // TODO: reduce this to what's needed for a typical scene +#define PRIMBUFLEN 65536*2 // TODO: reduce this to what's needed for a typical scene extern char *nextprim; extern char *primbuf_bounds; diff --git a/ps1bsp.h b/ps1bsp.h index e9583c5..d2eb4a0 100755 --- a/ps1bsp.h +++ b/ps1bsp.h @@ -90,7 +90,7 @@ typedef struct // High quality: Face -> polygons -> polygon vertex indices (index + UV + light) -> vertices // Low quality: Face -> face vertex indices (index + color) -> vertices -typedef struct +typedef struct ps1bsp_face_s { unsigned short planeId; unsigned char side; @@ -111,7 +111,8 @@ typedef struct SVECTOR center; // Run-time data - u_long drawFrame; // Which frame was this face last drawn on? Used to check if this face should be drawn. + const struct ps1bsp_face_s* nextFace; // For chaining faces in drawing order + u_long drawFrame; // Which frame was this face last drawn on? Used to check if this face should be drawn. } ps1bsp_face_t; typedef struct @@ -140,8 +141,7 @@ typedef struct ps1bsp_leaf_s u_short numLeafFaces; // Run-time data - struct ps1bsp_leaf_s *nextLeaf; // For chaining leafs in drawing order - u_short leafDepth; // At what depth the leaf's faces should be placed in the ordering table + const struct ps1bsp_leaf_s* nextLeaf; // For chaining leafs in drawing order } ps1bsp_leaf_t; // Pre-parsed and encoded entity data (this runs the risk of becoming too bloated) diff --git a/test.ps1bsp b/test.ps1bsp index 8d808f6354c2558896d01080efd804f2e6746f3a..1ddc31373dadc9dab80257a7477abd09dc30eae2 100755 GIT binary patch delta 13016 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 delta 13045 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 UV#AIDCobHpad = polyVertex->light; } - if (poly->numPolyVertices == 3) - draw_triangle_textured(verts, faceTexture->tpage, ot); - else - draw_quadstrip_textured(verts, poly->numPolyVertices, faceTexture->tpage, ot); + draw_quadstrip_textured(verts, poly->numPolyVertices, faceTexture->tpage, ot); } } } @@ -218,30 +215,40 @@ static void world_drawface_textured(const world_t *world, const ps1bsp_face_t *f static void (*world_drawface)(const world_t*, const ps1bsp_face_t*, u_long *ot) = &world_drawface_fast; static ps1bsp_leaf_t *firstLeaf = NULL; -static u_short leafDepth = 0; +static ps1bsp_face_t *firstFace = NULL; -static void world_drawLeafs(const world_t *world) +static void world_drawFaces(const world_t *world) +{ + // Draw the faces in front-to-back order. There are two advantages to this: + // 1) We don't need to do any depth calculations. The ordering table will be rendered in reverse order, i.e. back-to-front. + // 2) If we need to stop drawing because the primitive buffer is full, we'll only be culling distant faces. + for (const ps1bsp_face_t *face = firstFace; face != NULL; face = face->nextFace) + { + world_drawface(world, face, curOT); + } +} + +static void world_sortFaces(const world_t *world) { u_long frameNum = time_getFrameNumber(); - // Draw each visible leaf's faces in front-to-back order - // This way if we run out of primitive buffer space, we will stop drawing at far-away faces + // Traverse leaves in back-to-front order + // This ensures that faces shared between adjacent leaves are drawn in the correct order, with the back leaf being drawn behind for (const ps1bsp_leaf_t *leaf = firstLeaf; leaf != NULL; leaf = leaf->nextLeaf) { - u_long *ot = curOT + leaf->leafDepth; - 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]]; - // Check if we've already drawn this face on the current frame - // NOTE: this can cause sorting issues when rendering front-to-back with leaf-based depth + // Ensure we draw each face only once per frame if (face->drawFrame == frameNum) continue; - world_drawface(world, face, ot); + // Sort the faces to draw in front-to-back order face->drawFrame = frameNum; + face->nextFace = firstFace; + firstFace = face; } } } @@ -263,7 +270,6 @@ static void world_sortLeafs(const world_t *world, short nodeIdx, u_char *pvs) // Since we're traversing the BSP tree front-to-back, adding each leaf at the start sorts the list in back-to-front order ps1bsp_leaf_t *leaf = (ps1bsp_leaf_t*)&world->leaves[leafIdx]; leaf->nextLeaf = firstLeaf; - leaf->leafDepth = leafDepth++; firstLeaf = leaf; return; } @@ -361,7 +367,9 @@ void world_draw(const world_t *world) world_drawface = &world_drawface_lit; firstLeaf = NULL; - leafDepth = 1; + firstFace = NULL; + world_sortLeafs(world, 0, pvs); - world_drawLeafs(world); + world_sortFaces(world); + world_drawFaces(world); }