diff --git a/hello_fx/Makefile b/hello_fx/Makefile new file mode 100644 index 0000000..bc72a3b --- /dev/null +++ b/hello_fx/Makefile @@ -0,0 +1,8 @@ +TARGET = hello_fx + +SRCS = hello_fx.c \ +TIM/cube.tim \ +TIM/sky.tim \ +TIM/bg.tim \ + +include ../common.mk diff --git a/hello_fx/TIM/bg.tim b/hello_fx/TIM/bg.tim new file mode 100644 index 0000000..90947a6 Binary files /dev/null and b/hello_fx/TIM/bg.tim differ diff --git a/hello_fx/TIM/cube.tim b/hello_fx/TIM/cube.tim new file mode 100644 index 0000000..a92f567 Binary files /dev/null and b/hello_fx/TIM/cube.tim differ diff --git a/hello_fx/TIM/sky.tim b/hello_fx/TIM/sky.tim new file mode 100644 index 0000000..c6dab81 Binary files /dev/null and b/hello_fx/TIM/sky.tim differ diff --git a/hello_fx/cubetex.c b/hello_fx/cubetex.c new file mode 100644 index 0000000..7ecbb90 --- /dev/null +++ b/hello_fx/cubetex.c @@ -0,0 +1,158 @@ +SVECTOR modelCube_mesh[] = { + {48,48,-48.0}, + {48,-48,-48}, + {-48,-48,-48}, + {-48,48,-48}, + {48,48,48}, + {48,-48,48}, + {-48,-48,48}, + {-48,48,48} +}; + +SVECTOR modelCube_normal[] = { + 0,-0,-1,0, + 0,0,1,0, + 1,0,-2,0, + -9,-1,-3,0, + -1,2,-1,0, + 3,1,2,0, + 0,0,-1,0, + 0,-0,1,0, + 1,-6,3,0, + -5,-1,9,0, + -1,2,-1,0, + 2,1,2,0 +}; + +SVECTOR modelCube_uv[] = { + 84,84, 0, 0, + 125,42, 0, 0, + 84,42, 0, 0, + 125,84, 0, 0, + 84,125, 0, 0, + 125,125, 0, 0, + 1,84, 0, 0, + 42,125, 0, 0, + 42,84, 0, 0, + 42,125, 0, 0, + 84,84, 0, 0, + 42,84, 0, 0, + 42,1, 0, 0, + 1,42, 0, 0, + 42,42, 0, 0, + 42,84, 0, 0, + 1,42, 0, 0, + 1,84, 0, 0, + 84,84, 0, 0, + 125,84, 0, 0, + 125,42, 0, 0, + 125,84, 0, 0, + 84,84, 0, 0, + 84,125, 0, 0, + 1,84, 0, 0, + 1,125, 0, 0, + 42,125, 0, 0, + 42,125, 0, 0, + 84,125, 0, 0, + 84,84, 0, 0, + 42,1, 0, 0, + 1,1, 0, 0, + 1,42, 0, 0, + 42,84, 0, 0, + 42,42, 0, 0, + 1,42, 0, 0 +}; + +CVECTOR modelCube_color[] = { + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0, + 128,128,128, 0 +}; + +int modelCube_index[] = { + 0,2,3, + 7,5,4, + 4,1,0, + 5,2,1, + 2,7,3, + 0,7,4, + 0,1,2, + 7,6,5, + 4,5,1, + 5,6,2, + 2,6,7, + 0,3,7 +}; + +TMESH modelCube = { + modelCube_mesh, + modelCube_normal, + modelCube_uv, + modelCube_color, + 12 +}; + +typedef struct RGB_PIX { + u_int R:5, G:5, B:5, STP:1; + } RGB_PIX; + +// Some structures to handle TIM files +typedef struct PIXEL { + u_long bnum; + u_short DX, DY; + u_short W, H; + RGB_PIX data[]; +} PIXEL; + +typedef struct CLUT { + u_long bnum; + u_short DX, DY; + u_short W, H; + u_short clut[]; +} CLUT; + +typedef struct TIM_FILE_CLUT{ + u_long ID; + u_long flag; + u_long clut; + PIXEL pixel[]; +} TIM_FILE_CLUT; + +typedef struct TIM_FILE{ + u_long ID; + u_long flag; + PIXEL pixel[]; +} TIM_FILE; diff --git a/hello_fx/hello_fx.c b/hello_fx/hello_fx.c new file mode 100644 index 0000000..e3a7d6b --- /dev/null +++ b/hello_fx/hello_fx.c @@ -0,0 +1,377 @@ +// Controls : +// SELECT : Switch semi-transparency on/off on primitives +// START : Cycle semi-transparency rates on/off on primitives +// LEFT/RIGHT: Move forward cube +// X : Reset Cube position +// Schnappy 11-2021 +#include +#include +#include +#include +#include +#include +#include +// Sample vector model +#include "cubetex.c" +#define VMODE 0 +// Number of primitives to draw +#define NUM_PRIM 2 +// Number of textures to load +#define NUM_TEX 3 +#define SCREENXRES 320 +#define SCREENYRES 240 +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 +#define MARGINX 16 // margins for text display +#define MARGINY 16 +#define OTLEN 2048 // Maximum number of OT entries +#define PRIMBUFFLEN 32768 // Maximum number of POLY_GT3 primitives +// Display and draw environments, double buffered +DISPENV disp[2]; +DRAWENV draw[2]; +u_long ot[2][OTLEN]; // Ordering table (contains addresses to primitives) +char primbuff[2][PRIMBUFFLEN]; // Primitive list // That's our prim buffer +char * nextpri = primbuff[0]; // Primitive counter +short db = 0; // Current buffer counter +// Store TIM files in an array so we can iterate over them - see 'cubetex.c' for TIM_FILE struct and declaration +TIM_FILE * timFiles[3]; +TIM_IMAGE timImages[3]; +// Get included tim files address +extern TIM_FILE _binary_TIM_cube_tim_start; +extern TIM_FILE _binary_TIM_sky_tim_start; +extern TIM_FILE _binary_TIM_bg_tim_start; +// Light +CVECTOR BGc = {130, 200, 255, 0}; +// Back color +VECTOR BKc = {128, 128, 128, 0}; +// Light rotation angle +SVECTOR lgtang = {0, 0, 0}; +// These will be used to store the light rotation matrix, cube rotation matrix, and composite light matrix. +MATRIX rotlgt, rotcube, light; +// Local Light Matrix : Direction and reach of each light source. +MATRIX lgtmat = { +// X Y Z + 0, -ONE, 0, // Lightsource 1 : here, the light source is at the Bottom-Left of the screen, and points into the screen. + 0, 0, 0, // Lightsource 2 + 0, 0, 0, // Lightsource 3 + }; +// Local Color Matrix +MATRIX cmat = { +// L1 L2 L3 + 4096, 0, 0, // R + 4096, 0, 0, // G + 4096, 0, 0 // B + }; + +// Prototypes +void init(void); +void display(void); +void LoadTexture(TIM_FILE * tim, TIM_IMAGE * tparam); +void init(){ + // Reset the GPU before doing anything and the controller + PadInit(0); + ResetGraph(0); + // Initialize and setup the GTE + InitGeom(); + SetGeomOffset(CENTERX, CENTERY); // x, y offset + SetGeomScreen(CENTERX); // Distance between eye and screen + // Set the display and draw environments + SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); + SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); + SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); + SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES); + if (VMODE) + { + SetVideoMode(MODE_PAL); + disp[0].screen.y += 8; + disp[1].screen.y += 8; + } + SetDispMask(1); + // Set far color + SetFarColor( BGc.r, BGc.g, BGc.b ); + // Set Ambient color + SetBackColor( BKc.vx, BKc.vy, BKc.vz ); + // Set Color matrix + SetColorMatrix(&cmat); + // Set Fog settings + SetFogNearFar( 128, 1024, CENTERX ); + setRGB0(&draw[0], 0, 0, 0); + setRGB0(&draw[1], 0, 0, 0); + draw[0].isbg = 1; + draw[1].isbg = 1; + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + // Init font system + FntLoad(960, 0); + FntOpen(MARGINX, MARGINY, SCREENXRES - MARGINX * 2, SCREENXRES - MARGINY * 2, 0, 512 ); +} +void display(void){ + DrawSync(0); + VSync(0); + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + DrawOTag(&ot[db][OTLEN - 1]); + db = !db; + nextpri = primbuff[db]; + } +void LoadTexture(TIM_FILE * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous + OpenTIM((u_long*)tim); // Open the tim binary data, feed it the address of the data in memory + ReadTIM(tparam); // This read the header of the TIM data and sets the corresponding members of the TIM_IMAGE structure + LoadImage(tparam->prect, tparam->paddr); // Transfer the data from memory to VRAM at position prect.x, prect.y + DrawSync(0); // Wait for the drawing to end + if (tparam->mode & 0x8){ // check 4th bit // If 4th bit == 1, TIM has a CLUT + LoadImage(tparam->crect, tparam->caddr); // Load it to VRAM at position crect.x, crect.y + DrawSync(0); // Wait for drawing to end + } +} +int main() { + // Populate array with pointers to TIM data + timFiles[0] = &_binary_TIM_cube_tim_start; + timFiles[1] = &_binary_TIM_sky_tim_start; + timFiles[2] = &_binary_TIM_bg_tim_start; + // Pad values + int pad, oldPad; + // Set semi-transparency on (1) and off (0) + int stpFlag = 1; + // Set primitive semi-transparency rate - See LibOver47.pdf, p.107 + int stpRate = 0; + // If set, rotate cube + int rotateCube = 1; + int offsetCube = 0; + // Array of pointers to a POLY_G4 we iterate over + POLY_GT3 * poly[NUM_PRIM]; + // Rotation vector + SVECTOR rotVector={ 384, 0, 128, 0 }; + // Translation vector + VECTOR transVector= { 0, 0, 256, 0}; + // BG sprt + POLY_FT4 * bg; + // Normalized UV coordinates for the X axis + long normH = ((255 << 12) / SCREENXRES); + + // Init Disp/Drawenv, Font, etc. + init(); + // Load textures to VRAM + for (char tex = 0; tex < NUM_TEX; tex++){ + LoadTexture(timFiles[tex], &timImages[tex]); + } + + // Main loop + while (1) { + // Work matrix + MATRIX Work= {0} ; + // Triangle counters array - one for each cube + long curTriangle[3] = {0,0,0}; + // Clear the current OT + ClearOTagR(ot[db], OTLEN); + // Draw BG + bg = (POLY_FT4 * )nextpri; + SetPolyFT4(bg); + bg->tpage = getTPage( timImages[2].mode&0x3, 0, + timImages[2].prect->x, + timImages[2].prect->y + ); + if ( (timImages[2].mode & 0x3) < 2 ) { + setClut( bg, + timImages[2].crect->x, + timImages[2].crect->y + ); + } + setRGB0(bg, 127,127,127); + setUV4(bg, 0, 0, + SCREENYRES, 0, + 0, SCREENYRES, + SCREENYRES, SCREENYRES + ); + setXY4(bg, 0 , 0, + SCREENXRES, 0, + 0 , SCREENYRES, + SCREENXRES, SCREENYRES); + addPrim(ot[db][OTLEN-1], bg); + nextpri += sizeof(POLY_FT4); + // Rotate cube + if(rotateCube) rotVector.vy += 10; + // Find and apply light rotation matrix + // Find rotmat from light angles + RotMatrix_gte(&lgtang, &rotlgt); + // Find rotmat from cube angles + RotMatrix_gte(&rotVector, &rotcube); + // RotMatrix cube * RotMatrix light + MulMatrix0(&rotcube, &rotlgt, &rotlgt); + // Light Matrix * RotMatrix light + MulMatrix0(&lgtmat, &rotlgt, &light); + // Set new light matrix + SetLightMatrix(&light); + // Apply Transl, Rot, then matrix + RotMatrix(&rotVector, &Work); + TransMatrix(&Work, &transVector); + SetRotMatrix(&Work); + SetTransMatrix(&Work); + long p, OTz, Flag; + // Draw NUM_PRIM primitives + for (int i = 0; i < (modelCube.len * 3); i += 3) { + // Set projection matrices + transVector.vx = 0; + TransMatrix(&Work, &transVector); + SetRotMatrix(&Work); + SetTransMatrix(&Work); + // Cast nextpri as POLY_GT3 + poly[0] = (POLY_GT3 *)nextpri; + poly[1] = (POLY_GT3 *)nextpri+sizeof(POLY_GT3); + // Initialize the primitives + SetPolyGT3(poly[0]); + SetPolyGT3(poly[1]); + + // Reflection Cube + // This cube has its UVs mapped directly to VRAM coordinates + // We're using the framebuffers as a texture (0,0 and 0,256) + // Get 256x256 texture page that's at x0, y0 + poly[1]->tpage = getTPage( 2, stpRate, + 0, + !(db) << 8 // Here, we're using db's value that can be either 0 or 1 to determine the texture page Y coordinate. + ); + // Set STP + SetSemiTrans(poly[1], stpFlag); + // Map coordinates from drawarea (320x240) to texture size (128x128) in fixed point math + // x = x * (256 / 320) => ( x * ( 128 * 4096 ) / 320 ) / 4096 + // y = y * (240 / 240) => ( y * ( 240 * 4096 ) / 240 ) / 4096 => y * 2184 >> 12 -> y + setUV3( poly[1], + (poly[1]->x0 * normH) >> 12, + poly[1]->y0 - (!(db) << 4) , // We're using db's value again to add a 16 pixels offset to the Y's coordinates of the UVs + (poly[1]->x1 * normH) >> 12, + poly[1]->y1 - (!(db) << 4), // We have to do that because the buffer is 240 high, whereas our texture page is 256, hence 256 - 240 == 16 + (poly[1]->x2 * normH) >> 12, + poly[1]->y2 - (!(db) << 4) + ); + + // Draw "container" cube + // This cube has a texture with transparent areas. + // STP bit is set on PNG's alpha channel : img2tim -usealpha -org 320 0 -o cube.tim cube.png + poly[0]->tpage = getTPage( timImages[0].mode&0x3, stpRate, + timImages[0].prect->x, + timImages[0].prect->y + ); + // If 8/4bpp, load CLUT to vram + if ( (timImages[0].mode & 0x3) < 2 ) { + setClut( poly[0], + timImages[0].crect->x, + timImages[0].crect->y + ); + } + // Set UV coordinates + setUV3(poly[0], modelCube.u[i].vx, modelCube.u[i].vy, + modelCube.u[i+2].vx, modelCube.u[i+2].vy, + modelCube.u[i+1].vx, modelCube.u[i+1].vy + ); + // Rotate, translate, and project the vectors and output the results into a primitive + // curTriangle, +1, +2 point to the vertices index of the triangle we're drawing. + OTz = RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] ] ] , ( long * ) &poly[1]->x0, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 2] ], ( long*) &poly[1]->x1, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 1] ], ( long * ) &poly[1]->x2, &p, &Flag); + + // Here we're only messing with the matrices so that the foreground cube can be moved independantly from the backgound one. + // In real code, you don't want to do the same calculation twice ! + transVector.vx = offsetCube; + TransMatrix(&Work, &transVector); + SetRotMatrix(&Work); + SetTransMatrix(&Work); + + OTz = RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] ] ] , ( long * ) &poly[0]->x0, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 2] ], ( long*) &poly[0]->x1, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 1] ], ( long * ) &poly[0]->x2, &p, &Flag); + + // The right way to do it is re-using the results from the first RotTransPer() batch + // i.e commenting lines 273 to 280 and uncommenting lines 284 to 289 + //~ poly[0]->x0 = poly[1]->x0; + //~ poly[0]->y0 = poly[1]->y0; + //~ poly[0]->x1 = poly[1]->x1; + //~ poly[0]->y1 = poly[1]->y1; + //~ poly[0]->x2 = poly[1]->x2; + //~ poly[0]->y2 = poly[1]->y2; + + // Average OTz value for 3 vertices + // OTz is 1/4 of screen to vertex length + OTz /= 3; + // Work color vectors + // This is the hue of the transparent cube + CVECTOR prismCol = {0xff,0xff,0x0,0x0}; + // This will store the result of the depth cueing. + CVECTOR outCol, outCol1, outCol2 = { 0,0,0,0 }; + // Find local color from three normal vectors and perform depth cueing. + gte_NormalColorDpq3( &modelCube.n[i+0], + &modelCube.n[i+2], + &modelCube.n[i+3], + &prismCol, p, &outCol, &outCol1, &outCol2); + // Set vertex colors on transparent/background cube + setRGB0(poly[1], outCol.r, outCol.g , outCol.b); + setRGB1(poly[1], outCol1.r, outCol1.g, outCol1.b); + setRGB2(poly[1], outCol2.r, outCol2.g, outCol2.b); + // Non-transparent/foreground cube color + // Find local color from three normal vectors and perform depth cueing. + gte_NormalColorDpq( &modelCube.n[i+0], &modelCube.c[i+0], p, &outCol); + gte_NormalColorDpq( &modelCube.n[i+2], &modelCube.c[i+2], p, &outCol2); + gte_NormalColorDpq( &modelCube.n[i+1], &modelCube.c[i+1], p, &outCol1); + // Set vertex colors + setRGB0(poly[0], outCol.r, outCol.g , outCol.b); + setRGB1(poly[0], outCol1.r, outCol1.g, outCol1.b); + setRGB2(poly[0], outCol2.r, outCol2.g, outCol2.b); + // If OTz is in range (not too close) + if ((OTz > 0) && (OTz < OTLEN)) + // Add to ordering table, at index OTz-2 + AddPrim(&ot[ db ][ OTz-2 ], poly[0]); + AddPrim(&ot[ db ][ OTz-2 ], poly[1]); + // Increment next primitive address + nextpri += sizeof(POLY_GT3)*2; + // Increment to next triangle + curTriangle[0] += 3; + curTriangle[1] += 3; + } + // Get pad input + pad = PadRead(0); + // If select button is used + if ( pad & PADselect && !(oldPad & PADselect) ){ + // Flip STP flag + stpFlag = !stpFlag; + // Set flag to avoir misfire + oldPad = pad; + } + // Reset flag when button released + if ( !(pad & PADselect) && oldPad & PADselect) { + oldPad = pad; + } + // If start button is used + if ( pad & PADstart && !( oldPad & PADstart ) ){ + // Switch STP rates + stpRate > 2 ? stpRate = 0 : stpRate++; + // Set flag to avoir misfire + oldPad = pad; + } + // Reset flag when button released + if (!(pad & PADstart) && oldPad & PADstart) { + oldPad = pad; + } + if ( pad & PADRdown && !( oldPad & PADRdown ) ){ + // Switch STP rates + offsetCube = 0; + // Set flag to avoir misfire + oldPad = pad; + } + // Reset flag when button released + if (!(pad & PADRdown) && oldPad & PADRdown) { + oldPad = pad; + } + if ( pad & PADLright && !( oldPad & PADLright ) ){ + offsetCube += 6; + } + if ( pad & PADLleft && !( oldPad & PADLleft ) ){ + offsetCube -= 6; + } + FntPrint("Hello fx !\n"); + FntPrint("Select: STP on/off\nStart: Cycle STP rates\nLeft/Right: Move FG cube.\nX: Reset cube pos\n"); + FntPrint("STP : %d\n", stpFlag); + FntPrint("STP rate : %d\n", stpRate); + FntFlush(-1); + display(); + } + return 0; +}