Browse Source

Overhaul of geometry representation, with correct UV'ing and more finely tesselated surfaces.

unrollquadloop
Nico de Poel 3 years ago
parent
commit
ebbc01c672
  1. BIN
      atlas-n64start.tim
  2. 2
      memory.h
  3. 35
      ps1bsp.h
  4. BIN
      test.ps1bsp
  5. 135
      world.c
  6. 6
      world.h

BIN
atlas-n64start.tim

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

35
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

BIN
test.ps1bsp

135
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);
if (poly->numPolyVertices == 3)
draw_triangle_fan(verts, 3);
else
drawface_quad_strip(face, vecs);
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)
{
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[faceVertex->index];
*((SVECTOR*)curVec) = *((SVECTOR*)vert);
curVec->u = (u_short)faceVertex->u;
curVec->v = (u_short)faceVertex->v;
curVec->pad = faceVertex->light;
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)

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

Loading…
Cancel
Save