diff --git a/ps1bsp.h b/ps1bsp.h index e385a10..574f551 100755 --- a/ps1bsp.h +++ b/ps1bsp.h @@ -71,9 +71,14 @@ typedef struct typedef struct { + unsigned short planeId; + unsigned short side; + unsigned short firstFaceVertex; unsigned short numFaceVertices; + SVECTOR centerPoint; + u_long drawFrame; // Which frame was this face last drawn on? Used to check if this face should be drawn. } ps1bsp_face_t; diff --git a/qmath.h b/qmath.h index cba16a9..4b2ac13 100644 --- a/qmath.h +++ b/qmath.h @@ -3,9 +3,9 @@ MATRIX *RotMatrixQ(SVECTOR *r, MATRIX *m); -INLINE short m_dot12(const SVECTOR *a, const SVECTOR *b) +INLINE short m_dot12s(const SVECTOR a, const SVECTOR b) { - return ((a->vx * b->vx) >> 12) + ((a->vy * b->vy) >> 12) + ((a->vz * b->vz) >> 12); + return (short)(((int)a.vx * b.vx) >> 12) + (((int)a.vy * b.vy) >> 12) + (((int)a.vz * b.vz) >> 12); } // TODO: worth a benchmark: is it faster to copy these vectors and use them from the stack, or to do six pointer dereferences? diff --git a/test.ps1bsp b/test.ps1bsp index 855c22c..4d79a12 100755 Binary files a/test.ps1bsp and b/test.ps1bsp differ diff --git a/world.c b/world.c index 4490687..d5bf477 100644 --- a/world.c +++ b/world.c @@ -65,12 +65,6 @@ static INLINE void drawface_triangle_fan(const ps1bsp_face_t *face, SVECTOR *vec gte_ldv3(v0, v1, v2); gte_rtpt(); // Rotation, translation, perspective projection - // Normal clipping for backface culling - gte_nclip(); - gte_stopz(&p); - if (p < 0) - continue; - // Average Z for depth sorting and culling gte_avsz3(); gte_stotz(&p); @@ -127,12 +121,6 @@ static INLINE void drawface_triangle_strip(const ps1bsp_face_t *face, SVECTOR *v gte_ldv3(v0, v1, v2); gte_rtpt(); // Rotation, translation, perspective projection - // Normal clipping for backface culling - gte_nclip(); - gte_stopz(&p); - if (p < 0) - continue; - // Average Z for depth sorting and culling gte_avsz3(); gte_stotz(&p); @@ -183,12 +171,6 @@ static INLINE void drawface_quad_strip(const ps1bsp_face_t *face, SVECTOR *vecs) gte_ldv3(v0, v1, v2); gte_rtpt(); // Rotation, translation, perspective projection - // Normal clipping for backface culling (TODO: should be necessary only once per face, using plane normal & camera direction) - gte_nclip(); - gte_stopz(&p); - if (p < 0) - continue; - // Average Z for depth sorting and culling gte_avsz3(); gte_stotz(&p); @@ -221,9 +203,25 @@ static INLINE void drawface_quad_strip(const ps1bsp_face_t *face, SVECTOR *vecs) } } +static INLINE short world_pointPlaneDist(const VECTOR *point, const ps1bsp_plane_t *plane) +{ + // TODO: can be optimized for axis-aligned planes, no need for a dot product there + return m_pointPlaneDist2(*point, plane->normal, plane->dist); +} + static void world_drawface(const world_t *world, const ps1bsp_face_t *face) { - const CVECTOR *col = &colors[(u_long)face % numColors]; + // Backface culling using the face's plane and center point + // This eliminates the need for normal clipping per polygon + SVECTOR cam_vec, center = face->centerPoint; + cam_vec.vx = center.vx - cam_pos.vx; + cam_vec.vy = center.vy - cam_pos.vy; + cam_vec.vz = center.vz - cam_pos.vz; + + const ps1bsp_plane_t *plane = &world->planes[face->planeId]; + short dot = m_dot12s(cam_vec, plane->normal); + if ((dot >= 0) ^ face->side) + return; SVECTOR *vecs = (SVECTOR*)(scratchpad + 256); @@ -273,21 +271,8 @@ static void world_drawnode(const world_t *world, short nodeIdx, u_char *pvs) } const ps1bsp_node_t *node = &world->nodes[nodeIdx]; - - // Still not sure why we have faces attached to nodes... Try to remove this and see what happens - // ps1bsp_face_t *face = &world->faces[node->firstFace]; - // for (u_short faceIdx = 0; faceIdx < node->numFaces; ++faceIdx, ++face) - // { - // // Check if we've already drawn this face on the current frame - // if (face->drawFrame == frameNum) - // continue; - - // world_drawface(world, face); - // face->drawFrame = frameNum; - // } - const ps1bsp_plane_t *plane = &world->planes[node->planeId]; - short dist = m_pointPlaneDist2(cam_pos, plane->normal, plane->dist); + short dist = world_pointPlaneDist(&cam_pos, plane); // Draw child nodes in front-to-back order; adding faces to the OT will reverse the drawing order if (dist > 0) @@ -356,9 +341,7 @@ static u_short world_leafAtPoint(const world_t *world, const VECTOR *point) const ps1bsp_node_t *node = &world->nodes[nodeIdx]; const ps1bsp_plane_t *plane = &world->planes[node->planeId]; - // TODO: can be optimized for axis-aligned planes, no need for a dot product there - short dist = m_pointPlaneDist2(*point, plane->normal, plane->dist); - + short dist = world_pointPlaneDist(point, plane); nodeIdx = dist > 0 ? node->front : node->back; // TODO: this can be done branchless with (dist < 0)^1 }