diff --git a/atlas-n64start.tim b/atlas-n64start.tim index bb0ad5a..f48abac 100755 Binary files a/atlas-n64start.tim and b/atlas-n64start.tim differ diff --git a/memory.h b/memory.h index 04cc457..52e7753 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*2 // TODO: reduce this to what's needed for a typical scene +#define PRIMBUFLEN 65536*4 // 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 b35d933..efa633f 100755 --- a/ps1bsp.h +++ b/ps1bsp.h @@ -30,6 +30,9 @@ typedef struct ps1bsp_dentry_t textures; ps1bsp_dentry_t vertices; + ps1bsp_dentry_t surfVertices; + ps1bsp_dentry_t polygons; + ps1bsp_dentry_t polyVertices; ps1bsp_dentry_t faces; ps1bsp_dentry_t faceVertices; ps1bsp_dentry_t planes; @@ -54,12 +57,25 @@ typedef struct short pad; } ps1bsp_vertex_t; +// Texture UV and lighting data for a vertex on a particular surface. Can be shared between multiple polygons on the same surface. typedef struct { unsigned short index; - unsigned short light; + unsigned short light; // Can be made into u_char if we need to store more data; currently u_short for 32-bit alignment purposes + unsigned short u, v; // Can be made into u_char if we need to store more data; currently u_short for 32-bit alignment purposes +} ps1bsp_surfvertex_t; - unsigned char u, v; // TODO: make into unsigned short, clamp/mask/modulo to u_char at run-time. So we can build tiling polygons later. +// Faces are broken up into one or more polygons, each of which can be drawn as a quad/triangle strip with a single texture. +// This ahead-of-time tesselation is done to deal with the fact that the PS1 can't do texture wrapping, meaning tiling textures have to be broken up into separate polygons. +typedef struct +{ + unsigned short firstPolyVertex; + unsigned short numPolyVertices; +} ps1bsp_polygon_t; + +typedef struct +{ + unsigned short index; // Sampled texture color * light, for untextured gouraud shaded drawing at range unsigned char a : 1; @@ -68,19 +84,30 @@ typedef struct unsigned char b : 5; } ps1bsp_facevertex_t; +// High quality: Face -> polygons -> polygon vertex indices (index + UV + light) -> vertices +// Low quality: Face -> face vertex indices (index + color) -> vertices typedef struct { unsigned short planeId; - unsigned short side; + unsigned char side; + // Used for high-quality tesselated textured drawing + unsigned short firstPolygon; + unsigned char numPolygons; + + // Used for low-quality untextured vertex colored drawing unsigned short firstFaceVertex; unsigned char numFaceVertices; unsigned char textureId; + // Used for backface culling SVECTOR center; - u_long drawFrame; // Which frame was this face last drawn on? Used to check if this face should be drawn. + // Which frame was this face last drawn on? Used to check if this face should be drawn. + u_long drawFrame; + + u_short pad; // For 32-bit alignment } ps1bsp_face_t; typedef struct diff --git a/test.ps1bsp b/test.ps1bsp index 0426cae..9fb6515 100755 Binary files a/test.ps1bsp and b/test.ps1bsp differ diff --git a/world.c b/world.c index 6e3aea9..c76ba2e 100644 --- a/world.c +++ b/world.c @@ -41,6 +41,8 @@ void world_load(const u_long *data, world_t *world) LOAD_CHUNK(ps1bsp_texture_t, world->textures, world->numTextures, bytes, header->textures); LOAD_CHUNK(ps1bsp_vertex_t, world->vertices, world->numVertices, bytes, header->vertices); + LOAD_CHUNK(ps1bsp_surfvertex_t, world->surfVertices, world->numSurfVertices, bytes, header->surfVertices); + LOAD_CHUNK(ps1bsp_polygon_t, world->polygons, world->numPolygons, bytes, header->polygons); LOAD_CHUNK(ps1bsp_face_t, world->faces, world->numFaces, bytes, header->faces); LOAD_CHUNK(ps1bsp_facevertex_t, world->faceVertices, world->numFaceVertices, bytes, header->faceVertices); LOAD_CHUNK(ps1bsp_plane_t, world->planes, world->numPlanes, bytes, header->planes); @@ -50,20 +52,20 @@ void world_load(const u_long *data, world_t *world) LOAD_CHUNK(u_char, world->visData, world->numVisData, bytes, header->visData); } -static INLINE void drawface_triangle_fan(const ps1bsp_face_t *face, SVECTOR *vecs) +static INLINE void draw_triangle_fan(SVECTOR *verts, u_char numVerts) { int p; - if (!mem_checkprim(sizeof(POLY_G3), face->numFaceVertices - 2)) + if (!mem_checkprim(sizeof(POLY_G3), numVerts - 2)) return; // Draw the face as a triangle fan - u_char maxVert = face->numFaceVertices - 1; + u_char maxVert = numVerts - 1; for (int vertIdx = 1; vertIdx < maxVert; ++vertIdx) { - const SVECTOR *v0 = &vecs[0]; - const SVECTOR *v1 = &vecs[vertIdx]; - const SVECTOR *v2 = &vecs[vertIdx + 1]; + const SVECTOR *v0 = &verts[0]; + const SVECTOR *v1 = &verts[vertIdx]; + const SVECTOR *v2 = &verts[vertIdx + 1]; // Naively draw the triangle with GTE, nothing special or optimized about this gte_ldv3(v0, v1, v2); @@ -90,18 +92,18 @@ static INLINE void drawface_triangle_fan(const ps1bsp_face_t *face, SVECTOR *vec } } -static INLINE void drawface_triangle_strip(const ps1bsp_face_t *face, SVECTOR *vecs) +static INLINE void draw_triangle_strip(SVECTOR *verts, u_char numVerts) { int p; - u_char numTris = face->numFaceVertices - 2; + u_char numTris = numVerts - 2; if (!mem_checkprim(sizeof(POLY_G3), numTris)) return; // Draw the face as a triangle strip const SVECTOR *v0, *v1, *v2; - const SVECTOR *head = vecs; - const SVECTOR *tail = vecs + face->numFaceVertices; + const SVECTOR *head = verts; + const SVECTOR *tail = verts + numVerts; u_char reverse = 0; v2 = head++; // Initialize first vertex to index 0 and set head to index 1 @@ -146,18 +148,18 @@ static INLINE void drawface_triangle_strip(const ps1bsp_face_t *face, SVECTOR *v } } -static INLINE void drawface_quad_strip(const ps1bsp_face_t *face, SVECTOR *vecs) +static INLINE void draw_quad_strip(SVECTOR *verts, u_char numVerts) { int p; - u_char numQuads = (face->numFaceVertices - 1) / 2; + u_char numQuads = (numVerts - 1) / 2; if (!mem_checkprim(sizeof(POLY_G4), numQuads)) return; // Draw the face as a quad strip const SVECTOR *v0, *v1, *v2, *v3; - const SVECTOR *head = vecs; - const SVECTOR *tail = vecs + face->numFaceVertices; + const SVECTOR *head = verts; + const SVECTOR *tail = verts + numVerts; // Initialize the first two vertices v2 = --tail; @@ -204,24 +206,24 @@ static INLINE void drawface_quad_strip(const ps1bsp_face_t *face, SVECTOR *vecs) } } -static INLINE void drawface_quad_strip_tex(const ps1bsp_face_t *face, STVECTOR *vecs, u_short tpage) +static INLINE void draw_quad_strip_tex(STVECTOR *verts, u_char numVerts, u_short tpage) { int p; - u_char numQuads = (face->numFaceVertices - 1) / 2; + // 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; if (!mem_checkprim(sizeof(POLY_GT4), numQuads)) return; // Draw the face as a quad strip const STVECTOR *v0, *v1, *v2, *v3; - const STVECTOR *head = vecs; - const STVECTOR *tail = vecs + face->numFaceVertices; + const STVECTOR *head = verts; + const STVECTOR *tail = verts + numVerts; // Initialize the first two vertices v2 = --tail; v3 = head++; - // 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 for (u_char quadIdx = 0; quadIdx < numQuads; ++quadIdx) { v0 = v2; @@ -307,44 +309,89 @@ static void world_drawface(const world_t *world, const ps1bsp_face_t *face) if (!enableTexturing) { // Draw untextured, vertex colored polygons - SVECTOR *vecs = (SVECTOR*)(scratchpad + 256); - - // Copy this face's vertices into scratch RAM for fast reuse - // TODO: this is the main performance bottleneck right now! - ps1bsp_facevertex_t *faceVertex = &world->faceVertices[face->firstFaceVertex]; - SVECTOR *curVec = vecs; - for (int vertIdx = 0; vertIdx < face->numFaceVertices; ++vertIdx, ++faceVertex, ++curVec) + SVECTOR *verts = (SVECTOR*)(scratchpad + 256); + const ps1bsp_polygon_t* poly = &world->polygons[face->firstPolygon]; + for (u_char polyIdx = 0; polyIdx < face->numPolygons; ++polyIdx, ++poly) { - const ps1bsp_vertex_t *vert = &world->vertices[faceVertex->index]; - *curVec = *((SVECTOR*)vert); - curVec->pad = faceVertex->light; - } + ps1bsp_surfvertex_t *surfVertex = &world->surfVertices[poly->firstPolyVertex]; + SVECTOR *curVert = verts; + for (u_char vertIdx = 0; vertIdx < poly->numPolyVertices; ++vertIdx, ++surfVertex, ++curVert) + { + const ps1bsp_vertex_t *vert = &world->vertices[surfVertex->index]; + *curVert = *((SVECTOR*)vert); + curVert->pad = surfVertex->light; + } - if (face->numFaceVertices == 3) // Special case: draw single triangles using the simplest method - drawface_triangle_fan(face, vecs); - else - drawface_quad_strip(face, vecs); + if (poly->numPolyVertices == 3) + draw_triangle_fan(verts, 3); + else + draw_quad_strip(verts, poly->numPolyVertices); + } } else { // Draw textured, vertex colored polygons - STVECTOR *vecs = (STVECTOR*)(scratchpad + 256); - - // Copy this face's vertices into scratch RAM for fast reuse - ps1bsp_facevertex_t *faceVertex = &world->faceVertices[face->firstFaceVertex]; + STVECTOR *verts = (STVECTOR*)(scratchpad + 256); ps1bsp_texture_t *faceTexture = &world->textures[face->textureId]; - STVECTOR *curVec = vecs; - for (int vertIdx = 0; vertIdx < face->numFaceVertices; ++vertIdx, ++faceVertex, ++curVec) + const ps1bsp_polygon_t* poly = &world->polygons[face->firstPolygon]; + for (u_char polyIdx = 0; polyIdx < face->numPolygons; ++polyIdx, ++poly) { - const ps1bsp_vertex_t *vert = &world->vertices[faceVertex->index]; - *((SVECTOR*)curVec) = *((SVECTOR*)vert); - curVec->u = (u_short)faceVertex->u; - curVec->v = (u_short)faceVertex->v; - curVec->pad = faceVertex->light; - } + ps1bsp_surfvertex_t *surfVertex = &world->surfVertices[poly->firstPolyVertex]; + STVECTOR *curVert = verts; + for (u_char vertIdx = 0; vertIdx < poly->numPolyVertices; ++vertIdx, ++surfVertex, ++curVert) + { + const ps1bsp_vertex_t *vert = &world->vertices[surfVertex->index]; + *((SVECTOR*)curVert) = *((SVECTOR*)vert); + curVert->u = (u_short)surfVertex->u; + curVert->v = (u_short)surfVertex->v; + curVert->pad = surfVertex->light; + } - drawface_quad_strip_tex(face, vecs, faceTexture->tpage); + draw_quad_strip_tex(verts, poly->numPolyVertices, faceTexture->tpage); + } } + + // if (!enableTexturing) + // { + // // Draw untextured, vertex colored polygons + // SVECTOR *vecs = (SVECTOR*)(scratchpad + 256); + + // // Copy this face's vertices into scratch RAM for fast reuse + // // TODO: this is the main performance bottleneck right now! + // ps1bsp_facevertex_t *faceVertex = &world->faceVertices[face->firstFaceVertex]; + // SVECTOR *curVec = vecs; + // for (int vertIdx = 0; vertIdx < face->numFaceVertices; ++vertIdx, ++faceVertex, ++curVec) + // { + // const ps1bsp_vertex_t *vert = &world->vertices[faceVertex->index]; + // *curVec = *((SVECTOR*)vert); + // curVec->pad = faceVertex->light; + // } + + // if (face->numFaceVertices == 3) // Special case: draw single triangles using the simplest method + // draw_triangle_fan(vecs, 3); + // else + // draw_quad_strip(vecs, face->numFaceVertices); + // } + // else + // { + // // Draw textured, vertex colored polygons + // STVECTOR *vecs = (STVECTOR*)(scratchpad + 256); + + // // Copy this face's vertices into scratch RAM for fast reuse + // ps1bsp_facevertex_t *faceVertex = &world->faceVertices[face->firstFaceVertex]; + // ps1bsp_texture_t *faceTexture = &world->textures[face->textureId]; + // STVECTOR *curVec = vecs; + // for (int vertIdx = 0; vertIdx < face->numFaceVertices; ++vertIdx, ++faceVertex, ++curVec) + // { + // const ps1bsp_vertex_t *vert = &world->vertices[faceVertex->index]; + // *((SVECTOR*)curVec) = *((SVECTOR*)vert); + // curVec->u = (u_short)faceVertex->u; + // curVec->v = (u_short)faceVertex->v; + // curVec->pad = faceVertex->light; + // } + + // draw_quad_strip_tex(vecs, face->numFaceVertices, faceTexture->tpage); + // } } static void world_drawnode(const world_t *world, short nodeIdx, u_char *pvs) diff --git a/world.h b/world.h index 0f2bf6e..d886009 100644 --- a/world.h +++ b/world.h @@ -11,6 +11,12 @@ typedef struct u_short numVertices; ps1bsp_vertex_t *vertices; + u_short numSurfVertices; + ps1bsp_surfvertex_t *surfVertices; + + u_short numPolygons; + ps1bsp_polygon_t *polygons; + u_short numFaces; ps1bsp_face_t *faces;