Browse Source

Some reorganization of face drawing code; allow better separation between drawing methods, and require the "enable texturing" check only once ahead of drawing.

unrollquadloop
Nico de Poel 3 years ago
parent
commit
85a8b332c5
  1. 19
      draw.h
  2. 5
      ps1bsp.h
  3. 114
      world.c

19
draw.h

@ -5,7 +5,7 @@
#include "display.h"
#include <inline_c.h>
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;

5
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;

114
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);
}
Loading…
Cancel
Save