diff --git a/CMakeLists.txt b/CMakeLists.txt index ba452a5..ffd4d7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ psn00bsdk_add_executable(template STATIC main.c) psn00bsdk_target_incbin(template PRIVATE tim_player128 player128.tim) psn00bsdk_target_incbin(template PRIVATE tim_player16bpp player128-16.tim) psn00bsdk_target_incbin(template PRIVATE tim_shambler shambler128.tim) +psn00bsdk_target_incbin(template PRIVATE mdl_player player.ps1mdl) psn00bsdk_add_cd_image( iso # Target name diff --git a/main.c b/main.c index f89b179..d3c54af 100644 --- a/main.c +++ b/main.c @@ -21,26 +21,37 @@ #include #include #include +#include -#define OTLEN 8 +#include "ps1mdl.h" + +#define OTLEN 256 + +#define NUMVERTEXNORMALS 162 +static int anorms[NUMVERTEXNORMALS][3] = { +#include "ps1anorms.h" +}; // Define display/draw environments for double buffering DISPENV disp[2]; DRAWENV draw[2]; int db; -u_long ot[2][OTLEN]; // Ordering tables, two arrays for double buffering. These are basically "layers" for drawing. +u_long ot[2][OTLEN]; // Ordering tables, two arrays for double buffering. These are basically the buckets for bucket sorting of polygons. + // You can also see them as "layers" in the modern 3D graphics sense, but they serve a more immediate role for polygon ordering. char primbuff[2][32768]; // Primitive buffer, just a raw buffer of bytes to use as a pool for primitives char *nextpri; extern u_long tim_player128[]; extern u_long tim_player16bpp[]; extern u_long tim_shambler[]; +extern u_long mdl_player[]; TIM_IMAGE playerTex; TIM_IMAGE shamblerTex; +ps1mdl_t model; -void loadTexture(u_long* tim, TIM_IMAGE* tparam) +void loadTexture(const u_long* tim, TIM_IMAGE* tparam) { GetTimInfo(tim, tparam); @@ -56,6 +67,22 @@ void loadTexture(u_long* tim, TIM_IMAGE* tparam) DrawSync(0); } +void loadModel(const u_long* data, ps1mdl_t *mdl) +{ + const char *bytes = (const char*)data; + + mdl->header = (ps1mdl_header_t*)bytes; + bytes += sizeof(ps1mdl_header_t); + + mdl->texCoords = (ps1mdl_texcoord_t*)bytes; + bytes += sizeof(ps1mdl_texcoord_t) * mdl->header->vertexCount; + + mdl->triangles = (ps1mdl_triangle_t*)bytes; + bytes += sizeof(ps1mdl_triangle_t) * mdl->header->triangleCount; + + mdl->vertices = (ps1mdl_vertex_t*)bytes; +} + // Init function void init(void) { @@ -96,6 +123,8 @@ void init(void) loadTexture(tim_player16bpp, &playerTex); loadTexture(tim_shambler, &shamblerTex); + loadModel(mdl_player, &model); + // Set texture page for the entire drawing environment. Nice in some cases perhaps, but not what we need. //draw[0].tpage = getTPage(playerTex.mode & 0x3, 0, playerTex.prect->x, playerTex.prect->y); //draw[1].tpage = getTPage(playerTex.mode & 0x3, 0, playerTex.prect->x, playerTex.prect->y); @@ -149,7 +178,7 @@ void addTile(int x, int y, int w, int h, int r, int g, int b) nextpri += sizeof(TILE); } -void addColoredTriangle(int x0, int y0, int x1, int y1, int x2, int y2, unsigned int rgb0, unsigned int rgb1, unsigned int rgb2) +void addColoredTriangle(int x0, int y0, int x1, int y1, int x2, int y2, unsigned int rgb0, unsigned int rgb1, unsigned int rgb2, unsigned char depth) { POLY_G3 *poly = (POLY_G3*)nextpri; setPolyG3(poly); @@ -158,7 +187,7 @@ void addColoredTriangle(int x0, int y0, int x1, int y1, int x2, int y2, unsigned setRGB1(poly, (rgb1 >> 16) & 0xFF, (rgb1 >> 8) & 0xFF, rgb1 & 0xFF); setRGB2(poly, (rgb2 >> 16) & 0xFF, (rgb2 >> 8) & 0xFF, rgb2 & 0xFF); - addPrim(ot[db], poly); + addPrim(ot[db] + depth, poly); nextpri += sizeof(POLY_G3); } @@ -200,19 +229,80 @@ void addTexturedSprite(int x, int y, int w, int h, TIM_IMAGE *img) nextpri += sizeof(DR_TPAGE); } +static int normToRGB(const int *norm) +{ + int r = ((norm[0] + 4096) >> 5) & 0xFF; + int g = ((norm[1] + 4096) >> 5) & 0xFF; + int b = ((norm[2] + 4096) >> 5) & 0xFF; + return r << 16 | g << 8 | b; +} + +static int dot(const int *a, const int *b) +{ + int c0 = (a[0] * b[0]) >> 12; + int c1 = (a[1] * b[1]) >> 12; + int c2 = (a[2] * b[2]) >> 12; + return c0 + c1 + c2; +} + +static int fakeLight(const int *norm) +{ + const int light[3] = { ONE, 0, 0 }; + int lit = dot(norm, light) >> 4; // Calculate light intensity and normalize to [-256..255] + lit = lit < 0 ? 0 : lit; // Clamp to [0..255] + lit += 32; // Add some ambient light + lit = lit > 255 ? 255 : lit; // Clamp to [0..255] again + return lit << 16 | lit << 8 | lit; +} + void drawStuff(int counter) { ClearOTagR(ot[db], OTLEN); // Print the obligatory hello world and counter to show that the // program isn't locking up to the last created text stream - FntPrint(-1, "HELLO WORLD\n"); FntPrint(-1, "COUNTER=%d, SIN=%d\n", counter, isin(counter)); FntPrint(-1, "Image x %d y %d w %d h %d mode 0x%x\n", playerTex.prect->x, playerTex.prect->y, playerTex.prect->w, playerTex.prect->h, playerTex.mode); + FntPrint(-1, "Model: %d tris, %d verts\n", model.header->triangleCount, model.header->vertexCount); // Draw the last created text stream FntFlush(-1); + int frameNum = (counter >> 2) % model.header->frameCount; + int vertOffs = frameNum * model.header->vertexCount; + + for (int triIdx = 0; triIdx < model.header->triangleCount; ++triIdx) + { + ps1mdl_triangle_t *tri = &model.triangles[triIdx]; + + ps1mdl_vertex_t *v0 = &model.vertices[tri->vertexIndex[0] + vertOffs]; + ps1mdl_vertex_t *v1 = &model.vertices[tri->vertexIndex[1] + vertOffs]; + ps1mdl_vertex_t *v2 = &model.vertices[tri->vertexIndex[2] + vertOffs]; + + // Use normal indices to generate a greyscale value. Good for just giving the polygons some gradients. + unsigned int n0 = v0->normalIndex, n1 = v1->normalIndex, n2 = v2->normalIndex; + unsigned int rgb0 = (n0 << 16) | (n0 << 8) | n0; + unsigned int rgb1 = (n1 << 16) | (n1 << 8) | n1; + unsigned int rgb2 = (n2 << 16) | (n2 << 8) | n2; + + // Visualize normals as RGB + rgb0 = normToRGB(anorms[v0->normalIndex]); + rgb1 = normToRGB(anorms[v1->normalIndex]); + rgb2 = normToRGB(anorms[v2->normalIndex]); + + // Calculate some actual Lambert shading based on a static light vector + rgb0 = fakeLight(anorms[v0->normalIndex]); + rgb1 = fakeLight(anorms[v1->normalIndex]); + rgb2 = fakeLight(anorms[v2->normalIndex]); + + // Normally you'd have GTE do this but we're just going for a quick hack now + unsigned short depth = ((unsigned short)v0->position[1] + (unsigned short)v1->position[1] + (unsigned short)v2->position[1]) / 3; + + addColoredTriangle(v0->position[0], 240 - v0->position[2], v1->position[0], 240 - v1->position[2], v2->position[0], 240 - v2->position[2], rgb0, rgb1, rgb2, (unsigned char)depth); + } + + return; + int r, g, b; lerpcol(255, 255, 0, 255, 0, 0, (icos(counter * 32) + 4096) >> 1, &r, &g, &b); @@ -221,7 +311,7 @@ void drawStuff(int counter) lerpcol(0, 255, 255, 0, 255, 0, (icos(counter * 24 + 512) + 4096) >> 1, &r, &g, &b); addTile(128, 160, 48, 48, r, g, b); - addColoredTriangle(260, 140, 220, 220, 300, 220, 0xFF0000, 0x00FF00, 0x0000FF); + addColoredTriangle(260, 140, 220, 220, 300, 220, 0xFF0000, 0x00FF00, 0x0000FF, 0); addTexturedTriangle(260, 40, 220, 120, 300, 120, 0, 94, 74, 124, 58, 1, &playerTex); addTexturedSprite(20, 140, 80, 80, &playerTex); addTexturedSprite(80, 40, 154, 114, &shamblerTex); diff --git a/player.ps1mdl b/player.ps1mdl new file mode 100644 index 0000000..1feabe9 Binary files /dev/null and b/player.ps1mdl differ diff --git a/ps1anorms.h b/ps1anorms.h new file mode 100644 index 0000000..3eb399c --- /dev/null +++ b/ps1anorms.h @@ -0,0 +1,162 @@ +{-2153, 0, 3484}, +{-1813, 978, 3539}, +{-1209, 0, 3913}, +{-1265, 2048, 3313}, +{-665, 1076, 3895}, +{0, 0, 4096}, +{0, 3484, 2153}, +{-604, 2935, 2792}, +{604, 2935, 2792}, +{0, 2153, 3484}, +{1265, 2048, 3313}, +{2153, 0, 3484}, +{1209, 0, 3913}, +{1813, 978, 3539}, +{665, 1076, 3895}, +{-2792, 604, 2935}, +{-3313, 1265, 2048}, +{-2407, 1742, 2818}, +{-3484, 2153, 0}, +{-3539, 1813, 978}, +{-2935, 2792, 604}, +{-2818, 2407, 1742}, +{-2048, 3313, 1265}, +{-978, 3539, 1813}, +{-1742, 2818, 2407}, +{-2935, 2792, -604}, +{-2048, 3313, -1265}, +{-2153, 3484, 0}, +{0, 3484, -2153}, +{-978, 3539, -1813}, +{0, 3913, -1209}, +{-1076, 3895, -665}, +{0, 4096, 0}, +{0, 3913, 1209}, +{-1076, 3895, 665}, +{978, 3539, 1813}, +{1076, 3895, 665}, +{2048, 3313, 1265}, +{978, 3539, -1813}, +{1076, 3895, -665}, +{2048, 3313, -1265}, +{3484, 2153, 0}, +{2935, 2792, 604}, +{2935, 2792, -604}, +{2153, 3484, 0}, +{1742, 2818, 2407}, +{3539, 1813, 978}, +{2818, 2407, 1742}, +{3313, 1265, 2048}, +{2792, 604, 2935}, +{2407, 1742, 2818}, +{3913, 1209, 0}, +{4096, 0, 0}, +{3895, 665, 1076}, +{3484, -2153, 0}, +{3913, -1209, 0}, +{3539, -1813, 978}, +{3895, -665, 1076}, +{3313, -1265, 2048}, +{2792, -604, 2935}, +{3484, 0, 2153}, +{3539, 1813, -978}, +{3313, 1265, -2048}, +{3895, 665, -1076}, +{2153, 0, -3484}, +{2792, 604, -2935}, +{2792, -604, -2935}, +{3484, 0, -2153}, +{3313, -1265, -2048}, +{3539, -1813, -978}, +{3895, -665, -1076}, +{604, 2935, -2792}, +{1265, 2048, -3313}, +{1742, 2818, -2407}, +{1813, 978, -3539}, +{2407, 1742, -2818}, +{2818, 2407, -1742}, +{-604, 2935, -2792}, +{-1265, 2048, -3313}, +{0, 2153, -3484}, +{-2153, 0, -3484}, +{-1813, 978, -3539}, +{-1209, 0, -3913}, +{-665, 1076, -3895}, +{0, 0, -4096}, +{1209, 0, -3913}, +{665, 1076, -3895}, +{-1813, -978, -3539}, +{-1265, -2048, -3313}, +{-665, -1076, -3895}, +{0, -3484, -2153}, +{-604, -2935, -2792}, +{604, -2935, -2792}, +{0, -2153, -3484}, +{1265, -2048, -3313}, +{1813, -978, -3539}, +{665, -1076, -3895}, +{978, -3539, -1813}, +{2048, -3313, -1265}, +{1742, -2818, -2407}, +{2935, -2792, -604}, +{2818, -2407, -1742}, +{2407, -1742, -2818}, +{0, -3913, -1209}, +{0, -4096, 0}, +{1076, -3895, -665}, +{0, -3484, 2153}, +{0, -3913, 1209}, +{978, -3539, 1813}, +{1076, -3895, 665}, +{2048, -3313, 1265}, +{2935, -2792, 604}, +{2153, -3484, 0}, +{-978, -3539, -1813}, +{-2048, -3313, -1265}, +{-1076, -3895, -665}, +{-3484, -2153, 0}, +{-2935, -2792, -604}, +{-2935, -2792, 604}, +{-2153, -3484, 0}, +{-2048, -3313, 1265}, +{-978, -3539, 1813}, +{-1076, -3895, 665}, +{-3539, -1813, 978}, +{-3313, -1265, 2048}, +{-2818, -2407, 1742}, +{-2792, -604, 2935}, +{-1813, -978, 3539}, +{-2407, -1742, 2818}, +{-1265, -2048, 3313}, +{-604, -2935, 2792}, +{-1742, -2818, 2407}, +{-665, -1076, 3895}, +{1813, -978, 3539}, +{665, -1076, 3895}, +{1265, -2048, 3313}, +{604, -2935, 2792}, +{0, -2153, 3484}, +{1742, -2818, 2407}, +{2407, -1742, 2818}, +{2818, -2407, 1742}, +{-3913, 1209, 0}, +{-3895, 665, 1076}, +{-4096, 0, 0}, +{-3484, 0, 2153}, +{-3913, -1209, 0}, +{-3895, -665, 1076}, +{-3539, 1813, -978}, +{-3895, 665, -1076}, +{-3313, 1265, -2048}, +{-3539, -1813, -978}, +{-3895, -665, -1076}, +{-3313, -1265, -2048}, +{-2792, 604, -2935}, +{-2792, -604, -2935}, +{-3484, 0, -2153}, +{-2818, 2407, -1742}, +{-2407, 1742, -2818}, +{-1742, 2818, -2407}, +{-1742, -2818, -2407}, +{-2407, -1742, -2818}, +{-2818, -2407, -1742}, diff --git a/ps1mdl.h b/ps1mdl.h new file mode 100644 index 0000000..3ceb71e --- /dev/null +++ b/ps1mdl.h @@ -0,0 +1,47 @@ +#ifndef __PS1MDL_H__ +#define __PS1MDL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + int ident; + int version; + + unsigned short vertexCount; + unsigned short triangleCount; + unsigned short frameCount; +} ps1mdl_header_t; + +typedef struct +{ + char onSeam; + short u, v; +} ps1mdl_texcoord_t; + +typedef struct +{ + unsigned short vertexIndex[3]; +} ps1mdl_triangle_t; + +typedef struct +{ + unsigned char position[3]; + unsigned char normalIndex; +} ps1mdl_vertex_t; + +typedef struct +{ + ps1mdl_header_t *header; + ps1mdl_texcoord_t *texCoords; + ps1mdl_triangle_t *triangles; + ps1mdl_vertex_t *vertices; +} ps1mdl_t; + +#ifdef __cplusplus +} +#endif + +#endif // __PS1BSP_H__ diff --git a/shambler128.tim b/shambler128.tim index dc5a572..5a5020f 100644 Binary files a/shambler128.tim and b/shambler128.tim differ