diff --git a/draw.h b/draw.h index e48f880..b24972c 100644 --- a/draw.h +++ b/draw.h @@ -5,7 +5,7 @@ #include "display.h" #include -static INLINE void draw_triangle_fan(SVECTOR *verts, u_char numVerts) +static INLINE void draw_trianglefan_lit(SVECTOR *verts, u_char numVerts) { int p; @@ -14,7 +14,7 @@ static INLINE void draw_triangle_fan(SVECTOR *verts, u_char numVerts) // Draw the face as a triangle fan u_char maxVert = numVerts - 1; - for (int vertIdx = 1; vertIdx < maxVert; ++vertIdx) + for (u_char vertIdx = 1; vertIdx < maxVert; ++vertIdx) { const SVECTOR *v0 = &verts[0]; const SVECTOR *v1 = &verts[vertIdx]; @@ -45,7 +45,7 @@ static INLINE void draw_triangle_fan(SVECTOR *verts, u_char numVerts) } } -static INLINE void draw_triangle_strip(SVECTOR *verts, u_char numVerts) +static INLINE void draw_trianglestrip_lit(SVECTOR *verts, u_char numVerts) { int p; @@ -101,11 +101,11 @@ static INLINE void draw_triangle_strip(SVECTOR *verts, u_char numVerts) } } -static INLINE void draw_quad_strip(SVECTOR *verts, u_char numVerts) +static INLINE void draw_quadstrip_lit(SVECTOR *verts, u_char numVerts) { int p; - u_char numQuads = (numVerts - 1) / 2; + u_char numQuads = (numVerts - 1) >> 1; if (!mem_checkprim(sizeof(POLY_G4), numQuads)) return; @@ -159,12 +159,13 @@ static INLINE void draw_quad_strip(SVECTOR *verts, u_char numVerts) } } -static INLINE void draw_quad_strip_tex(STVECTOR *verts, u_char numVerts, u_short tpage) +static INLINE void draw_quadstrip_textured(STVECTOR *verts, u_char numVerts, u_short tpage) { int p; // Normally a quad strip would have (N-2)/2 quads, but we might end up with a sole triangle at the end which will be drawn as a collapsed quad - u_char numQuads = (numVerts - 1) / 2; + // NOTE: testing has shown that the PS1 is faster just rendering quads and accepting the odd collapsed quad, rather than being clever with pointer comparisons and drawing a single triangle at the end. + u_char numQuads = (numVerts - 1) >> 1; if (!mem_checkprim(sizeof(POLY_GT4), numQuads)) return; @@ -223,12 +224,12 @@ static INLINE void draw_quad_strip_tex(STVECTOR *verts, u_char numVerts, u_short } } -static INLINE void draw_quad_strip_water(STVECTOR *verts, u_char numVerts, u_short tpage) +static INLINE void draw_quadstrip_water(STVECTOR *verts, u_char numVerts, u_short tpage) { int p; // Normally a quad strip would have (N-2)/2 quads, but we might end up with a sole triangle at the end which will be drawn as a collapsed quad - u_char numQuads = (numVerts - 1) / 2; + u_char numQuads = (numVerts - 1) >> 1; if (!mem_checkprim(sizeof(POLY_FT4), numQuads)) return; diff --git a/ps1bsp.h b/ps1bsp.h index 6b8676f..bf2ca32 100755 --- a/ps1bsp.h +++ b/ps1bsp.h @@ -53,11 +53,11 @@ typedef struct typedef struct { short x, y, z; - short pad; + short pad; // TODO: Turn into color * light averaged over all adjacent faces, for untextured gouraud shaded drawing at range } ps1bsp_vertex_t; // Texture UV and lighting data for a vertex on a particular polygon. -// TODO: break up into poly vertex (index + uv) and surface vertex (index + light) shared between polygons. +// TODO: break up into poly vertex (index + uv) and surface vertex (index + light) shared between polygons. Will reduce this struct size to 4 bytes and eliminate duplicate light values. typedef struct { unsigned short index; @@ -73,6 +73,7 @@ typedef struct unsigned short numPolyVertices; } ps1bsp_polygon_t; +// TODO: should become obsolete; let faces directly reference ps1bsp_vertex_t typedef struct { unsigned short index; diff --git a/world.c b/world.c index 2770cdf..31c0f20 100644 --- a/world.c +++ b/world.c @@ -69,10 +69,8 @@ static INLINE short world_planeDot(const SVECTOR *dir, const ps1bsp_plane_t *pla return (short)m_dot12s(*dir, plane->normal); } -static void world_drawface(const world_t *world, const ps1bsp_face_t *face) +static INLINE char world_cull_backface(const world_t *world, const ps1bsp_face_t *face, short *dot) { - // TODO: do an early primitive buffer check here, so we can skip backface culling & vertex copying if it's already full - // Backface culling using the face's plane and center point // This eliminates the need for normal clipping per polygon SVECTOR cam_vec; @@ -81,65 +79,79 @@ static void world_drawface(const world_t *world, const ps1bsp_face_t *face) cam_vec.vz = face->center.vz - cam_pos.vz; const ps1bsp_plane_t *plane = &world->planes[face->planeId]; - // NOTE: this value could be REALLY useful for determining the tessellation subdivisions. It has camera distance *and* angle in it. - // Just include the face size/area for an approximate screen size. Maybe also separate x/y/z for angle-dependent tessellation. - short dot = world_planeDot(&cam_vec, plane); - if ((dot >= 0) ^ face->side) + *dot = world_planeDot(&cam_vec, plane); + return ((*dot >= 0) ^ face->side); +} + +static void world_drawface_lit(const world_t *world, const ps1bsp_face_t *face) +{ + // TODO: early primitive buffer check + + short dot; + if (world_cull_backface(world, face, &dot)) return; - if (!enableTexturing) + // Draw untextured, vertex colored polygons + SVECTOR *verts = (SVECTOR*)(scratchpad + 256); + const ps1bsp_polygon_t* poly = &world->polygons[face->firstPolygon]; + for (u_char polyIdx = 0; polyIdx < face->numPolygons; ++polyIdx, ++poly) { - // Draw untextured, vertex colored polygons - SVECTOR *verts = (SVECTOR*)(scratchpad + 256); - const ps1bsp_polygon_t* poly = &world->polygons[face->firstPolygon]; - for (u_char polyIdx = 0; polyIdx < face->numPolygons; ++polyIdx, ++poly) + ps1bsp_polyvertex_t *polyVertex = &world->polyVertices[poly->firstPolyVertex]; + SVECTOR *curVert = verts; + for (u_char vertIdx = 0; vertIdx < poly->numPolyVertices; ++vertIdx, ++polyVertex, ++curVert) { - ps1bsp_polyvertex_t *polyVertex = &world->polyVertices[poly->firstPolyVertex]; - SVECTOR *curVert = verts; - for (u_char vertIdx = 0; vertIdx < poly->numPolyVertices; ++vertIdx, ++polyVertex, ++curVert) - { - const ps1bsp_vertex_t *vert = &world->vertices[polyVertex->index]; - *curVert = *((SVECTOR*)vert); - curVert->pad = polyVertex->light; - } - - if (poly->numPolyVertices == 3) - draw_triangle_fan(verts, 3); - else - draw_quad_strip(verts, poly->numPolyVertices); + const ps1bsp_vertex_t *vert = &world->vertices[polyVertex->index]; + *curVert = *((SVECTOR*)vert); + curVert->pad = polyVertex->light; } + + if (poly->numPolyVertices == 3) + draw_trianglefan_lit(verts, 3); + else + draw_quadstrip_lit(verts, poly->numPolyVertices); } - else +} + +static void world_drawface_textured(const world_t *world, const ps1bsp_face_t *face) +{ + // NOTE: this value could be REALLY useful for determining the tessellation subdivisions. It has camera distance *and* angle in it. + // Just include the face size/area for an approximate screen size. Maybe also separate x/y/z for angle-dependent tessellation. + short dot; + if (world_cull_backface(world, face, &dot)) + return; + + // TODO: do an early primitive buffer check here, so we can skip vertex copying if it's already full + // When doing tessellation, we need the above dot product to decide how many polys to draw, so we can only safely check the primbuffer size here + // Though since we're drawing front-to-back and tessellation will only happen close to the camera, I suppose we could get away with just checking for non-tessellated polycounts + + // Draw textured, vertex colored polygons + STVECTOR *verts = (STVECTOR*)(scratchpad + 256); + ps1bsp_texture_t *faceTexture = &world->textures[face->textureId]; + const ps1bsp_polygon_t* poly = &world->polygons[face->firstPolygon]; + for (u_char polyIdx = 0; polyIdx < face->numPolygons; ++polyIdx, ++poly) { - // Draw textured, vertex colored polygons - STVECTOR *verts = (STVECTOR*)(scratchpad + 256); - ps1bsp_texture_t *faceTexture = &world->textures[face->textureId]; - const ps1bsp_polygon_t* poly = &world->polygons[face->firstPolygon]; - for (u_char polyIdx = 0; polyIdx < face->numPolygons; ++polyIdx, ++poly) + ps1bsp_polyvertex_t *polyVertex = &world->polyVertices[poly->firstPolyVertex]; + STVECTOR *curVert = verts; + for (u_char vertIdx = 0; vertIdx < poly->numPolyVertices; ++vertIdx, ++polyVertex, ++curVert) { - ps1bsp_polyvertex_t *polyVertex = &world->polyVertices[poly->firstPolyVertex]; - STVECTOR *curVert = verts; - for (u_char vertIdx = 0; vertIdx < poly->numPolyVertices; ++vertIdx, ++polyVertex, ++curVert) - { - const ps1bsp_vertex_t *vert = &world->vertices[polyVertex->index]; - *((SVECTOR*)curVert) = *((SVECTOR*)vert); - curVert->u = (u_short)polyVertex->u; - curVert->v = (u_short)polyVertex->v; - curVert->pad = polyVertex->light; - } - - if (face->flags & SURF_DRAWWATER) - draw_quad_strip_water(verts, poly->numPolyVertices, faceTexture->tpage); - else - draw_quad_strip_tex(verts, poly->numPolyVertices, faceTexture->tpage); + const ps1bsp_vertex_t *vert = &world->vertices[polyVertex->index]; + *((SVECTOR*)curVert) = *((SVECTOR*)vert); + curVert->u = (u_short)polyVertex->u; + curVert->v = (u_short)polyVertex->v; + curVert->pad = polyVertex->light; } + + if (face->flags & SURF_DRAWWATER) + draw_quadstrip_water(verts, poly->numPolyVertices, faceTexture->tpage); + else + draw_quadstrip_textured(verts, poly->numPolyVertices, faceTexture->tpage); } } +static void (*world_drawface)(const world_t*, const ps1bsp_face_t*) = &world_drawface_textured; + static void world_drawnode(const world_t *world, short nodeIdx, u_char *pvs) { - u_long frameNum = time_getFrameNumber(); - if (nodeIdx < 0) // Leaf node { // Check if this leaf is visible from the current camera position @@ -147,6 +159,7 @@ static void world_drawnode(const world_t *world, short nodeIdx, u_char *pvs) if ((pvs[test >> 3] & (1 << (test & 0x7))) == 0) return; + u_long frameNum = time_getFrameNumber(); const ps1bsp_leaf_t *leaf = &world->leaves[~nodeIdx]; const u_short *leafFace = &world->leafFaces[leaf->firstLeafFace]; @@ -250,5 +263,10 @@ void world_draw(const world_t *world) u_char *pvs = world_loadVisData(world, cam_leaf, &pvsbuf); //u_char *pvs = world_noVisData(world, &pvsbuf); + if (enableTexturing) + world_drawface = &world_drawface_textured; + else + world_drawface = &world_drawface_lit; + world_drawnode(world, 0, pvs); }