From d7b8f5d5a76febd1166c155ae612fde03679cff7 Mon Sep 17 00:00:00 2001 From: ABelliqueux Date: Sat, 10 Jul 2021 15:48:45 +0200 Subject: [PATCH] Revert "Remove old files" This reverts commit 2bd77289c028e7950c17972b22ac1f0002e404e8. --- CPUMAC.H | 68 +++ cube.blend | Bin 0 -> 463032 bytes cube.c | 167 +++++++ cube.gif | Bin 0 -> 144452 bytes cubetex.blend | Bin 0 -> 526812 bytes cubetex.c | 132 ++++++ fun_with_poly.c | 618 ++++++++++++++++++++++++++ hello_2pads.c | 417 +++++++++++++++++ hello_2pads/hello_2pads.c | 417 +++++++++++++++++ hello_cube.c | 178 ++++++++ hello_cube/cube.c | 167 +++++++ hello_cube/hello_cube.c | 178 ++++++++ hello_cubetex.c | 297 +++++++++++++ hello_cubetex/cubetex.c | 132 ++++++ hello_cubetex/hello_cubetex.c | 297 +++++++++++++ hello_gt.jpg | Bin 0 -> 13901 bytes hello_gte_opti.c | 239 ++++++++++ hello_gte_opti/hello_gte_opti.c | 239 ++++++++++ hello_light.c | 275 ++++++++++++ hello_light/hello_light.c | 276 ++++++++++++ hello_multivag.c | 320 +++++++++++++ hello_multivag/hello_multivag.c | 320 +++++++++++++ hello_pad.c | 213 +++++++++ hello_pad/hello_pad.c | 213 +++++++++ hello_poly.c | 197 ++++++++ hello_poly/hello_poly.c | 197 ++++++++ hello_poly_ft.c | 231 ++++++++++ hello_poly_ft/hello_poly_ft.c | 231 ++++++++++ hello_poly_fun/hello_poly_fun.c | 618 ++++++++++++++++++++++++++ hello_poly_gt.c | 215 +++++++++ hello_poly_gt/hello_poly_gt.c | 215 +++++++++ hello_poly_gt_tw.c | 234 ++++++++++ hello_poly_gt_tw/hello_poly_gt_tw.c | 234 ++++++++++ hello_poly_inline.c | 214 +++++++++ hello_poly_inline/hello_poly_inline.c | 214 +++++++++ hello_sio.c | 219 +++++++++ hello_sio/hello_sio.c | 219 +++++++++ hello_sprt.c | 232 ++++++++++ hello_sprt/hello_sprt.c | 232 ++++++++++ hello_tile.c | 150 +++++++ hello_tile/hello_tile.c | 150 +++++++ hello_vag.c | 274 ++++++++++++ hello_vag/hello_vag.c | 275 ++++++++++++ hello_world.c | 86 ++++ hello_world/hello_world.c | 86 ++++ 45 files changed, 9886 insertions(+) create mode 100644 CPUMAC.H create mode 100644 cube.blend create mode 100644 cube.c create mode 100644 cube.gif create mode 100644 cubetex.blend create mode 100644 cubetex.c create mode 100644 fun_with_poly.c create mode 100644 hello_2pads.c create mode 100644 hello_2pads/hello_2pads.c create mode 100644 hello_cube.c create mode 100644 hello_cube/cube.c create mode 100644 hello_cube/hello_cube.c create mode 100644 hello_cubetex.c create mode 100644 hello_cubetex/cubetex.c create mode 100644 hello_cubetex/hello_cubetex.c create mode 100644 hello_gt.jpg create mode 100644 hello_gte_opti.c create mode 100644 hello_gte_opti/hello_gte_opti.c create mode 100644 hello_light.c create mode 100644 hello_light/hello_light.c create mode 100644 hello_multivag.c create mode 100644 hello_multivag/hello_multivag.c create mode 100644 hello_pad.c create mode 100644 hello_pad/hello_pad.c create mode 100644 hello_poly.c create mode 100644 hello_poly/hello_poly.c create mode 100644 hello_poly_ft.c create mode 100644 hello_poly_ft/hello_poly_ft.c create mode 100644 hello_poly_fun/hello_poly_fun.c create mode 100644 hello_poly_gt.c create mode 100644 hello_poly_gt/hello_poly_gt.c create mode 100644 hello_poly_gt_tw.c create mode 100644 hello_poly_gt_tw/hello_poly_gt_tw.c create mode 100644 hello_poly_inline.c create mode 100644 hello_poly_inline/hello_poly_inline.c create mode 100644 hello_sio.c create mode 100644 hello_sio/hello_sio.c create mode 100644 hello_sprt.c create mode 100644 hello_sprt/hello_sprt.c create mode 100644 hello_tile.c create mode 100644 hello_tile/hello_tile.c create mode 100644 hello_vag.c create mode 100644 hello_vag/hello_vag.c create mode 100644 hello_world.c create mode 100644 hello_world/hello_world.c diff --git a/CPUMAC.H b/CPUMAC.H new file mode 100644 index 0000000..1d2f806 --- /dev/null +++ b/CPUMAC.H @@ -0,0 +1,68 @@ +/* +** cpumac.h + mike acton +*/ + +// cpu_ldr(cpu register,data pointer) +// copy 32bit data from dp to r +#define cpu_ldr(r,dp)\ +asm(\ + "lw %0, 0(%1);"\ + : "=r" (r)\ + : "r" (dp)\ +) + +// cpu_gted0(cpu register) +// copy 32bit data from r to gte register 0 +#define cpu_gted0(r)\ +asm(\ + "mtc2 %0, $0;"\ + :\ + : "r" (r)\ +) + +// cpu_gted1(cpu register) +// copy 32bit data from r to gte register 1 +#define cpu_gted1(r)\ +asm(\ + "mtc2 %0, $1;"\ + :\ + : "r" (r)\ +) + +// cpu_gted2(cpu register) +// copy 32bit data from r to gte register 2 +#define cpu_gted2(r)\ +asm(\ + "mtc2 %0, $2;"\ + :\ + : "r" (r)\ +) + +// cpu_gted3(cpu register) +// copy 32bit data from r to gte register 3 +#define cpu_gted3(r)\ +asm(\ + "mtc2 %0, $3;"\ + :\ + : "r" (r)\ +) + +// cpu_gted4(cpu register) +// copy 32bit data from r to gte register 4 +#define cpu_gted4(r)\ +asm(\ + "mtc2 %0, $4;"\ + :\ + : "r" (r)\ +) + +// cpu_gted5(cpu register) +// copy 32bit data from r to gte register 5 +#define cpu_gted5(r)\ +asm(\ + "mtc2 %0, $5;"\ + :\ + : "r" (r)\ +) + diff --git a/cube.blend b/cube.blend new file mode 100644 index 0000000000000000000000000000000000000000..a1bf53f550cfa52f0b4220a4b6d6747684cc3d7c GIT binary patch literal 463032 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 SD&uCs_R^J0mB7;%fBz5Yk+Jpw literal 0 HcmV?d00001 diff --git a/cube.c b/cube.c new file mode 100644 index 0000000..12dd65c --- /dev/null +++ b/cube.c @@ -0,0 +1,167 @@ +SVECTOR modelCube_mesh[] = { + { -128,128,128 }, + { 128,128,128 }, + { 128,128,-128 }, + { -128,128,-128 }, + { -128,-128,128 }, + { 128,-128,128 }, + { 128,-128,-128 }, + { -128,-128,-128 } +}; + +SVECTOR modelCube_normal[] = { + 2365,-2365,-2365, 0, + -2365,-2365,-2365, 0, + -2365,-2365,2365, 0, + 2365,-2365,2365, 0, + 2365,2365,-2365, 0, + -2365,2365,-2365, 0, + -2365,2365,2365, 0, + 2365,2365,2365, 0 +}; + +CVECTOR modelCube_color[] = { + 255,237,0, 0, + 255,235,0, 0, + 255,236,0, 0, + 255,2,0, 0, + 254,3,0, 0, + 255,8,0, 0, + 229,0,255, 0, + 229,0,255, 0, + 229,0,255, 0, + 5,16,250, 0, + 0,12,255, 0, + 0,12,255, 0, + 4,251,25, 0, + 0,255,26, 0, + 0,255,26, 0, + 0,248,255, 0, + 0,248,255, 0, + 0,248,255, 0, + 255,237,0, 0, + 255,237,0, 0, + 255,235,0, 0, + 255,2,0, 0, + 255,6,2, 0, + 254,3,0, 0, + 229,0,255, 0, + 232,21,232, 0, + 229,0,255, 0, + 5,16,250, 0, + 2,13,253, 0, + 0,12,255, 0, + 4,251,25, 0, + 0,255,26, 0, + 0,255,26, 0, + 0,248,255, 0, + 0,248,255, 0, + 0,248,255, 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, + 0, + modelCube_color, + 12 +}; + +SVECTOR modelCube1_mesh[] = { + { -128,128,128 }, + { 128,128,128 }, + { 128,128,-128 }, + { -128,128,-128 }, + { -128,-128,128 }, + { 128,-128,128 }, + { 128,-128,-128 }, + { -128,-128,-128 } +}; + +SVECTOR modelCube1_normal[] = { + 2365,-2365,-2365, 0, + -2365,-2365,-2365, 0, + -2365,-2365,2365, 0, + 2365,-2365,2365, 0, + 2365,2365,-2365, 0, + -2365,2365,-2365, 0, + -2365,2365,2365, 0, + 2365,2365,2365, 0 +}; + +CVECTOR modelCube1_color[] = { + 255,237,0, 0, + 255,235,0, 0, + 255,236,0, 0, + 255,2,0, 0, + 254,3,0, 0, + 255,8,0, 0, + 229,0,255, 0, + 229,0,255, 0, + 229,0,255, 0, + 5,16,250, 0, + 0,12,255, 0, + 0,12,255, 0, + 4,251,25, 0, + 0,255,26, 0, + 0,255,26, 0, + 0,248,255, 0, + 0,248,255, 0, + 0,248,255, 0, + 255,237,0, 0, + 255,237,0, 0, + 255,235,0, 0, + 255,2,0, 0, + 255,6,2, 0, + 254,3,0, 0, + 229,0,255, 0, + 232,21,232, 0, + 229,0,255, 0, + 5,16,250, 0, + 2,13,253, 0, + 0,12,255, 0, + 4,251,25, 0, + 0,255,26, 0, + 0,255,26, 0, + 0,248,255, 0, + 0,248,255, 0, + 0,248,255, 0 +}; + +int modelCube1_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 modelCube1 = { + modelCube1_mesh, + modelCube1_normal, + 0, + modelCube1_color, + 12 +}; diff --git a/cube.gif b/cube.gif new file mode 100644 index 0000000000000000000000000000000000000000..5e8dbc06cc53dff5ae80ee8dec44f78415e55b33 GIT binary patch literal 144452 zcmd42XH=7YyX|`?1PBly^bUc9A~iJWl2AhxlqjOo1;nT*2uL$Ps0IjCKtMVe5h;-- zD!oY;B_c>w5F1Jr(QKaQUGG}&+T*M<&e&(~J=VyVjI=u+k~y#Wo7dmc%0gGq!xgjv z>`JKv0DuAj8~_jifC2zC0H6cF!QYRfPyh}E5KsUG1<+6c9R(cx{ZbSTz~KM_4xr!w z8V;c2fP=r^k0Jm#0ze=D6aqja0CWOy@b`zJC;*NE5GVkJ0?;S`odO*EeI*nPz|jB# z4WQ5f8V#V+fP=p;kD>!OIzXTU6goho19UoY@b`B@9RRokfN%g%4glH#KtBKu{`#VL z6oAJ8cmjZ@0C*aJrvvx{01u$>C=?!t!V^$<3JOm{;pr&+0SXV`@F*M}hr<(acnS_r z!{O;T`~eOR5b!7h9!J0v2zUwsPb1*z1pEO34^Z$Z3LZzn6DW8J1y7^k=@k3{1rN~h zC>kC|!xLzD3Jp)A;psH|0Syn(@hCbTN5>QBcnTd)qvPpx`~e*g9NH&@l5U3~u6-S^F2viDzN+VF|1nL2S3Q(vh3Kd785-3y( zg-WAP=@jY#g$mH9C>j+H(b! z98ggQRNMiTa6qLTP-zEL`T_OefC>N%6u`g%3&SVh~Ua z3W`BPG3Y490g3_O7$_VAhhq?M3<{1x!!hVM#sQ825Ev)|14m#G2n-5=K_f8e1jYe@ z0ZjGtV-RQz3XMUdG3YeL0gVCB87MjfM`sY| z3<{k=qciAq#sQrH957G^4BP>OaKNA(FlYx1`T^tMfFaG`us3%)X^M3)*HA?>fewFA z(mx>(2p|P0AKnN4`kMey1^|MJ<0uHUJCRGqDR;xYJB~}lnvCNq?M)Dtcb&?u_v}xR z)Juk0@sb$EPDC#uOST%MJ6U#1PO{<;f;USPdl@hT79==!h~uk^as{dw|x{ntuw8hv!+BCH;(j8+FM4;9)p*Umi&Eo+-I zfM0fget}UbV5?i_(UHh1b*exY>Gtd<(x%9^wc-5>GpTPXv!Uv~eZ|JNcWm1lKaO9} ze$qSK_W09uo!f&e;bB2vCabT08Xb8Xe04U}_jrn3d&~Fbfduw@>reDHR-Y7FCj1H4 zcPZk&^T~Gf>66`WHy^JuXs%D&gEpte9vmM#R=(G~u>9iQapSdLcRvlTT0VMi#9;io zal;L}E#S8hJ;2#=^7M|@)B^U;-e#K7j>1;4xzI5P$%yZn*`o0uCuj+YU*Bⅅ4jz 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 Vw$P0{ngZ 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
  • #}z1#@_LJ>k! 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 f0A}F4|AXit{kE(0S7qMJ<|tOKXkk44 +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SPIN 16 // Rotation speed increment + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 10 // margins for text display +#define MARGINY 4 + +#define FONTSIZE 8 * 7 // Text Field Height + +#define OTLEN 16 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +CVECTOR BgColor[3] = {20, 20, 20}; + +struct polygon + { + POLY_F4 * poly_f4; + CVECTOR color; + short width; + short height; + //~ VECTOR PosV_L; // Not used anymore + SVECTOR RotV_L; + VECTOR TransV_L; + VECTOR ScaleV_L; + SVECTOR PivotV_L; + SVECTOR Verts[4]; + MATRIX Matrix; + long depth; + long flag; + short rotSpeed; + int otz; + }; + +void init(void) +{ + ResetGraph(0); + + // Initialize and setup the GTE : Not needed ? + + InitGeom(); + SetGeomOffset(CENTERX,CENTERY); + SetGeomScreen(CENTERX); + + PadInit(0); + + 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; + } + + setRGB0(&draw[0], BgColor->r, BgColor->g, BgColor->b); + setRGB0(&draw[1], BgColor->r, BgColor->g, BgColor->b); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +void pivotPoint(SVECTOR VertPos[3],short width,short height, SVECTOR pivot){ + + // Not very efficient I think + + VertPos[0].vx = -pivot.vx; + VertPos[0].vy = -pivot.vy; + VertPos[0].vz = 1; + + VertPos[1].vx = width - pivot.vx; + VertPos[1].vy = -pivot.vy; + VertPos[1].vz = 1; + + VertPos[2].vx = -pivot.vx; + VertPos[2].vy = height-pivot.vy; + VertPos[2].vz = 1; + + VertPos[3].vx = width - pivot.vx; + VertPos[3].vy = height - pivot.vy; + VertPos[3].vz = 1; +} + + +MATRIX identity(int num) +{ + int row, col; + MATRIX matrix; + + for (row = 0; row < num; row++) + { + for (col = 0; col < num; col++) + { + if (row == col) + matrix.m[row][col] = 4096; + else + matrix.m[row][col] = 0; + } + } + return matrix; +} + +int main(void) +{ + + MATRIX IDMATRIX = identity(3); + + u_short BtnTimer = 0; // Timer to limit pad input rate + u_short polyCount = 1; // current polygon index + + int otz; // z-index + + struct polygon *CurrentPoly; // points to the address of selected polygon + + + // White cursor : shows which polygon is selected + + struct polygon cursorS = { + cursorS.poly_f4, + {255, 255, 255}, // color + 30, 30, // width, height + {0,0,0}, // RotV_L + {0,0,0, 0}, // TransV_L + {4096,4096,4096}, // ScaleV_L + {1,1,1}, // PivotV + { // Verts[4] + {-1, -1, 1}, + { 1, -1, 1}, + {-1, 1, 1}, + { 1, 1, 1} + }, + IDMATRIX // Matrix + }; + + //Red + + struct polygon polyS = { + polyS.poly_f4, + {255, 0, 0}, // color + 30, 30, // width, height + {0,0,0}, // RotV_L + {-48, -30, 0, 0}, // TransV_L + {4096,4096,4096}, // ScaleV_L + {15,15,1}, // PivotV + { // Verts[4] + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0} + }, + IDMATRIX, // Matrix + 0,0, // depth, flag + 8, // rotSpeed + 1 // z-index + }; + + + //Yellow + + struct polygon poly1S = { + poly1S.poly_f4, + {255, 187, 0}, // color + 28, 28, // width, height + {0,0,0}, // RotV_L + {-20, 10, 0, 0}, // TransV_L + {4096,4096,4096}, // ScaleV_L + {4,4,1}, // PivotV + { // Verts[4] + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0} + }, + IDMATRIX, // Matrix + 0,0, // depth, flag + -12, // rotSpeed + 2 // z-index + }; + + + //Green + + struct polygon poly2S = { + poly2S.poly_f4, + {0, 255, 153}, // color + 24, 24, // width, height + {0,0,0}, // RotV_L + {36, -10, 0, 0}, // TransV_L + {4096,4096,4096}, // ScaleV_L + {12,12,1}, // PivotV + { // Verts[4] + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0} + }, + IDMATRIX, // Matrix + 0,0, // depth, flag + -6, // rotSpeed + 3 // z-index + }; + + + //Blue + + struct polygon poly3S = { + poly3S.poly_f4, + {112, 254, 254}, // color + 26, 26, // width, height + {0,0,0}, // RotV_L + {20, 20, 0, 0}, // TransV_L + {4096,4096,4096}, // ScaleV_L + {13,13,1}, // PivotV + { // Verts[4] + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0} + }, + IDMATRIX, // Matrix + 0,0, //depth, flag + 256, //rotSpeed + 4 // z-index + }; + + + ///// + + CurrentPoly = &polyS; + + pivotPoint(polyS.Verts, polyS.width, polyS.height, polyS.PivotV_L); + pivotPoint(poly1S.Verts, poly1S.width, poly1S.height, poly1S.PivotV_L); + pivotPoint(poly2S.Verts, poly2S.width, poly2S.height, poly2S.PivotV_L); + pivotPoint(poly3S.Verts, poly3S.width, poly3S.height, poly3S.PivotV_L); + + init(); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + cursorS.poly_f4 = (POLY_F4 *)nextpri; + + RotMatrix(&cursorS.RotV_L , &cursorS.Matrix); + TransMatrix(&cursorS.Matrix, &CurrentPoly->TransV_L); + + SetRotMatrix(&cursorS.Matrix); + SetTransMatrix(&cursorS.Matrix); + + setPolyF4(cursorS.poly_f4); + setRGB0(cursorS.poly_f4,cursorS.color.r,cursorS.color.g,cursorS.color.b); + //~ setXY4(cursorS, MovVector.vx-1, MovVector.vy-1 ,MovVector.vx + 1, MovVector.vy -1,MovVector.vx-1, MovVector.vy+1,MovVector.vx+1, MovVector.vy+1); + + RotTransPers4( + &cursorS.Verts[0], &cursorS.Verts[1], &cursorS.Verts[2], &cursorS.Verts[3], + (long*)&cursorS.poly_f4->x0, (long*)&cursorS.poly_f4->x1, (long*)&cursorS.poly_f4->x2, (long*)&cursorS.poly_f4->x3, + &cursorS.depth, + &cursorS.flag + ); + + addPrim(ot[db], cursorS.poly_f4); + + nextpri += sizeof(POLY_F4); + + ///// Red + + polyS.poly_f4 = (POLY_F4 *)nextpri; + + polyS.RotV_L.vz += polyS.rotSpeed; + + RotMatrix(&polyS.RotV_L, &polyS.Matrix); + TransMatrix(&polyS.Matrix, &polyS.TransV_L); + ScaleMatrix(&polyS.Matrix, &polyS.ScaleV_L); + + SetRotMatrix(&polyS.Matrix); + SetTransMatrix(&polyS.Matrix); + + setPolyF4(polyS.poly_f4); + setRGB0(polyS.poly_f4, polyS.color.r,polyS.color.g,polyS.color.b); + RotTransPers4( + &polyS.Verts[0], &polyS.Verts[1], &polyS.Verts[2], &polyS.Verts[3], + (long*)&polyS.poly_f4->x0, (long*)&polyS.poly_f4->x1, (long*)&polyS.poly_f4->x2, (long*)&polyS.poly_f4->x3, + &polyS.depth, + &polyS.flag + ); + + addPrim(ot[db]+polyS.otz, polyS.poly_f4); + + nextpri += sizeof(POLY_F4); + + ///// Yellow + + poly1S.poly_f4 = (POLY_F4 *)nextpri; + + poly1S.RotV_L.vz += poly1S.rotSpeed; + + RotMatrix(&poly1S.RotV_L, &poly1S.Matrix); + TransMatrix(&poly1S.Matrix, &poly1S.TransV_L); + ScaleMatrix(&poly1S.Matrix, &poly1S.ScaleV_L); + + + SetRotMatrix(&poly1S.Matrix); + SetTransMatrix(&poly1S.Matrix); + + setPolyF4(poly1S.poly_f4); + setRGB0(poly1S.poly_f4, poly1S.color.r,poly1S.color.g,poly1S.color.b); + RotTransPers4( + &poly1S.Verts[0], &poly1S.Verts[1], &poly1S.Verts[2], &poly1S.Verts[3], + (long*)&poly1S.poly_f4->x0, (long*)&poly1S.poly_f4->x1, (long*)&poly1S.poly_f4->x2, (long*)&poly1S.poly_f4->x3, + &poly1S.depth, + &poly1S.flag + ); + + addPrim(ot[db]+poly1S.otz, poly1S.poly_f4); + + nextpri += sizeof(POLY_F4); + + + ///// Green + + poly2S.poly_f4 = (POLY_F4 *)nextpri; + + poly2S.RotV_L.vz += poly2S.rotSpeed; + + RotMatrix(&poly2S.RotV_L, &poly2S.Matrix); + TransMatrix(&poly2S.Matrix, &poly2S.TransV_L); + ScaleMatrix(&poly2S.Matrix, &poly2S.ScaleV_L); + + + SetRotMatrix(&poly2S.Matrix); + SetTransMatrix(&poly2S.Matrix); + + setPolyF4(poly2S.poly_f4); + setRGB0(poly2S.poly_f4, poly2S.color.r,poly2S.color.g,poly2S.color.b); + RotTransPers4( + &poly2S.Verts[0], &poly2S.Verts[1], &poly2S.Verts[2], &poly2S.Verts[3], + (long*)&poly2S.poly_f4->x0, (long*)&poly2S.poly_f4->x1, (long*)&poly2S.poly_f4->x2, (long*)&poly2S.poly_f4->x3, + &poly2S.depth, + &poly2S.flag + ); + + addPrim(ot[db]+poly2S.otz, poly2S.poly_f4); + + nextpri += sizeof(POLY_F4); + + ///// Blue + + poly3S.poly_f4 = (POLY_F4 *)nextpri; + + poly3S.RotV_L.vz += poly3S.rotSpeed; + + RotMatrix(&poly3S.RotV_L, &poly3S.Matrix); + TransMatrix(&poly3S.Matrix, &poly3S.TransV_L); + ScaleMatrix(&poly3S.Matrix, &poly3S.ScaleV_L); + + SetRotMatrix(&poly3S.Matrix); + SetTransMatrix(&poly3S.Matrix); + + setPolyF4(poly3S.poly_f4); + setRGB0(poly3S.poly_f4, poly3S.color.r,poly3S.color.g,poly3S.color.b); + RotTransPers4( + &poly3S.Verts[0], &poly3S.Verts[1], &poly3S.Verts[2], &poly3S.Verts[3], + (long*)&poly3S.poly_f4->x0, (long*)&poly3S.poly_f4->x1, (long*)&poly3S.poly_f4->x2, (long*)&poly3S.poly_f4->x3, + &poly3S.depth, + &poly3S.flag + ); + + addPrim(ot[db]+poly3S.otz, poly3S.poly_f4); + + nextpri += sizeof(POLY_F4); + + + // Pad stuff + + + int pad = PadRead(0); // init pad + + // Right D-pad + + if(pad & PADRup){ + if (CurrentPoly->PivotV_L.vy >= 0){ + CurrentPoly->PivotV_L.vy -= 1; + pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L); + } + else { + CurrentPoly->PivotV_L.vy = CurrentPoly->PivotV_L.vy; + } + }; + + if(pad & PADRdown){ + if (CurrentPoly->PivotV_L.vy <= CurrentPoly->height ){ + CurrentPoly->PivotV_L.vy += 1; + pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L); + } + else { + CurrentPoly->PivotV_L.vy = CurrentPoly->PivotV_L.vy; + } + }; + + if(pad & PADRleft){ + if (CurrentPoly->PivotV_L.vx >= 0){ + CurrentPoly->PivotV_L.vx -= 1; + pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L); + } + else { + CurrentPoly->PivotV_L.vx = CurrentPoly->PivotV_L.vx; + } + }; + + if(pad & PADRright){ + if (CurrentPoly->PivotV_L.vx <= CurrentPoly->width ){ + CurrentPoly->PivotV_L.vx += 1; + pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L); + } + else { + CurrentPoly->PivotV_L.vx = CurrentPoly->PivotV_L.vx; + } + }; + + // R1, R2, L2, L2 + + if(pad & PADR1){ + + if(BtnTimer == 0){ + + if (polyCount < 4){ + CurrentPoly -= 1; + BtnTimer = 10; + polyCount++; + } + else { + CurrentPoly = &polyS + 1; + polyCount = 0; + } + } + } + + if(pad & PADR2){ + + if(BtnTimer == 0){ + if(CurrentPoly->otz < 5 ){ + CurrentPoly->otz += 1; + BtnTimer = 10; + } else { + CurrentPoly->otz = 1; + BtnTimer = 10; + } + } + } + + if(pad & PADL1){ + + if(BtnTimer == 0){ + if (CurrentPoly->rotSpeed <= 320){ + CurrentPoly->rotSpeed += 8; + } + BtnTimer = 10; + } + } + + if(pad & PADL2){ + + if(BtnTimer == 0){ + if (CurrentPoly->rotSpeed >= -320){ + CurrentPoly->rotSpeed -= 8; + } + BtnTimer = 10; + } + } + + // Left D-Pad + + if(pad & PADLup){ + + if(BtnTimer == 0){ + CurrentPoly->TransV_L.vy -= 1; + //~ BtnTimer = 2; + } + } + if(pad & PADLdown){ + + if(BtnTimer == 0){ + CurrentPoly->TransV_L.vy += 1; + //~ BtnTimer = 2; + } + } + if(pad & PADLleft){ + + if(BtnTimer == 0){ + CurrentPoly->TransV_L.vx -= 1; + //~ BtnTimer = 2; + } + } + if(pad & PADLright){ + + if(BtnTimer == 0){ + CurrentPoly->TransV_L.vx += 1; + //~ BtnTimer = 2; + } + } + if(pad & PADstart){ + + if(BtnTimer == 0){ + CurrentPoly->ScaleV_L.vx += 100; + CurrentPoly->ScaleV_L.vy += 100; + //~ CurrentPoly->TransV_L.vz += 1; + + } + } + if(pad & PADselect){ + + if(BtnTimer == 0){ + CurrentPoly->ScaleV_L.vx -= 100; + CurrentPoly->ScaleV_L.vy -= 100; + //~ CurrentPoly->TransV_L.vz -= 1; + + } + } + + // Btn_timer decrement + + if(BtnTimer > 0){ + BtnTimer -= 1; + } + + // Debug stuff + + // Display Rotation matrix + + //~ FntPrint("Rotmatrix:\n%d %d %d\n%d %d %d\n%d %d %d \n", + //~ Poly1Matrix.m[0][0], Poly1Matrix.m[0][1], Poly1Matrix.m[0][2], + //~ Poly1Matrix.m[1][0], Poly1Matrix.m[1][1], Poly1Matrix.m[1][2], + //~ Poly1Matrix.m[2][0], Poly1Matrix.m[2][1], Poly1Matrix.m[2][2]); + + // Display Mem adress and values of verticess + //~ FntPrint("cur:%x\n 0:%x\n 1:%x\n 2:%x\n 3:%x\n", CurrentPoly, &polyS, &poly1S, &poly2S, &poly3S); + //~ FntPrint("timer:%d polyCount:%d speed:%d", BtnTimer, polyCount, CurrentPoly->rotSpeed ); + + //~ FntPrint("&poly->x0 Addr:%x Value:%d \n&poly->y0 Addr:%x Value:%d \n&poly->x1 Addr:%x Value:%d", + //~ (long)&poly->x0, poly->x0, + //~ (long)&poly->y0, poly->y0, + //~ (long)&poly->x1, poly->x1); + + //~ FntPrint("otz : %d\n" , CurrentPoly->rotSpeed); + + // On-screen instructions + + FntPrint("\ +D-Pad:move polygon.\n\ +[],X,O,\/\\ : Move pivot point.\n\ +L1,L2 : Rotations speed +/-\n\ +R1 : select polygon\n\ +R2 : change z-index\n\ +Start,Select : Scale polygon +/-\ +"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_2pads.c b/hello_2pads.c new file mode 100644 index 0000000..e1ac71a --- /dev/null +++ b/hello_2pads.c @@ -0,0 +1,417 @@ +// hello_libpad example +// +// We're using libpad this time. +// You can use the classic controller, analog, wheel, gun buttons or mouse +// +// Schnappy - 12/2020 +// +// Based on : ../psyq/addons/scea/CNTRL/PAD.C +// +// Controller demo +// Written by Mike Fulton +// Last Modified 6:25pm, 11/15/96 +// Copyright (c) 1996 Sony Computer Entertainment America + + +#include +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 32 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + + +// Pad stuff + +// Structure for RAW hardware-based light gun position values + +typedef struct +{ + unsigned short v_count; // Y-axis (vertical scan counter) + unsigned short h_count; // H-axis (horizontal pixel clock value) +} Gun_Position; + +// Structure for storing processed controller data + +typedef struct +{ + int xpos, ypos; // Stored position for sprite(s) + int xpos2, ypos2; // controlled by this controller. + + unsigned char status; // These 8 values are obtained + unsigned char type; // directly from the controller + unsigned char button1; // buffer we installed with InitPAD. + unsigned char button2; + unsigned char analog0; + unsigned char analog1; + unsigned char analog2; + unsigned char analog3; +} Controller_Data; + +// All-purpose controller data buffer + +typedef struct +{ + unsigned char pad[34]; // 8-bytes w/o Multi-Tap, 34-bytes w/Multi-Tap +} Controller_Buffer; + +Controller_Buffer controllers[2]; // Buffers for reading controllers + +Controller_Data theControllers[8]; // Processed controller data + +void init(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + +void get_digital_direction( Controller_Data *c, int buttondata ) // get analog stick values +{ +int i; + + i = ~(buttondata); + + if( i & 0x80 ) + c->xpos -= 1; + + if( i & 0x20 ) + c->xpos += 1; + + if( i & 0x40 ) + c->ypos += 1; + + if( i & 0x10 ) + c->ypos -= 1; +} + +void read_controller( Controller_Data *c, unsigned char *buf, int port ) // get the raw values from controller +{ + register int mouse_x, mouse_y, x; + register Gun_Position *g; + + c->status = buf[0]; // Copy over raw controller data + c->type = buf[1]; + c->button1 = buf[2]; + c->button2 = buf[3]; + c->analog0 = buf[4]; + c->analog1 = buf[5]; + c->analog2 = buf[6]; + c->analog3 = buf[7]; + + if( buf[0] == 0xff ) // If controller returns BAD status then bail on it. + { + c->type = 0; + return; + } + + // Look at the controller type code & process controller data as indicated + + switch( c->type ) + { + case 0x12: // Sony Mouse + mouse_x = buf[4]; + mouse_y = buf[5]; + + if( mouse_x & 0x80 ) + mouse_x |= 0xffffff80; + if( mouse_y & 0x80 ) + mouse_y |= 0xffffff80; + + c->xpos += mouse_x; + c->ypos += mouse_y; + break; + + case 0x23: // Namco negCon + // Steering wheel + // Sankyo Pachinko controler + get_digital_direction( c, buf[2] ); + break; + + case 0x53: // Analog 2-stick + get_digital_direction( c, buf[2] ); + break; + + case 0x41: // Standard Sony PAD controller + get_digital_direction( c, buf[2] ); + break; + + default: // If don't know what it is, treat it like standard controller + get_digital_direction( c, buf[2] ); + break; + } +} + +int main(void) +{ + + TILE * PADL; // Tile primitives + TILE * TRIGGERL; + + TILE * PADR; + TILE * TRIGGERR; + + TILE * START, * SELECT; + + init(); + + InitPAD(controllers[0].pad, 34, controllers[1].pad, 34); + + StartPAD(); + + + while (1) + { + + read_controller( &theControllers[0], &controllers[0].pad[0], 0 ); // Read controllers + read_controller( &theControllers[1], &controllers[1].pad[0], 1 ); + + ClearOTagR(ot[db], OTLEN); + + // D-cross + + PADL = (TILE *)nextpri; + + setTile(PADL); + setRGB0(PADL, 80, 180, 255); + setXY0(PADL, CENTERX - 80, CENTERY); + setWH(PADL, 24, 24); + + + addPrim(ot[db], PADL); + + nextpri += sizeof(TILE); + + // L1+L2 + + TRIGGERL = (TILE *)nextpri; + + setTile(TRIGGERL); + setRGB0(TRIGGERL, 255, 0, 0); + setXY0(TRIGGERL, CENTERX - 80, CENTERY - 80); + setWH(TRIGGERL, 24, 24); + + + addPrim(ot[db], TRIGGERL); + + nextpri += sizeof(TILE); + + // /\, X, O, [] + + PADR = (TILE *)nextpri; + + setTile(PADR); + setRGB0(PADR, 0, 255, 0); + setXY0(PADR, CENTERX + 50, CENTERY); + setWH(PADR, 24, 24); + + addPrim(ot[db], PADR); + + nextpri += sizeof(TILE); + + // R1+R2 + + TRIGGERR = (TILE *)nextpri; + + setTile(TRIGGERR); + setRGB0(TRIGGERR, 255, 0, 255); + setXY0(TRIGGERR, CENTERX + 50, CENTERY -80); + setWH(TRIGGERR, 24, 24); + + addPrim(ot[db], TRIGGERR); + + nextpri += sizeof(TILE); + + // START + SELECT + + START = (TILE *)nextpri; + + setTile(START); + setRGB0(START, 240, 240, 240); + setXY0(START, CENTERX - 16, CENTERY - 36); + setWH(START, 24, 24); + + addPrim(ot[db], START); + + nextpri += sizeof(TILE); + + // D-pad + + switch(theControllers[0].button1){ + + case 0xDF: // Right + PADL->x0 = CENTERX - 64; + break; + case 0x7F: // Left + PADL->x0 = CENTERX - 96; + break; + case 0xEF: // Up + PADL->y0 = CENTERY - 16; + break; + case 0xBF: // Down + PADL->y0 = CENTERY + 16; + break; + + // Start & Select + + case 0xF7: + START->w = 32; START->h = 32;START->x0 -= 4;START->y0 -= 4; // START + break; + case 0xFE: // SELECT + START->r0 = 0; + break; + + // Dualshock L3 + R3 + + case 0xFD: // L3 + TRIGGERL->w += 10; + TRIGGERL->h += 10; + break; + + case 0xFB: //R3 + TRIGGERR->w += 10; + TRIGGERR->h += 10; + break; + + } + + // Buttons + + switch(theControllers[0].button2){ + + case 0xDF: // â­˜ + PADR->x0 = CENTERX + 66; + break; + case 0x7F: // ⬜ + PADR->x0 = CENTERX + 34; + break; + case 0xEF: // â–³ + PADR->y0 = CENTERY - 16; + break; + case 0xBF: // ╳ + PADR->y0 = CENTERY + 16; + break; + + // Shoulder buttons + + case 0xFB: // L1 + TRIGGERL->y0 = CENTERY - 64; + break; + case 0xFE: // L2 + TRIGGERL->y0 = CENTERY - 96; + break; + + case 0xF7: // R1 + TRIGGERR->y0 = CENTERY - 96; + break; + case 0xFD: // R2 + TRIGGERR->y0 = CENTERY - 64; + break; + + + // Mouse buttons + + case 0xF4: // Mouse Left click + PADL->w += 10; + PADL->h += 10; + break; + + case 0xF8: // Mouse Right click + PADL->w -= 10; + PADL->h -= 10; + break; + } + + + FntPrint("Hello 2 pads!\n\n"); + + FntPrint( "Pad 1 : %02x\nButtons:%02x %02x, Stick:%02x %02x %02x %02x\n", + theControllers[0].type, // Controller type : 00 == none, 41 == standard, 73 == analog/dualshock, 12 == mouse, 23 == steering wheel, 63 == gun, 53 == analog joystick + theControllers[0].button1, // + theControllers[0].button2, + theControllers[0].analog0, + theControllers[0].analog1, + theControllers[0].analog2, + theControllers[0].analog3 ); + + FntPrint( "Pad 2 : %02x\nButtons:%02x %02x, Stick:%02x %02x %02x %02x\n", + theControllers[1].type, // Controller type : 00 == none, 41 == standard, 73 == analog/dualshock, 12 == mouse, 23 == steering wheel, 63 == gun, 53 == analog joystick + theControllers[1].button1, // + theControllers[1].button2, + theControllers[1].analog0, // R3 horizontal + theControllers[1].analog1, // R3 vertical + theControllers[1].analog2, // L3 horizontal + theControllers[1].analog3 ); // L3 vertical + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_2pads/hello_2pads.c b/hello_2pads/hello_2pads.c new file mode 100644 index 0000000..e1ac71a --- /dev/null +++ b/hello_2pads/hello_2pads.c @@ -0,0 +1,417 @@ +// hello_libpad example +// +// We're using libpad this time. +// You can use the classic controller, analog, wheel, gun buttons or mouse +// +// Schnappy - 12/2020 +// +// Based on : ../psyq/addons/scea/CNTRL/PAD.C +// +// Controller demo +// Written by Mike Fulton +// Last Modified 6:25pm, 11/15/96 +// Copyright (c) 1996 Sony Computer Entertainment America + + +#include +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 32 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + + +// Pad stuff + +// Structure for RAW hardware-based light gun position values + +typedef struct +{ + unsigned short v_count; // Y-axis (vertical scan counter) + unsigned short h_count; // H-axis (horizontal pixel clock value) +} Gun_Position; + +// Structure for storing processed controller data + +typedef struct +{ + int xpos, ypos; // Stored position for sprite(s) + int xpos2, ypos2; // controlled by this controller. + + unsigned char status; // These 8 values are obtained + unsigned char type; // directly from the controller + unsigned char button1; // buffer we installed with InitPAD. + unsigned char button2; + unsigned char analog0; + unsigned char analog1; + unsigned char analog2; + unsigned char analog3; +} Controller_Data; + +// All-purpose controller data buffer + +typedef struct +{ + unsigned char pad[34]; // 8-bytes w/o Multi-Tap, 34-bytes w/Multi-Tap +} Controller_Buffer; + +Controller_Buffer controllers[2]; // Buffers for reading controllers + +Controller_Data theControllers[8]; // Processed controller data + +void init(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + +void get_digital_direction( Controller_Data *c, int buttondata ) // get analog stick values +{ +int i; + + i = ~(buttondata); + + if( i & 0x80 ) + c->xpos -= 1; + + if( i & 0x20 ) + c->xpos += 1; + + if( i & 0x40 ) + c->ypos += 1; + + if( i & 0x10 ) + c->ypos -= 1; +} + +void read_controller( Controller_Data *c, unsigned char *buf, int port ) // get the raw values from controller +{ + register int mouse_x, mouse_y, x; + register Gun_Position *g; + + c->status = buf[0]; // Copy over raw controller data + c->type = buf[1]; + c->button1 = buf[2]; + c->button2 = buf[3]; + c->analog0 = buf[4]; + c->analog1 = buf[5]; + c->analog2 = buf[6]; + c->analog3 = buf[7]; + + if( buf[0] == 0xff ) // If controller returns BAD status then bail on it. + { + c->type = 0; + return; + } + + // Look at the controller type code & process controller data as indicated + + switch( c->type ) + { + case 0x12: // Sony Mouse + mouse_x = buf[4]; + mouse_y = buf[5]; + + if( mouse_x & 0x80 ) + mouse_x |= 0xffffff80; + if( mouse_y & 0x80 ) + mouse_y |= 0xffffff80; + + c->xpos += mouse_x; + c->ypos += mouse_y; + break; + + case 0x23: // Namco negCon + // Steering wheel + // Sankyo Pachinko controler + get_digital_direction( c, buf[2] ); + break; + + case 0x53: // Analog 2-stick + get_digital_direction( c, buf[2] ); + break; + + case 0x41: // Standard Sony PAD controller + get_digital_direction( c, buf[2] ); + break; + + default: // If don't know what it is, treat it like standard controller + get_digital_direction( c, buf[2] ); + break; + } +} + +int main(void) +{ + + TILE * PADL; // Tile primitives + TILE * TRIGGERL; + + TILE * PADR; + TILE * TRIGGERR; + + TILE * START, * SELECT; + + init(); + + InitPAD(controllers[0].pad, 34, controllers[1].pad, 34); + + StartPAD(); + + + while (1) + { + + read_controller( &theControllers[0], &controllers[0].pad[0], 0 ); // Read controllers + read_controller( &theControllers[1], &controllers[1].pad[0], 1 ); + + ClearOTagR(ot[db], OTLEN); + + // D-cross + + PADL = (TILE *)nextpri; + + setTile(PADL); + setRGB0(PADL, 80, 180, 255); + setXY0(PADL, CENTERX - 80, CENTERY); + setWH(PADL, 24, 24); + + + addPrim(ot[db], PADL); + + nextpri += sizeof(TILE); + + // L1+L2 + + TRIGGERL = (TILE *)nextpri; + + setTile(TRIGGERL); + setRGB0(TRIGGERL, 255, 0, 0); + setXY0(TRIGGERL, CENTERX - 80, CENTERY - 80); + setWH(TRIGGERL, 24, 24); + + + addPrim(ot[db], TRIGGERL); + + nextpri += sizeof(TILE); + + // /\, X, O, [] + + PADR = (TILE *)nextpri; + + setTile(PADR); + setRGB0(PADR, 0, 255, 0); + setXY0(PADR, CENTERX + 50, CENTERY); + setWH(PADR, 24, 24); + + addPrim(ot[db], PADR); + + nextpri += sizeof(TILE); + + // R1+R2 + + TRIGGERR = (TILE *)nextpri; + + setTile(TRIGGERR); + setRGB0(TRIGGERR, 255, 0, 255); + setXY0(TRIGGERR, CENTERX + 50, CENTERY -80); + setWH(TRIGGERR, 24, 24); + + addPrim(ot[db], TRIGGERR); + + nextpri += sizeof(TILE); + + // START + SELECT + + START = (TILE *)nextpri; + + setTile(START); + setRGB0(START, 240, 240, 240); + setXY0(START, CENTERX - 16, CENTERY - 36); + setWH(START, 24, 24); + + addPrim(ot[db], START); + + nextpri += sizeof(TILE); + + // D-pad + + switch(theControllers[0].button1){ + + case 0xDF: // Right + PADL->x0 = CENTERX - 64; + break; + case 0x7F: // Left + PADL->x0 = CENTERX - 96; + break; + case 0xEF: // Up + PADL->y0 = CENTERY - 16; + break; + case 0xBF: // Down + PADL->y0 = CENTERY + 16; + break; + + // Start & Select + + case 0xF7: + START->w = 32; START->h = 32;START->x0 -= 4;START->y0 -= 4; // START + break; + case 0xFE: // SELECT + START->r0 = 0; + break; + + // Dualshock L3 + R3 + + case 0xFD: // L3 + TRIGGERL->w += 10; + TRIGGERL->h += 10; + break; + + case 0xFB: //R3 + TRIGGERR->w += 10; + TRIGGERR->h += 10; + break; + + } + + // Buttons + + switch(theControllers[0].button2){ + + case 0xDF: // â­˜ + PADR->x0 = CENTERX + 66; + break; + case 0x7F: // ⬜ + PADR->x0 = CENTERX + 34; + break; + case 0xEF: // â–³ + PADR->y0 = CENTERY - 16; + break; + case 0xBF: // ╳ + PADR->y0 = CENTERY + 16; + break; + + // Shoulder buttons + + case 0xFB: // L1 + TRIGGERL->y0 = CENTERY - 64; + break; + case 0xFE: // L2 + TRIGGERL->y0 = CENTERY - 96; + break; + + case 0xF7: // R1 + TRIGGERR->y0 = CENTERY - 96; + break; + case 0xFD: // R2 + TRIGGERR->y0 = CENTERY - 64; + break; + + + // Mouse buttons + + case 0xF4: // Mouse Left click + PADL->w += 10; + PADL->h += 10; + break; + + case 0xF8: // Mouse Right click + PADL->w -= 10; + PADL->h -= 10; + break; + } + + + FntPrint("Hello 2 pads!\n\n"); + + FntPrint( "Pad 1 : %02x\nButtons:%02x %02x, Stick:%02x %02x %02x %02x\n", + theControllers[0].type, // Controller type : 00 == none, 41 == standard, 73 == analog/dualshock, 12 == mouse, 23 == steering wheel, 63 == gun, 53 == analog joystick + theControllers[0].button1, // + theControllers[0].button2, + theControllers[0].analog0, + theControllers[0].analog1, + theControllers[0].analog2, + theControllers[0].analog3 ); + + FntPrint( "Pad 2 : %02x\nButtons:%02x %02x, Stick:%02x %02x %02x %02x\n", + theControllers[1].type, // Controller type : 00 == none, 41 == standard, 73 == analog/dualshock, 12 == mouse, 23 == steering wheel, 63 == gun, 53 == analog joystick + theControllers[1].button1, // + theControllers[1].button2, + theControllers[1].analog0, // R3 horizontal + theControllers[1].analog1, // R3 vertical + theControllers[1].analog2, // L3 horizontal + theControllers[1].analog3 ); // L3 vertical + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_cube.c b/hello_cube.c new file mode 100644 index 0000000..900a55d --- /dev/null +++ b/hello_cube.c @@ -0,0 +1,178 @@ +/* primdrawG.c, by Schnappy, 12-2020 + - Draw a gouraud shaded mesh exported as a TMESH by the blender <= 2.79b plugin io_export_psx_tmesh.py + based on primdraw.c by Lameguy64 (http://www.psxdev.net/forum/viewtopic.php?f=64&t=537) + 2014 Meido-Tek Productions. + Demonstrates: + - Using a primitive OT to draw triangles without libgs. + - Using the GTE to rotate, translate, and project 3D primitives. + Controls: + Start - Toggle interactive/non-interactive mode. + Select - Reset object's position and angles. + L1/L2 - Move object closer/farther. + L2/R2 - Rotate object (XY). + Up/Down/Left/Right - Rotate object (XZ/YZ). + Triangle/Cross/Square/Circle - Move object up/down/left/right. +*/ + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ +#include +#include +#include +#include +#include +// Sample vector model +#include "cube.c" +#define VMODE 0 +#define SCREENXRES 320 +#define SCREENYRES 240 +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 +#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] = {0}; // Primitive list // That's our prim buffer +char * nextpri = primbuff[0]; // Primitive counter +short db = 0; // Current buffer counter +// Prototypes +void init(void); +void display(void); +//~ void LoadTexture(u_long * 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; + } + setRGB0(&draw[0], 0, 128, 255); + setRGB0(&draw[1], 0, 128, 255); + draw[0].isbg = 1; + draw[1].isbg = 1; + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + // Init font system + FntLoad(960, 0); + FntOpen(16, 16, 196, 64, 0, 256); + } +void display(void){ + DrawSync(0); + VSync(0); + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + SetDispMask(1); + DrawOTag(ot[db] + OTLEN - 1); + db = !db; + nextpri = primbuff[db]; + } +int main() { + int i; + int PadStatus; + int TPressed=0; + int AutoRotate=1; + long t, p, OTz, Flag; // t == vertex count, p == depth cueing interpolation value, OTz == value to create Z-ordered OT, Flag == see LibOver47.pdf, p.143 + POLY_G3 *poly = {0}; // pointer to a POLY_G4 + SVECTOR Rotate={ 0 }; // Rotation coordinates + VECTOR Trans={ 0, 0, CENTERX * 3, 0 }; // Translation coordinates + // Scaling coordinates + VECTOR Scale={ ONE/2, ONE/2, ONE/2, 0 }; // ONE == 4096 + MATRIX Matrix={0}; // Matrix data for the GTE + init(); + // Main loop + while (1) { + // Read pad status + PadStatus = PadRead(0); + if (AutoRotate == 0) { + if (PadStatus & PADL1) Trans.vz -= 4; + if (PadStatus & PADR1) Trans.vz += 4; + if (PadStatus & PADL2) Rotate.vz -= 8; + if (PadStatus & PADR2) Rotate.vz += 8; + if (PadStatus & PADLup) Rotate.vx -= 8; + if (PadStatus & PADLdown) Rotate.vx += 8; + if (PadStatus & PADLleft) Rotate.vy -= 8; + if (PadStatus & PADLright) Rotate.vy += 8; + if (PadStatus & PADRup) Trans.vy -= 2; + if (PadStatus & PADRdown) Trans.vy += 2; + if (PadStatus & PADRleft) Trans.vx -= 2; + if (PadStatus & PADRright) Trans.vx += 2; + if (PadStatus & PADselect) { + Rotate.vx = Rotate.vy = Rotate.vz = 0; + Scale.vx = Scale.vy = Scale.vz = ONE/2; + Trans.vx = Trans.vy = 0; + Trans.vz = CENTERX * 3; + } + } + if (PadStatus & PADstart) { + if (TPressed == 0) { + AutoRotate = (AutoRotate + 1) & 1; + Rotate.vx = Rotate.vy = Rotate.vz = 0; + Scale.vx = Scale.vy = Scale.vz = ONE/2; + Trans.vx = Trans.vy = 0; + Trans.vz = CENTERX * 3; + } + TPressed = 1; + } else { + TPressed = 0; + } + if (AutoRotate) { + Rotate.vy += 8; // Pan + Rotate.vx += 8; // Tilt + //~ Rotate.vz += 8; // Roll + } + // Clear the current OT + ClearOTagR(ot[db], OTLEN); + // Convert and set the matrixes + RotMatrix(&Rotate, &Matrix); + TransMatrix(&Matrix, &Trans); + ScaleMatrix(&Matrix, &Scale); + SetRotMatrix(&Matrix); + SetTransMatrix(&Matrix); + // Render the sample vector model + t=0; + // modelCube is a TMESH, len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ... + for (i = 0; i < (modelCube.len*3); i += 3) { + poly = (POLY_G3 *)nextpri; + // Initialize the primitive and set its color values + SetPolyG3(poly); + setRGB0(poly, modelCube.c[i].r , modelCube.c[i].g , modelCube.c[i].b); + setRGB1(poly, modelCube.c[i+2].r, modelCube.c[i+2].g, modelCube.c[i+2].b); + setRGB2(poly, modelCube.c[i+1].r, modelCube.c[i+1].g, modelCube.c[i+1].b); + // Rotate, translate, and project the vectors and output the results into a primitive + OTz = RotTransPers(&modelCube_mesh[modelCube_index[t]] , (long*)&poly->x0, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+2]], (long*)&poly->x1, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+1]], (long*)&poly->x2, &p, &Flag); + // Sort the primitive into the OT + OTz /= 3; + if ((OTz > 0) && (OTz < OTLEN)) + AddPrim(&ot[db][OTz-2], poly); + nextpri += sizeof(POLY_G3); + t+=3; + } + FntPrint("Hello gouraud shaded cube!\n"); + FntFlush(-1); + display(); + } + return 0; +} diff --git a/hello_cube/cube.c b/hello_cube/cube.c new file mode 100644 index 0000000..12dd65c --- /dev/null +++ b/hello_cube/cube.c @@ -0,0 +1,167 @@ +SVECTOR modelCube_mesh[] = { + { -128,128,128 }, + { 128,128,128 }, + { 128,128,-128 }, + { -128,128,-128 }, + { -128,-128,128 }, + { 128,-128,128 }, + { 128,-128,-128 }, + { -128,-128,-128 } +}; + +SVECTOR modelCube_normal[] = { + 2365,-2365,-2365, 0, + -2365,-2365,-2365, 0, + -2365,-2365,2365, 0, + 2365,-2365,2365, 0, + 2365,2365,-2365, 0, + -2365,2365,-2365, 0, + -2365,2365,2365, 0, + 2365,2365,2365, 0 +}; + +CVECTOR modelCube_color[] = { + 255,237,0, 0, + 255,235,0, 0, + 255,236,0, 0, + 255,2,0, 0, + 254,3,0, 0, + 255,8,0, 0, + 229,0,255, 0, + 229,0,255, 0, + 229,0,255, 0, + 5,16,250, 0, + 0,12,255, 0, + 0,12,255, 0, + 4,251,25, 0, + 0,255,26, 0, + 0,255,26, 0, + 0,248,255, 0, + 0,248,255, 0, + 0,248,255, 0, + 255,237,0, 0, + 255,237,0, 0, + 255,235,0, 0, + 255,2,0, 0, + 255,6,2, 0, + 254,3,0, 0, + 229,0,255, 0, + 232,21,232, 0, + 229,0,255, 0, + 5,16,250, 0, + 2,13,253, 0, + 0,12,255, 0, + 4,251,25, 0, + 0,255,26, 0, + 0,255,26, 0, + 0,248,255, 0, + 0,248,255, 0, + 0,248,255, 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, + 0, + modelCube_color, + 12 +}; + +SVECTOR modelCube1_mesh[] = { + { -128,128,128 }, + { 128,128,128 }, + { 128,128,-128 }, + { -128,128,-128 }, + { -128,-128,128 }, + { 128,-128,128 }, + { 128,-128,-128 }, + { -128,-128,-128 } +}; + +SVECTOR modelCube1_normal[] = { + 2365,-2365,-2365, 0, + -2365,-2365,-2365, 0, + -2365,-2365,2365, 0, + 2365,-2365,2365, 0, + 2365,2365,-2365, 0, + -2365,2365,-2365, 0, + -2365,2365,2365, 0, + 2365,2365,2365, 0 +}; + +CVECTOR modelCube1_color[] = { + 255,237,0, 0, + 255,235,0, 0, + 255,236,0, 0, + 255,2,0, 0, + 254,3,0, 0, + 255,8,0, 0, + 229,0,255, 0, + 229,0,255, 0, + 229,0,255, 0, + 5,16,250, 0, + 0,12,255, 0, + 0,12,255, 0, + 4,251,25, 0, + 0,255,26, 0, + 0,255,26, 0, + 0,248,255, 0, + 0,248,255, 0, + 0,248,255, 0, + 255,237,0, 0, + 255,237,0, 0, + 255,235,0, 0, + 255,2,0, 0, + 255,6,2, 0, + 254,3,0, 0, + 229,0,255, 0, + 232,21,232, 0, + 229,0,255, 0, + 5,16,250, 0, + 2,13,253, 0, + 0,12,255, 0, + 4,251,25, 0, + 0,255,26, 0, + 0,255,26, 0, + 0,248,255, 0, + 0,248,255, 0, + 0,248,255, 0 +}; + +int modelCube1_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 modelCube1 = { + modelCube1_mesh, + modelCube1_normal, + 0, + modelCube1_color, + 12 +}; diff --git a/hello_cube/hello_cube.c b/hello_cube/hello_cube.c new file mode 100644 index 0000000..0affcf4 --- /dev/null +++ b/hello_cube/hello_cube.c @@ -0,0 +1,178 @@ +/* primdrawG.c, by Schnappy, 12-2020 + - Draw a gouraud shaded mesh exported as a TMESH by the blender <= 2.79b plugin io_export_psx_tmesh.py + based on primdraw.c by Lameguy64 (http://www.psxdev.net/forum/viewtopic.php?f=64&t=537) + 2014 Meido-Tek Productions. + Demonstrates: + - Using a primitive OT to draw triangles without libgs. + - Using the GTE to rotate, translate, and project 3D primitives. + Controls: + Start - Toggle interactive/non-interactive mode. + Select - Reset object's position and angles. + L1/L2 - Move object closer/farther. + L2/R2 - Rotate object (XY). + Up/Down/Left/Right - Rotate object (XZ/YZ). + Triangle/Cross/Square/Circle - Move object up/down/left/right. +*/ + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ +#include +#include +#include +#include +#include +// Sample vector model +#include "../includes/cube.c" +#define VMODE 0 +#define SCREENXRES 320 +#define SCREENYRES 240 +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 +#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] = {0}; // Primitive list // That's our prim buffer +char * nextpri = primbuff[0]; // Primitive counter +short db = 0; // Current buffer counter +// Prototypes +void init(void); +void display(void); +//~ void LoadTexture(u_long * 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; + } + setRGB0(&draw[0], 0, 128, 255); + setRGB0(&draw[1], 0, 128, 255); + draw[0].isbg = 1; + draw[1].isbg = 1; + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + // Init font system + FntLoad(960, 0); + FntOpen(16, 16, 196, 64, 0, 256); + } +void display(void){ + DrawSync(0); + VSync(0); + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + SetDispMask(1); + DrawOTag(ot[db] + OTLEN - 1); + db = !db; + nextpri = primbuff[db]; + } +int main() { + int i; + int PadStatus; + int TPressed=0; + int AutoRotate=1; + long t, p, OTz, Flag; // t == vertex count, p == depth cueing interpolation value, OTz == value to create Z-ordered OT, Flag == see LibOver47.pdf, p.143 + POLY_G3 *poly = {0}; // pointer to a POLY_G4 + SVECTOR Rotate={ 0 }; // Rotation coordinates + VECTOR Trans={ 0, 0, CENTERX * 3, 0 }; // Translation coordinates + // Scaling coordinates + VECTOR Scale={ ONE/2, ONE/2, ONE/2, 0 }; // ONE == 4096 + MATRIX Matrix={0}; // Matrix data for the GTE + init(); + // Main loop + while (1) { + // Read pad status + PadStatus = PadRead(0); + if (AutoRotate == 0) { + if (PadStatus & PADL1) Trans.vz -= 4; + if (PadStatus & PADR1) Trans.vz += 4; + if (PadStatus & PADL2) Rotate.vz -= 8; + if (PadStatus & PADR2) Rotate.vz += 8; + if (PadStatus & PADLup) Rotate.vx -= 8; + if (PadStatus & PADLdown) Rotate.vx += 8; + if (PadStatus & PADLleft) Rotate.vy -= 8; + if (PadStatus & PADLright) Rotate.vy += 8; + if (PadStatus & PADRup) Trans.vy -= 2; + if (PadStatus & PADRdown) Trans.vy += 2; + if (PadStatus & PADRleft) Trans.vx -= 2; + if (PadStatus & PADRright) Trans.vx += 2; + if (PadStatus & PADselect) { + Rotate.vx = Rotate.vy = Rotate.vz = 0; + Scale.vx = Scale.vy = Scale.vz = ONE/2; + Trans.vx = Trans.vy = 0; + Trans.vz = CENTERX * 3; + } + } + if (PadStatus & PADstart) { + if (TPressed == 0) { + AutoRotate = (AutoRotate + 1) & 1; + Rotate.vx = Rotate.vy = Rotate.vz = 0; + Scale.vx = Scale.vy = Scale.vz = ONE/2; + Trans.vx = Trans.vy = 0; + Trans.vz = CENTERX * 3; + } + TPressed = 1; + } else { + TPressed = 0; + } + if (AutoRotate) { + Rotate.vy += 8; // Pan + Rotate.vx += 8; // Tilt + //~ Rotate.vz += 8; // Roll + } + // Clear the current OT + ClearOTagR(ot[db], OTLEN); + // Convert and set the matrixes + RotMatrix(&Rotate, &Matrix); + TransMatrix(&Matrix, &Trans); + ScaleMatrix(&Matrix, &Scale); + SetRotMatrix(&Matrix); + SetTransMatrix(&Matrix); + // Render the sample vector model + t=0; + // modelCube is a TMESH, len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ... + for (i = 0; i < (modelCube.len*3); i += 3) { + poly = (POLY_G3 *)nextpri; + // Initialize the primitive and set its color values + SetPolyG3(poly); + setRGB0(poly, modelCube.c[i].r , modelCube.c[i].g , modelCube.c[i].b); + setRGB1(poly, modelCube.c[i+2].r, modelCube.c[i+2].g, modelCube.c[i+2].b); + setRGB2(poly, modelCube.c[i+1].r, modelCube.c[i+1].g, modelCube.c[i+1].b); + // Rotate, translate, and project the vectors and output the results into a primitive + OTz = RotTransPers(&modelCube_mesh[modelCube_index[t]] , (long*)&poly->x0, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+2]], (long*)&poly->x1, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+1]], (long*)&poly->x2, &p, &Flag); + // Sort the primitive into the OT + OTz /= 3; + if ((OTz > 0) && (OTz < OTLEN)) + AddPrim(&ot[db][OTz-2], poly); + nextpri += sizeof(POLY_G3); + t+=3; + } + FntPrint("Hello gouraud shaded cube!\n"); + FntFlush(-1); + display(); + } + return 0; +} diff --git a/hello_cubetex.c b/hello_cubetex.c new file mode 100644 index 0000000..24a8c46 --- /dev/null +++ b/hello_cubetex.c @@ -0,0 +1,297 @@ + +/* primdrawG.c, by Schnappy, 12-2020 + + - Draw a gouraud shaded mesh exported as a TMESH by the blender <= 2.79b plugin io_export_psx_tmesh.py + + based on primdraw.c by Lameguy64 (http://www.psxdev.net/forum/viewtopic.php?f=64&t=537) + 2014 Meido-Tek Productions. + + Demonstrates: + - Using a primitive OT to draw triangles without libgs. + - Using the GTE to rotate, translate, and project 3D primitives. + + Controls: + Start - Toggle interactive/non-interactive mode. + Select - Reset object's position and angles. + L1/L2 - Move object closer/farther. + L2/R2 - Rotate object (XY). + Up/Down/Left/Right - Rotate object (XZ/YZ). + Triangle/Cross/Square/Circle - Move object up/down/left/right. + +*/ + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ + +#include +#include +#include +#include +#include + +// Sample vector model +#include "cubetex.c" + +#define VMODE 0 + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#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] = {0}; // Primitive list // That's our prim buffer + +//~ int primcnt=0; // Primitive counter + +char * nextpri = primbuff[0]; // Primitive counter + +short db = 0; // Current buffer counter + +// Prototypes +void init(void); +void display(void); +void LoadTexture(u_long * 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; + } + + setRGB0(&draw[0], 0, 0, 255); + setRGB0(&draw[1], 0, 0, 255); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + // Init font system + FntLoad(960, 0); + FntOpen(16, 16, 196, 64, 0, 256); + + } + +void display(void){ + + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; + + + } + +void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous + OpenTIM(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() { + + int i; + int PadStatus; + int TPressed=0; + int AutoRotate=1; + + long t, p, OTz, Flag; // t == vertex count, p == depth cueing interpolation value, OTz == value to create Z-ordered OT, Flag == see LibOver47.pdf, p.143 + + POLY_GT3 *poly = {0}; // pointer to a POLY_G4 + + + SVECTOR Rotate={ 0 }; // Rotation coordinates + VECTOR Trans={ 0, 0, CENTERX, 0 }; // Translation coordinates + // Scaling coordinates + VECTOR Scale={ ONE, ONE, ONE, 0 }; // ONE == 4096 + MATRIX Matrix={0}; // Matrix data for the GTE + + // Texture window + + DR_MODE * dr_mode; // Pointer to dr_mode prim + + RECT tws = {0, 0, 32, 32}; // Texture window coordinates : x, y, w, h + + init(); + + LoadTexture(_binary_TIM_cubetex_tim_start, &tim_cube); + + // Main loop + while (1) { + + // Read pad status + PadStatus = PadRead(0); + + if (AutoRotate == 0) { + + if (PadStatus & PADL1) Trans.vz -= 4; + if (PadStatus & PADR1) Trans.vz += 4; + if (PadStatus & PADL2) Rotate.vz -= 8; + if (PadStatus & PADR2) Rotate.vz += 8; + + if (PadStatus & PADLup) Rotate.vx -= 8; + if (PadStatus & PADLdown) Rotate.vx += 8; + if (PadStatus & PADLleft) Rotate.vy -= 8; + if (PadStatus & PADLright) Rotate.vy += 8; + + if (PadStatus & PADRup) Trans.vy -= 2; + if (PadStatus & PADRdown) Trans.vy += 2; + if (PadStatus & PADRleft) Trans.vx -= 2; + if (PadStatus & PADRright) Trans.vx += 2; + + if (PadStatus & PADselect) { + Rotate.vx = Rotate.vy = Rotate.vz = 0; + Scale.vx = Scale.vy = Scale.vz = ONE; + Trans.vx = Trans.vy = 0; + Trans.vz = CENTERX; + } + + } + + if (PadStatus & PADstart) { + if (TPressed == 0) { + AutoRotate = (AutoRotate + 1) & 1; + Rotate.vx = Rotate.vy = Rotate.vz = 0; + Scale.vx = Scale.vy = Scale.vz = ONE; + Trans.vx = Trans.vy = 0; + Trans.vz = CENTERX; + } + TPressed = 1; + } else { + TPressed = 0; + } + + if (AutoRotate) { + Rotate.vy += 8; // Pan + Rotate.vx += 8; // Tilt + //~ Rotate.vz += 8; // Roll + } + + + // Clear the current OT + ClearOTagR(ot[db], OTLEN); + + // Convert and set the matrixes + RotMatrix(&Rotate, &Matrix); + TransMatrix(&Matrix, &Trans); + ScaleMatrix(&Matrix, &Scale); + + SetRotMatrix(&Matrix); + SetTransMatrix(&Matrix); + + + // Render the sample vector model + t=0; + + // modelCube is a TMESH, len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ... + for (i = 0; i < (modelCube.len*3); i += 3) { + + poly = (POLY_GT3 *)nextpri; + + // Initialize the primitive and set its color values + + SetPolyGT3(poly); + + ((POLY_GT3 *)poly)->tpage = getTPage(tim_cube.mode&0x3, 0, + tim_cube.prect->x, + tim_cube.prect->y + ); + + setRGB0(poly, modelCube.c[i].r , modelCube.c[i].g , modelCube.c[i].b); + setRGB1(poly, modelCube.c[i+2].r, modelCube.c[i+2].g, modelCube.c[i+2].b); + setRGB2(poly, modelCube.c[i+1].r, modelCube.c[i+1].g, modelCube.c[i+1].b); + + setUV3(poly, 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 + + OTz = RotTransPers(&modelCube_mesh[modelCube_index[t]] , (long*)&poly->x0, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+2]], (long*)&poly->x1, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+1]], (long*)&poly->x2, &p, &Flag); + + // Sort the primitive into the OT + OTz /= 3; + if ((OTz > 0) && (OTz < OTLEN)) + AddPrim(&ot[db][OTz-2], poly); + + nextpri += sizeof(POLY_GT3); + + t+=3; + + } + + dr_mode = (DR_MODE *)nextpri; + + setDrawMode(dr_mode,1,0, getTPage(tim_cube.mode&0x3, 0, + tim_cube.prect->x, + tim_cube.prect->y), &tws); //set texture window + + AddPrim(&ot[db], dr_mode); + + nextpri += sizeof(DR_MODE); + + + FntPrint("Hello textured cube!\n"); + + + FntFlush(-1); + + display(); + + } + return 0; +} diff --git a/hello_cubetex/cubetex.c b/hello_cubetex/cubetex.c new file mode 100644 index 0000000..c408d4e --- /dev/null +++ b/hello_cubetex/cubetex.c @@ -0,0 +1,132 @@ +SVECTOR modelCube_mesh[] = { + {20.0,19.999998807907104,-20.0}, + {20.0,-20.0,-20.0}, + {-20.00000238418579,-19.999996423721313,-20.0}, + {-19.999992847442627,20.000007152557373,-20.0}, + {20.000009536743164,19.99998927116394,20.0}, + {19.99998688697815,-20.000011920928955,20.0}, + {-20.000007152557373,-19.999992847442627,20.0}, + {-19.999998807907104,20.0,20.0} +}; + +SVECTOR modelCube_normal[] = { + 0.0,-0.0,-1.0,0, + 0.0,0.0,1.0,0, + 1.0,0.0,-2.384185791015625e-07,0, + -8.940696716308594e-08,-1.0,-2.9802325229866256e-07,0, + -1.0,2.384185791015625e-07,-1.4901158351676713e-07,0, + 2.6822084464583895e-07,1.0,2.3841852225814364e-07,0, + 0.0,0.0,-1.0,0, + 0.0,-0.0,1.0,0, + 1.0,-5.662441253662109e-07,3.2782537573439186e-07,0, + -4.768372150465439e-07,-1.0,8.940690321423972e-08,0, + -1.0,2.0861631355728605e-07,-1.1920931797249068e-07,0, + 2.0861631355728605e-07,1.0,1.7881397695873602e-07,0 +}; + +SVECTOR modelCube_uv[] = { + 83.71398162841797,83.71389770507812, 0, 0, + 125.03179168701172,42.396141052246094, 0, 0, + 83.71398162841797,42.396141052246094, 0, 0, + 125.03179168701172,83.71392059326172, 0, 0, + 83.71398162841797,125.03166770935059, 0, 0, + 125.03179168701172,125.03169059753418, 0, 0, + 1.0784510374069214,83.71392059326172, 0, 0, + 42.39619445800781,125.03169059753418, 0, 0, + 42.39621353149414,83.71392440795898, 0, 0, + 42.39621353149414,125.03166770935059, 0, 0, + 83.71398162841797,83.71392440795898, 0, 0, + 42.39621353149414,83.71390151977539, 0, 0, + 42.39619445800781,1.0783309936523438, 0, 0, + 1.0784281492233276,42.39611053466797, 0, 0, + 42.39619445800781,42.39612579345703, 0, 0, + 42.39619064331055,83.71392059326172, 0, 0, + 1.0784281492233276,42.396141052246094, 0, 0, + 1.0784281492233276,83.71392059326172, 0, 0, + 83.71398162841797,83.71389770507812, 0, 0, + 125.03179168701172,83.71390151977539, 0, 0, + 125.03179168701172,42.396141052246094, 0, 0, + 125.03179168701172,83.71392059326172, 0, 0, + 83.71399688720703,83.71392440795898, 0, 0, + 83.71398162841797,125.03166770935059, 0, 0, + 1.0784510374069214,83.71392059326172, 0, 0, + 1.0784281492233276,125.03169059753418, 0, 0, + 42.39619445800781,125.03169059753418, 0, 0, + 42.39621353149414,125.03166770935059, 0, 0, + 83.71398162841797,125.03169059753418, 0, 0, + 83.71398162841797,83.71392440795898, 0, 0, + 42.39619445800781,1.0783309936523438, 0, 0, + 1.0784281492233276,1.0783309936523438, 0, 0, + 1.0784281492233276,42.39611053466797, 0, 0, + 42.39619064331055,83.71392059326172, 0, 0, + 42.39619445800781,42.396141052246094, 0, 0, + 1.0784281492233276,42.396141052246094, 0, 0 +}; + +CVECTOR modelCube_color[] = { + 255,255,255, 0, + 255,255,255, 0, + 255,0,251, 0, + 255,255,255, 0, + 255,5,7, 0, + 255,255,255, 0, + 255,255,255, 0, + 255,255,255, 0, + 4,18,255, 0, + 255,5,7, 0, + 255,255,255, 0, + 255,255,255, 0, + 254,255,23, 0, + 122,255,107, 0, + 255,255,255, 0, + 255,255,255, 0, + 255,255,255, 0, + 254,255,94, 0, + 255,255,255, 0, + 35,255,11, 0, + 255,255,255, 0, + 255,255,255, 0, + 255,255,255, 0, + 255,5,7, 0, + 255,255,255, 0, + 255,5,7, 0, + 255,255,255, 0, + 255,5,7, 0, + 255,255,255, 0, + 255,255,255, 0, + 254,255,23, 0, + 255,255,255, 0, + 122,255,107, 0, + 255,255,255, 0, + 54,65,255, 0, + 255,255,255, 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 +}; + +extern unsigned long _binary____TIM_cubetex_tim_start[]; +extern unsigned long _binary____TIM_cubetex_tim_end[]; +extern unsigned long _binary____TIM_cubetex_tim_length; + +TIM_IMAGE tim_cube; diff --git a/hello_cubetex/hello_cubetex.c b/hello_cubetex/hello_cubetex.c new file mode 100644 index 0000000..2f1060e --- /dev/null +++ b/hello_cubetex/hello_cubetex.c @@ -0,0 +1,297 @@ + +/* primdrawG.c, by Schnappy, 12-2020 + + - Draw a gouraud shaded mesh exported as a TMESH by the blender <= 2.79b plugin io_export_psx_tmesh.py + + based on primdraw.c by Lameguy64 (http://www.psxdev.net/forum/viewtopic.php?f=64&t=537) + 2014 Meido-Tek Productions. + + Demonstrates: + - Using a primitive OT to draw triangles without libgs. + - Using the GTE to rotate, translate, and project 3D primitives. + + Controls: + Start - Toggle interactive/non-interactive mode. + Select - Reset object's position and angles. + L1/L2 - Move object closer/farther. + L2/R2 - Rotate object (XY). + Up/Down/Left/Right - Rotate object (XZ/YZ). + Triangle/Cross/Square/Circle - Move object up/down/left/right. + +*/ + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ + +#include +#include +#include +#include +#include + +// Sample vector model +#include "cubetex.c" + +#define VMODE 0 + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#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] = {0}; // Primitive list // That's our prim buffer + +//~ int primcnt=0; // Primitive counter + +char * nextpri = primbuff[0]; // Primitive counter + +short db = 0; // Current buffer counter + +// Prototypes +void init(void); +void display(void); +void LoadTexture(u_long * 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; + } + + setRGB0(&draw[0], 0, 0, 255); + setRGB0(&draw[1], 0, 0, 255); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + // Init font system + FntLoad(960, 0); + FntOpen(16, 16, 196, 64, 0, 256); + + } + +void display(void){ + + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; + + + } + +void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous + OpenTIM(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() { + + int i; + int PadStatus; + int TPressed=0; + int AutoRotate=1; + + long t, p, OTz, Flag; // t == vertex count, p == depth cueing interpolation value, OTz == value to create Z-ordered OT, Flag == see LibOver47.pdf, p.143 + + POLY_GT3 *poly = {0}; // pointer to a POLY_G4 + + + SVECTOR Rotate={ 0 }; // Rotation coordinates + VECTOR Trans={ 0, 0, CENTERX, 0 }; // Translation coordinates + // Scaling coordinates + VECTOR Scale={ ONE, ONE, ONE, 0 }; // ONE == 4096 + MATRIX Matrix={0}; // Matrix data for the GTE + + // Texture window + + DR_MODE * dr_mode; // Pointer to dr_mode prim + + RECT tws = {0, 0, 32, 32}; // Texture window coordinates : x, y, w, h + + init(); + + LoadTexture(_binary____TIM_cubetex_tim_start, &tim_cube); + + // Main loop + while (1) { + + // Read pad status + PadStatus = PadRead(0); + + if (AutoRotate == 0) { + + if (PadStatus & PADL1) Trans.vz -= 4; + if (PadStatus & PADR1) Trans.vz += 4; + if (PadStatus & PADL2) Rotate.vz -= 8; + if (PadStatus & PADR2) Rotate.vz += 8; + + if (PadStatus & PADLup) Rotate.vx -= 8; + if (PadStatus & PADLdown) Rotate.vx += 8; + if (PadStatus & PADLleft) Rotate.vy -= 8; + if (PadStatus & PADLright) Rotate.vy += 8; + + if (PadStatus & PADRup) Trans.vy -= 2; + if (PadStatus & PADRdown) Trans.vy += 2; + if (PadStatus & PADRleft) Trans.vx -= 2; + if (PadStatus & PADRright) Trans.vx += 2; + + if (PadStatus & PADselect) { + Rotate.vx = Rotate.vy = Rotate.vz = 0; + Scale.vx = Scale.vy = Scale.vz = ONE; + Trans.vx = Trans.vy = 0; + Trans.vz = CENTERX; + } + + } + + if (PadStatus & PADstart) { + if (TPressed == 0) { + AutoRotate = (AutoRotate + 1) & 1; + Rotate.vx = Rotate.vy = Rotate.vz = 0; + Scale.vx = Scale.vy = Scale.vz = ONE; + Trans.vx = Trans.vy = 0; + Trans.vz = CENTERX; + } + TPressed = 1; + } else { + TPressed = 0; + } + + if (AutoRotate) { + Rotate.vy += 8; // Pan + Rotate.vx += 8; // Tilt + //~ Rotate.vz += 8; // Roll + } + + + // Clear the current OT + ClearOTagR(ot[db], OTLEN); + + // Convert and set the matrixes + RotMatrix(&Rotate, &Matrix); + TransMatrix(&Matrix, &Trans); + ScaleMatrix(&Matrix, &Scale); + + SetRotMatrix(&Matrix); + SetTransMatrix(&Matrix); + + + // Render the sample vector model + t=0; + + // modelCube is a TMESH, len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ... + for (i = 0; i < (modelCube.len*3); i += 3) { + + poly = (POLY_GT3 *)nextpri; + + // Initialize the primitive and set its color values + + SetPolyGT3(poly); + + ((POLY_GT3 *)poly)->tpage = getTPage(tim_cube.mode&0x3, 0, + tim_cube.prect->x, + tim_cube.prect->y + ); + + setRGB0(poly, modelCube.c[i].r , modelCube.c[i].g , modelCube.c[i].b); + setRGB1(poly, modelCube.c[i+2].r, modelCube.c[i+2].g, modelCube.c[i+2].b); + setRGB2(poly, modelCube.c[i+1].r, modelCube.c[i+1].g, modelCube.c[i+1].b); + + setUV3(poly, 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 + + OTz = RotTransPers(&modelCube_mesh[modelCube_index[t]] , (long*)&poly->x0, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+2]], (long*)&poly->x1, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+1]], (long*)&poly->x2, &p, &Flag); + + // Sort the primitive into the OT + OTz /= 3; + if ((OTz > 0) && (OTz < OTLEN)) + AddPrim(&ot[db][OTz-2], poly); + + nextpri += sizeof(POLY_GT3); + + t+=3; + + } + + dr_mode = (DR_MODE *)nextpri; + + setDrawMode(dr_mode,1,0, getTPage(tim_cube.mode&0x3, 0, + tim_cube.prect->x, + tim_cube.prect->y), &tws); //set texture window + + AddPrim(&ot[db], dr_mode); + + nextpri += sizeof(DR_MODE); + + + FntPrint("Hello textured cube!\n"); + + + FntFlush(-1); + + display(); + + } + return 0; +} diff --git a/hello_gt.jpg b/hello_gt.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b4004fb018960a5f032244f078d3bc93abced905 GIT binary patch literal 13901 zcmeHtcTiK?_b+NQZC%N#F#6 z0YYd2rFVo7T1b#ifF$%@9``r%X5O3m&F{YV{(SRW=ggUXc3Gdb*Iu8!෫ga2E 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 +#include +#include +#include +#include +// OldWorld PsyQ has a inline_c.h file for inline GTE functions. We have to use the one at https://github.com/grumpycoders/pcsx-redux/blob/07f9b02d1dbb68f57a9f5b9773041813c55a4913/src/mips/psyq/include/inline_n.h +// because the real GTE commands are needed in nugget : https://psx-spx.consoledev.net/geometrytransformationenginegte/#gte-coordinate-calculation-commands +#include +// RAM -> CPU and CPU -> GTE macros : +#include "CPUMAC.H" + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256 +#define CENTERX ( SCREENXRES >> 1 ) // Center of screen on x +#define CENTERY ( SCREENYRES >> 1 ) // Center of screen on y +#define MARGINX 0 // margins for text display +#define MARGINY 32 +#define FONTSIZE 8 * 7 // Text Field Height +#define OTLEN 10 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] +short db = 0; // index of which buffer is used, values 0, 1 + +// DCache setup +#define dc_camdirp ((sshort*) getScratchAddr(0)) +#define dc_ip ((uchar*) getScratchAddr(1)) +#define dc_opzp ((slong*) getScratchAddr(2)) +#define dc_wmatp ((MATRIX*) getScratchAddr(3)) +#define dc_cmatp ((MATRIX*) getScratchAddr(9)) +#define dc_sxytbl ((DVECTOR*) getScratchAddr(15)) + +void init(void) +{ + ResetGraph(0); + // Initialize and setup the GTE + InitGeom(); + //~ SetGeomOffset(CENTERX,CENTERY); + gte_SetGeomOffset(CENTERX,CENTERY); + gte_SetGeomScreen(CENTERX); + // Set display environment + SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES); + SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); + // Set draw environment + SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); + SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES); + // If PAL, use 320x256, hence 256 - 240 = 16 / 2 = 8 px vertical offset + if (VMODE) + { + SetVideoMode(MODE_PAL); + disp[0].screen.y += 8; + disp[1].screen.y += 8; + } + SetDispMask(1); + + // Set background color + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + draw[0].isbg = 1; + draw[1].isbg = 1; + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); +} + +void display(void) +{ + // Wait for drawing + DrawSync(0); + // Wait for vsync + VSync(1); + // Flip DISP and DRAW env + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + //~ SetDispMask(1); + DrawOTag(ot[db] + OTLEN - 1); + // Flip db index + db = !db; + // Get next primitive in buffer + nextpri = primbuff[db]; +} + +int main(void) +{ + long p, flag, OTz; + SVECTOR rotVector = {0}; + SVECTOR rotVector4 = {0}; // Initialize rotation vector {x, y, z} - ALWAYS ! + VECTOR transVector = {0, 0, CENTERX, 0}; // Initialize translation vector {x, y, z} + SVECTOR vertPos[4] = { + { 0, -32, 0, 0 }, // Vert 1 + { 32, 0, 0, 0 }, // Vert 2 + { -32, 0, 0, 0 }, + { 0, 32, 0, 0 } + }; // Vert 3 + MATRIX workMatrix = {0}; + POLY_F3 * poly = {0}; // pointer to a POLY_F4 + POLY_F4 * poly4 = {0}; // pointer to a POLY_F4 + init(); + + // Declare registers + register ulong ur0 asm("$16"); + register ulong ur1 asm("$17"); + register ulong ur2 asm("$18"); + register ulong ur3 asm("$19"); + register ulong ur4 asm("$20"); + register ulong ur5 asm("$21"); + + while (1) + { + // Set Ordering table + ClearOTagR(ot[db], OTLEN); + // Cast next primitives in buffer as a POLY_F3 and a POLY_F4 (see display() ) + poly = (POLY_F3 *)nextpri; + nextpri += sizeof(POLY_F3); + poly4 = (POLY_F4 *)nextpri; + // Set matrices - Move to left of screen + // Draw on the left part of the screen + transVector.vx = -CENTERX/2; + // Increment rotation angle on Y axis + rotVector.vy += 1; + // Find rotation matrix from vector, store in + RotMatrix_gte(&rotVector, &workMatrix); + // Ditto for translation + TransMatrix(&workMatrix, &transVector); + // Set the matrices we just found + gte_SetRotMatrix(&workMatrix); + gte_SetTransMatrix(&workMatrix); + // Draw a Tri and a Quad + // Copy Tri vertices from ram to cpu registers casting as ulong so that ur0 (len 32bits) contains vx and vy (2 * 8bits) + // Hence the use of vx, vz members + cpu_ldr(ur0,(ulong*)&vertPos[0].vx); // Put vx, vy value in ur0 + cpu_ldr(ur1,(ulong*)&vertPos[0].vz); // Put vz, pad value in ur1 + cpu_ldr(ur2,(ulong*)&vertPos[1].vx); + cpu_ldr(ur3,(ulong*)&vertPos[1].vz); + cpu_ldr(ur4,(ulong*)&vertPos[2].vx); + cpu_ldr(ur5,(ulong*)&vertPos[2].vz); + // Load the gte registers from the cpu registers (gte-cpu move 1 cycle) - mtc2 %0, $0; + cpu_gted0(ur0); + cpu_gted1(ur1); + cpu_gted2(ur2); + cpu_gted3(ur3); + cpu_gted4(ur4); + cpu_gted5(ur5); + // Tri RotTransPers3 + // The two last cpu->gte copy will happen during the 2 nops in gte_rtpt() + gte_rtpt(); + // Fill the cpu registers with the Quad vertices + cpu_ldr(ur0,(ulong*)&vertPos[0].vx); + cpu_ldr(ur1,(ulong*)&vertPos[0].vz); + cpu_ldr(ur2,(ulong*)&vertPos[1].vx); + cpu_ldr(ur3,(ulong*)&vertPos[1].vz); + cpu_ldr(ur4,(ulong*)&vertPos[2].vx); + cpu_ldr(ur5,(ulong*)&vertPos[2].vz); + // Get nclip value, and win two cycles + gte_nclip(); + // Copy Tri 's screen coordinates from gte registers to d-cache. + gte_stsxy3c(&dc_sxytbl[0]); + // Set matrices - Move to right of screen + transVector.vx = CENTERX/2; + // Increment rot on X/Y axis + rotVector4.vy -= 1 ; + rotVector4.vx -= 1 ; + // Set matrices + RotMatrix_gte(&rotVector4, &workMatrix); + TransMatrix(&workMatrix, &transVector); + gte_SetRotMatrix(&workMatrix); + gte_SetTransMatrix(&workMatrix); + // Load the gte registers from the cpu registers (gte-cpu move 1 cycle) - mtc2 %0, $0; + cpu_gted0(ur0); + cpu_gted1(ur1); + cpu_gted2(ur2); + cpu_gted3(ur3); + cpu_gted4(ur4); + cpu_gted5(ur5); + // Quad RotTransPers3 + // Getting 2 cycles back thanks to nops + gte_rtpt(); + // gte_nclip() has 2 nops, lets use them to load the remaining vertex data from ram->cpu register + cpu_ldr(ur0,(ulong*)&vertPos[3].vx); + cpu_ldr(ur1,(ulong*)&vertPos[3].vz); + // Calculate nclip (outer product) + gte_nclip(); + // Copy result to d-cache + 3 + gte_stsxy3c(&dc_sxytbl[3]); + // Copy from cpu-gte + cpu_gted0(ur0); + cpu_gted1(ur1); + // Quad last vertex RotTransPers + // These two last cpu->gte load are free :p + gte_rtps(); + gte_nclip(); + // Copy last vertex value to d-cache + gte_stsxy(&dc_sxytbl[6]); + // Get p, flag, OTz + gte_stdp(&p); + gte_stflg(&flag); + gte_stszotz(&OTz); + // That's 10 cycles we won back ? + // Copy vertices data from d-cache to ram + // Tri + *(unsigned long long*)&poly->x0 = *(unsigned long long*)&dc_sxytbl[0]; + *(ulong*)&poly->x2 = *(ulong*)&dc_sxytbl[2]; + // Quad + *(unsigned long long*)&poly4->x0 = *(unsigned long long*)&dc_sxytbl[3]; + *(unsigned long long*)&poly4->x2 = *(unsigned long long*)&dc_sxytbl[5]; + // Initialize polygons + setPolyF3(poly); + setRGB0(poly, 255, 0, 255); + setPolyF4(poly4); + setRGB0(poly4, 0, 255, 255); + // Add to OT + addPrim(ot[db], poly); + addPrim(ot[db], poly4); + // Display text + FntPrint("Hello Free cycles !\n"); + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_gte_opti/hello_gte_opti.c b/hello_gte_opti/hello_gte_opti.c new file mode 100644 index 0000000..cb956ef --- /dev/null +++ b/hello_gte_opti/hello_gte_opti.c @@ -0,0 +1,239 @@ +// Hello free cycles ! +// +// Ref : /psyq/DOCS/Devrefs/Inlinref.pdf, p.18 +// /psyq/psx/sample/scea/GTE +// https://psx-spx.consoledev.net/geometrytransformationenginegte/ +// PSX / Z+ +// screen / +//coordinate +-----X+ +//system / | +// eye | Y+ +// +// Credits, thanks : Nicolas Noble, Sickle, Lameguy64 @ psxdev discord : https://discord.com/invite/N2mmwp +// https://discord.com/channels/642647820683444236/663664210525290507/834831466100949002 +#include +#include +#include +#include +#include +// OldWorld PsyQ has a inline_c.h file for inline GTE functions. We have to use the one at https://github.com/grumpycoders/pcsx-redux/blob/07f9b02d1dbb68f57a9f5b9773041813c55a4913/src/mips/psyq/include/inline_n.h +// because the real GTE commands are needed in nugget : https://psx-spx.consoledev.net/geometrytransformationenginegte/#gte-coordinate-calculation-commands +#include +// RAM -> CPU and CPU -> GTE macros : +#include "../includes/CPUMAC.H" + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256 +#define CENTERX ( SCREENXRES >> 1 ) // Center of screen on x +#define CENTERY ( SCREENYRES >> 1 ) // Center of screen on y +#define MARGINX 0 // margins for text display +#define MARGINY 32 +#define FONTSIZE 8 * 7 // Text Field Height +#define OTLEN 10 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] +short db = 0; // index of which buffer is used, values 0, 1 + +// DCache setup +#define dc_camdirp ((sshort*) getScratchAddr(0)) +#define dc_ip ((uchar*) getScratchAddr(1)) +#define dc_opzp ((slong*) getScratchAddr(2)) +#define dc_wmatp ((MATRIX*) getScratchAddr(3)) +#define dc_cmatp ((MATRIX*) getScratchAddr(9)) +#define dc_sxytbl ((DVECTOR*) getScratchAddr(15)) + +void init(void) +{ + ResetGraph(0); + // Initialize and setup the GTE + InitGeom(); + //~ SetGeomOffset(CENTERX,CENTERY); + gte_SetGeomOffset(CENTERX,CENTERY); + gte_SetGeomScreen(CENTERX); + // Set display environment + SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES); + SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); + // Set draw environment + SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); + SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES); + // If PAL, use 320x256, hence 256 - 240 = 16 / 2 = 8 px vertical offset + if (VMODE) + { + SetVideoMode(MODE_PAL); + disp[0].screen.y += 8; + disp[1].screen.y += 8; + } + SetDispMask(1); + + // Set background color + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + draw[0].isbg = 1; + draw[1].isbg = 1; + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); +} + +void display(void) +{ + // Wait for drawing + DrawSync(0); + // Wait for vsync + VSync(1); + // Flip DISP and DRAW env + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + //~ SetDispMask(1); + DrawOTag(ot[db] + OTLEN - 1); + // Flip db index + db = !db; + // Get next primitive in buffer + nextpri = primbuff[db]; +} + +int main(void) +{ + long p, flag, OTz; + SVECTOR rotVector = {0}; + SVECTOR rotVector4 = {0}; // Initialize rotation vector {x, y, z} - ALWAYS ! + VECTOR transVector = {0, 0, CENTERX, 0}; // Initialize translation vector {x, y, z} + SVECTOR vertPos[4] = { + { 0, -32, 0, 0 }, // Vert 1 + { 32, 0, 0, 0 }, // Vert 2 + { -32, 0, 0, 0 }, + { 0, 32, 0, 0 } + }; // Vert 3 + MATRIX workMatrix = {0}; + POLY_F3 * poly = {0}; // pointer to a POLY_F4 + POLY_F4 * poly4 = {0}; // pointer to a POLY_F4 + init(); + + // Declare registers + register ulong ur0 asm("$16"); + register ulong ur1 asm("$17"); + register ulong ur2 asm("$18"); + register ulong ur3 asm("$19"); + register ulong ur4 asm("$20"); + register ulong ur5 asm("$21"); + + while (1) + { + // Set Ordering table + ClearOTagR(ot[db], OTLEN); + // Cast next primitives in buffer as a POLY_F3 and a POLY_F4 (see display() ) + poly = (POLY_F3 *)nextpri; + nextpri += sizeof(POLY_F3); + poly4 = (POLY_F4 *)nextpri; + // Set matrices - Move to left of screen + // Draw on the left part of the screen + transVector.vx = -CENTERX/2; + // Increment rotation angle on Y axis + rotVector.vy += 1; + // Find rotation matrix from vector, store in + RotMatrix_gte(&rotVector, &workMatrix); + // Ditto for translation + TransMatrix(&workMatrix, &transVector); + // Set the matrices we just found + gte_SetRotMatrix(&workMatrix); + gte_SetTransMatrix(&workMatrix); + // Draw a Tri and a Quad + // Copy Tri vertices from ram to cpu registers casting as ulong so that ur0 (len 32bits) contains vx and vy (2 * 8bits) + // Hence the use of vx, vz members + cpu_ldr(ur0,(ulong*)&vertPos[0].vx); // Put vx, vy value in ur0 + cpu_ldr(ur1,(ulong*)&vertPos[0].vz); // Put vz, pad value in ur1 + cpu_ldr(ur2,(ulong*)&vertPos[1].vx); + cpu_ldr(ur3,(ulong*)&vertPos[1].vz); + cpu_ldr(ur4,(ulong*)&vertPos[2].vx); + cpu_ldr(ur5,(ulong*)&vertPos[2].vz); + // Load the gte registers from the cpu registers (gte-cpu move 1 cycle) - mtc2 %0, $0; + cpu_gted0(ur0); + cpu_gted1(ur1); + cpu_gted2(ur2); + cpu_gted3(ur3); + cpu_gted4(ur4); + cpu_gted5(ur5); + // Tri RotTransPers3 + // The two last cpu->gte copy will happen during the 2 nops in gte_rtpt() + gte_rtpt(); + // Fill the cpu registers with the Quad vertices + cpu_ldr(ur0,(ulong*)&vertPos[0].vx); + cpu_ldr(ur1,(ulong*)&vertPos[0].vz); + cpu_ldr(ur2,(ulong*)&vertPos[1].vx); + cpu_ldr(ur3,(ulong*)&vertPos[1].vz); + cpu_ldr(ur4,(ulong*)&vertPos[2].vx); + cpu_ldr(ur5,(ulong*)&vertPos[2].vz); + // Get nclip value, and win two cycles + gte_nclip(); + // Copy Tri 's screen coordinates from gte registers to d-cache. + gte_stsxy3c(&dc_sxytbl[0]); + // Set matrices - Move to right of screen + transVector.vx = CENTERX/2; + // Increment rot on X/Y axis + rotVector4.vy -= 1 ; + rotVector4.vx -= 1 ; + // Set matrices + RotMatrix_gte(&rotVector4, &workMatrix); + TransMatrix(&workMatrix, &transVector); + gte_SetRotMatrix(&workMatrix); + gte_SetTransMatrix(&workMatrix); + // Load the gte registers from the cpu registers (gte-cpu move 1 cycle) - mtc2 %0, $0; + cpu_gted0(ur0); + cpu_gted1(ur1); + cpu_gted2(ur2); + cpu_gted3(ur3); + cpu_gted4(ur4); + cpu_gted5(ur5); + // Quad RotTransPers3 + // Getting 2 cycles back thanks to nops + gte_rtpt(); + // gte_nclip() has 2 nops, lets use them to load the remaining vertex data from ram->cpu register + cpu_ldr(ur0,(ulong*)&vertPos[3].vx); + cpu_ldr(ur1,(ulong*)&vertPos[3].vz); + // Calculate nclip (outer product) + gte_nclip(); + // Copy result to d-cache + 3 + gte_stsxy3c(&dc_sxytbl[3]); + // Copy from cpu-gte + cpu_gted0(ur0); + cpu_gted1(ur1); + // Quad last vertex RotTransPers + // These two last cpu->gte load are free :p + gte_rtps(); + gte_nclip(); + // Copy last vertex value to d-cache + gte_stsxy(&dc_sxytbl[6]); + // Get p, flag, OTz + gte_stdp(&p); + gte_stflg(&flag); + gte_stszotz(&OTz); + // That's 10 cycles we won back ? + // Copy vertices data from d-cache to ram + // Tri + *(unsigned long long*)&poly->x0 = *(unsigned long long*)&dc_sxytbl[0]; + *(ulong*)&poly->x2 = *(ulong*)&dc_sxytbl[2]; + // Quad + *(unsigned long long*)&poly4->x0 = *(unsigned long long*)&dc_sxytbl[3]; + *(unsigned long long*)&poly4->x2 = *(unsigned long long*)&dc_sxytbl[5]; + // Initialize polygons + setPolyF3(poly); + setRGB0(poly, 255, 0, 255); + setPolyF4(poly4); + setRGB0(poly4, 0, 255, 255); + // Add to OT + addPrim(ot[db], poly); + addPrim(ot[db], poly4); + // Display text + FntPrint("Hello Free cycles !\n"); + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_light.c b/hello_light.c new file mode 100644 index 0000000..a7cb2b9 --- /dev/null +++ b/hello_light.c @@ -0,0 +1,275 @@ +/* hello_light.c, by Schnappy, 06-2021 + - Demonstrates setting and using light sources in 3D without libgs. + Controls: + Start - Toggle interactive/non-interactive mode. + Select - Reset object's position and angles. + L1/L2 - Move object closer/farther. + L2/R2 - Rotate object (XY). + Up/Down/Left/Right - Rotate object (XZ/YZ). + Triangle/Cross/Square/Circle - Move object up/down/left/right. + based on primdraw.c by Lameguy64 (http://www.psxdev.net/forum/viewtopic.php?f=64&t=537) + 2014 Meido-Tek Productions. +*/ + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ +#include +#include +#include +#include +#include +// Sample vector model +#include "cube.c" +#define VMODE 0 +#define SCREENXRES 320 +#define SCREENYRES 240 +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 +#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] = {0}; // Primitive list // That's our prim buffer +char * nextpri = primbuff[0]; // Primitive counter +short db = 0; // Current buffer counter +long t, p, OTz, Flag; // t == vertex count, p == depth cueing interpolation value, OTz == value to create Z-ordered OT, Flag == see LibOver47.pdf, p.143 +// Lighting +// See PsyQ's LibOver47.pdf, p.133 for more details on the purpose of each component and full calculations. +// Far color : This is the color used to fade to when the mesh is far from the cam (NearFog) +CVECTOR BGc = {150, 50, 75, 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. +// Each light points in the direction aligned with the axis, hence direction is in the same coordinate system as the PSX (see l.23-30 of this file) +// Negative/positive value denotes light direction on corresponding axis +// -4096 > Value < 4096 denotes reach/intensity of light source +MATRIX lgtmat = { +// X Y Z + -ONE, -ONE, ONE, // 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 +// Set color of each light source (L) +// Value range : 0 > x < 4096 +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(u_long * 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; + } + // Set light env + // 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( 1200, 2200, SCREENXRES ); + setRGB0(&draw[0], 0, 0, 255); + setRGB0(&draw[1], 0, 0, 255); + draw[0].isbg = 1; + draw[1].isbg = 1; + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + // Init font system + FntLoad(960, 0); + FntOpen(16, 16, 196, 64, 0, 256); + } +void display(void){ + DrawSync(0); + VSync(0); + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + SetDispMask(1); + DrawOTag(ot[db] + OTLEN - 1); + db = !db; + nextpri = primbuff[db]; + } +int main() { + int i; + int PadStatus; + int TPressed=0; + int AutoRotate=1; + // Rotating cube + POLY_G3 * poly; + SVECTOR Rotate={ ONE/6,ONE/6,ONE/6 }; // Rotation coordinates + VECTOR Trans={ -SCREENXRES/2, 0, CENTERX * 3, 0 }; // Translation coordinates + VECTOR Scale={ ONE/2, ONE/2, ONE/2, 0 }; // Scaling coordinates : ONE == 4096 + MATRIX Matrix={0}; // Matrix data for the GTE + // Static cube + POLY_G3 * poly1; // pointer to a POLY_G4 + SVECTOR Rotate1={ ONE/6, ONE/6, ONE/6, 0 }; // Rotation coordinates + VECTOR Trans1={ SCREENXRES/2, 0, CENTERX * 3, 0 }; // Translation coordinates + VECTOR Scale1={ ONE/2, ONE/2, ONE/2, 0 }; // Scaling coordinates : ONE == 4096 + MATRIX Matrix1={0}; // Matrix data for the GTE + init(); + // Main loop + while (1) { + // Read pad status + PadStatus = PadRead(0); + if (AutoRotate == 0) { + if (PadStatus & PADL1) Trans.vz -= 4; + if (PadStatus & PADR1) Trans.vz += 4; + if (PadStatus & PADL2) Rotate.vz -= 8; + if (PadStatus & PADR2) Rotate.vz += 8; + if (PadStatus & PADLup) Rotate.vx -= 8; + if (PadStatus & PADLdown) Rotate.vx += 8; + if (PadStatus & PADLleft) Rotate.vy -= 8; + if (PadStatus & PADLright) Rotate.vy += 8; + if (PadStatus & PADRup) Trans.vy -= 2; + if (PadStatus & PADRdown) Trans.vy += 2; + if (PadStatus & PADRleft) Trans.vx -= 2; + if (PadStatus & PADRright) Trans.vx += 2; + } + if (PadStatus & PADstart) { + if (TPressed == 0) { + AutoRotate = (AutoRotate + 1) & 1; + Rotate.vy = Rotate.vx = Rotate.vz = ONE/6; + Scale.vx = Scale.vy = Scale.vz = ONE/2; + Trans.vx = -SCREENXRES/2; + Trans.vy = 0; + Trans.vz = CENTERX * 3; + } + TPressed = 1; + } else { + TPressed = 0; + } + if (AutoRotate) { + Rotate.vy += 8; // Pan + Rotate.vx += 8; // Tilt + //~ Rotate.vz += 8; // Roll + } + // Clear the current OT + ClearOTagR(ot[db], OTLEN); + // Render the sample vector model + t=0; + // modelCube is a TMESH, len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ... + for (i = 0; i < (modelCube.len*3); i += 3) { + poly = (POLY_G3 *)nextpri; + // Initialize the primitive and set its color values + SetPolyG3(poly); + // Rotate, translate, and project the vectors and output the results into a primitive + // Could be replaced with one call with RotTransPers3() + OTz = RotTransPers(&modelCube_mesh[modelCube_index[t]] , (long*)&poly->x0, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+2]], (long*)&poly->x1, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+1]], (long*)&poly->x2, &p, &Flag); + // Find light color + // Work color vectors + CVECTOR outCol, outCol1, outCol2 = { 0,0,0,0 }; + // Find local color from three normal vectors and perform depth cueing. + // Could be replaced with one call with NormalColorDpq3() + NormalColorDpq(&modelCube.n[ modelCube_index[t+0] ], &modelCube.c[i+0], p, &outCol); + NormalColorDpq(&modelCube.n[ modelCube_index[t+2] ], &modelCube.c[i+2], p, &outCol1); + NormalColorDpq(&modelCube.n[ modelCube_index[t+1] ], &modelCube.c[i+1], p, &outCol2); + // Set vertex colors + setRGB0(poly, outCol.r, outCol.g , outCol.b); + setRGB1(poly, outCol1.r, outCol1.g, outCol1.b); + setRGB2(poly, outCol2.r, outCol2.g, outCol2.b); + // Sort the primitive into the OT + OTz /= 3; + if ((OTz > 0) && (OTz < OTLEN)) + AddPrim(&ot[db][OTz-2], poly); + nextpri += sizeof(POLY_G3); + t+=3; + } + // Find and apply light rotation matrix + //~ // Find rotmat from light angles + RotMatrix_gte(&lgtang, &rotlgt); + // Find rotmat from cube angles + RotMatrix_gte(&Rotate, &rotcube); + // RotMatrix cube * RotMatrix light + MulMatrix0(&rotcube, &rotlgt, &rotlgt); + // Light Matrix * RotMatrix light + MulMatrix0(&lgtmat, &rotlgt, &light); + // Set new light matrix + SetLightMatrix(&light); + // Convert and set the matrices + // Find Rotation matrix from object's angles + RotMatrix(&Rotate, &Matrix); + // Find Scale matrix from object's angles + ScaleMatrix(&Matrix, &Scale); + // Find Translation matrix from object's angles + TransMatrix(&Matrix, &Trans); + // Set GTE's rotation matrix + SetRotMatrix(&Matrix); + // Set GTE's Translation matrix + SetTransMatrix(&Matrix); + // Draw static cube + t=0; + for (i = 0; i < (modelCube1.len*3); i += 3) { + poly1 = (POLY_G3 *)nextpri; + SetPolyG3(poly1); + OTz = RotTransPers(&modelCube1_mesh[modelCube1_index[t]] , (long*)&poly1->x0, &p, &Flag); + OTz += RotTransPers(&modelCube1_mesh[modelCube1_index[t+2]], (long*)&poly1->x1, &p, &Flag); + OTz += RotTransPers(&modelCube1_mesh[modelCube1_index[t+1]], (long*)&poly1->x2, &p, &Flag); + CVECTOR outCol = { 0,0,0,0 }; + CVECTOR outCol1 = { 0,0,0,0 }; + CVECTOR outCol2 = { 0,0,0,0 }; + NormalColorDpq(&modelCube1.n[ modelCube1_index[t+0] ], &modelCube1.c[i+0], p, &outCol); + NormalColorDpq(&modelCube1.n[ modelCube1_index[t+2] ], &modelCube1.c[i+2], p, &outCol1); + NormalColorDpq(&modelCube1.n[ modelCube1_index[t+1] ], &modelCube1.c[i+1], p, &outCol2); + setRGB0(poly1, outCol.r, outCol.g , outCol.b); + setRGB1(poly1, outCol1.r, outCol1.g, outCol1.b); + setRGB2(poly1, outCol2.r, outCol2.g, outCol2.b); + OTz /= 3; + if ((OTz > 0) && (OTz < OTLEN)) + AddPrim(&ot[db][OTz-2], poly1); + nextpri += sizeof(POLY_G3); + t+=3; + } + // See l.216 + RotMatrix_gte(&lgtang, &rotlgt); + RotMatrix_gte(&Rotate1, &rotcube); + MulMatrix0(&rotcube, &rotlgt, &rotlgt); + MulMatrix0(&lgtmat, &rotlgt, &light); + SetLightMatrix(&light); + // See l.227 + RotMatrix(&Rotate1, &Matrix1); + ScaleMatrix(&Matrix1, &Scale1); + TransMatrix(&Matrix1, &Trans1); + SetRotMatrix(&Matrix1); + SetTransMatrix(&Matrix1); + FntPrint("Hello lightsources !\n"); + FntFlush(-1); + display(); + } + return 0; +} diff --git a/hello_light/hello_light.c b/hello_light/hello_light.c new file mode 100644 index 0000000..a02c341 --- /dev/null +++ b/hello_light/hello_light.c @@ -0,0 +1,276 @@ +/* hello_light.c, by Schnappy, 06-2021 + - Demonstrates setting and using light sources in 3D without libgs. + Controls: + Start - Toggle interactive/non-interactive mode. + Select - Reset object's position and angles. + L1/L2 - Move object closer/farther. + L2/R2 - Rotate object (XY). + Up/Down/Left/Right - Rotate object (XZ/YZ). + Triangle/Cross/Square/Circle - Move object up/down/left/right. + based on primdraw.c by Lameguy64 (http://www.psxdev.net/forum/viewtopic.php?f=64&t=537) + 2014 Meido-Tek Productions. +*/ + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ +#include +#include +#include +#include +#include +// Sample vector model +#include "../includes/cube.c" + +#define VMODE 0 +#define SCREENXRES 320 +#define SCREENYRES 240 +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 +#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] = {0}; // Primitive list // That's our prim buffer +char * nextpri = primbuff[0]; // Primitive counter +short db = 0; // Current buffer counter +long t, p, OTz, Flag; // t == vertex count, p == depth cueing interpolation value, OTz == value to create Z-ordered OT, Flag == see LibOver47.pdf, p.143 +// Lighting +// See PsyQ's LibOver47.pdf, p.133 for more details on the purpose of each component and full calculations. +// Far color : This is the color used to fade to when the mesh is far from the cam (NearFog) +CVECTOR BGc = {150, 50, 75, 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. +// Each light points in the direction aligned with the axis, hence direction is in the same coordinate system as the PSX (see l.23-30 of this file) +// Negative/positive value denotes light direction on corresponding axis +// -4096 > Value < 4096 denotes reach/intensity of light source +MATRIX lgtmat = { +// X Y Z + -ONE, -ONE, ONE, // 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 +// Set color of each light source (L) +// Value range : 0 > x < 4096 +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(u_long * 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; + } + // Set light env + // 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( 1200, 2200, SCREENXRES ); + setRGB0(&draw[0], 0, 0, 255); + setRGB0(&draw[1], 0, 0, 255); + draw[0].isbg = 1; + draw[1].isbg = 1; + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + // Init font system + FntLoad(960, 0); + FntOpen(16, 16, 196, 64, 0, 256); + } +void display(void){ + DrawSync(0); + VSync(0); + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + SetDispMask(1); + DrawOTag(ot[db] + OTLEN - 1); + db = !db; + nextpri = primbuff[db]; + } +int main() { + int i; + int PadStatus; + int TPressed=0; + int AutoRotate=1; + // Rotating cube + POLY_G3 * poly; + SVECTOR Rotate={ ONE/6,ONE/6,ONE/6 }; // Rotation coordinates + VECTOR Trans={ -SCREENXRES/2, 0, CENTERX * 3, 0 }; // Translation coordinates + VECTOR Scale={ ONE/2, ONE/2, ONE/2, 0 }; // Scaling coordinates : ONE == 4096 + MATRIX Matrix={0}; // Matrix data for the GTE + // Static cube + POLY_G3 * poly1; // pointer to a POLY_G4 + SVECTOR Rotate1={ ONE/6, ONE/6, ONE/6, 0 }; // Rotation coordinates + VECTOR Trans1={ SCREENXRES/2, 0, CENTERX * 3, 0 }; // Translation coordinates + VECTOR Scale1={ ONE/2, ONE/2, ONE/2, 0 }; // Scaling coordinates : ONE == 4096 + MATRIX Matrix1={0}; // Matrix data for the GTE + init(); + // Main loop + while (1) { + // Read pad status + PadStatus = PadRead(0); + if (AutoRotate == 0) { + if (PadStatus & PADL1) Trans.vz -= 4; + if (PadStatus & PADR1) Trans.vz += 4; + if (PadStatus & PADL2) Rotate.vz -= 8; + if (PadStatus & PADR2) Rotate.vz += 8; + if (PadStatus & PADLup) Rotate.vx -= 8; + if (PadStatus & PADLdown) Rotate.vx += 8; + if (PadStatus & PADLleft) Rotate.vy -= 8; + if (PadStatus & PADLright) Rotate.vy += 8; + if (PadStatus & PADRup) Trans.vy -= 2; + if (PadStatus & PADRdown) Trans.vy += 2; + if (PadStatus & PADRleft) Trans.vx -= 2; + if (PadStatus & PADRright) Trans.vx += 2; + } + if (PadStatus & PADstart) { + if (TPressed == 0) { + AutoRotate = (AutoRotate + 1) & 1; + Rotate.vy = Rotate.vx = Rotate.vz = ONE/6; + Scale.vx = Scale.vy = Scale.vz = ONE/2; + Trans.vx = -SCREENXRES/2; + Trans.vy = 0; + Trans.vz = CENTERX * 3; + } + TPressed = 1; + } else { + TPressed = 0; + } + if (AutoRotate) { + Rotate.vy += 8; // Pan + Rotate.vx += 8; // Tilt + //~ Rotate.vz += 8; // Roll + } + // Clear the current OT + ClearOTagR(ot[db], OTLEN); + // Render the sample vector model + t=0; + // modelCube is a TMESH, len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ... + for (i = 0; i < (modelCube.len*3); i += 3) { + poly = (POLY_G3 *)nextpri; + // Initialize the primitive and set its color values + SetPolyG3(poly); + // Rotate, translate, and project the vectors and output the results into a primitive + // Could be replaced with one call with RotTransPers3() + OTz = RotTransPers(&modelCube_mesh[modelCube_index[t]] , (long*)&poly->x0, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+2]], (long*)&poly->x1, &p, &Flag); + OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+1]], (long*)&poly->x2, &p, &Flag); + // Find light color + // Work color vectors + CVECTOR outCol, outCol1, outCol2 = { 0,0,0,0 }; + // Find local color from three normal vectors and perform depth cueing. + // Could be replaced with one call with NormalColorDpq3() + NormalColorDpq(&modelCube.n[ modelCube_index[t+0] ], &modelCube.c[i+0], p, &outCol); + NormalColorDpq(&modelCube.n[ modelCube_index[t+2] ], &modelCube.c[i+2], p, &outCol1); + NormalColorDpq(&modelCube.n[ modelCube_index[t+1] ], &modelCube.c[i+1], p, &outCol2); + // Set vertex colors + setRGB0(poly, outCol.r, outCol.g , outCol.b); + setRGB1(poly, outCol1.r, outCol1.g, outCol1.b); + setRGB2(poly, outCol2.r, outCol2.g, outCol2.b); + // Sort the primitive into the OT + OTz /= 3; + if ((OTz > 0) && (OTz < OTLEN)) + AddPrim(&ot[db][OTz-2], poly); + nextpri += sizeof(POLY_G3); + t+=3; + } + // Find and apply light rotation matrix + //~ // Find rotmat from light angles + RotMatrix_gte(&lgtang, &rotlgt); + // Find rotmat from cube angles + RotMatrix_gte(&Rotate, &rotcube); + // RotMatrix cube * RotMatrix light + MulMatrix0(&rotcube, &rotlgt, &rotlgt); + // Light Matrix * RotMatrix light + MulMatrix0(&lgtmat, &rotlgt, &light); + // Set new light matrix + SetLightMatrix(&light); + // Convert and set the matrices + // Find Rotation matrix from object's angles + RotMatrix(&Rotate, &Matrix); + // Find Scale matrix from object's angles + ScaleMatrix(&Matrix, &Scale); + // Find Translation matrix from object's angles + TransMatrix(&Matrix, &Trans); + // Set GTE's rotation matrix + SetRotMatrix(&Matrix); + // Set GTE's Translation matrix + SetTransMatrix(&Matrix); + // Draw static cube + t=0; + for (i = 0; i < (modelCube1.len*3); i += 3) { + poly1 = (POLY_G3 *)nextpri; + SetPolyG3(poly1); + OTz = RotTransPers(&modelCube1_mesh[modelCube1_index[t]] , (long*)&poly1->x0, &p, &Flag); + OTz += RotTransPers(&modelCube1_mesh[modelCube1_index[t+2]], (long*)&poly1->x1, &p, &Flag); + OTz += RotTransPers(&modelCube1_mesh[modelCube1_index[t+1]], (long*)&poly1->x2, &p, &Flag); + CVECTOR outCol = { 0,0,0,0 }; + CVECTOR outCol1 = { 0,0,0,0 }; + CVECTOR outCol2 = { 0,0,0,0 }; + NormalColorDpq(&modelCube1.n[ modelCube1_index[t+0] ], &modelCube1.c[i+0], p, &outCol); + NormalColorDpq(&modelCube1.n[ modelCube1_index[t+2] ], &modelCube1.c[i+2], p, &outCol1); + NormalColorDpq(&modelCube1.n[ modelCube1_index[t+1] ], &modelCube1.c[i+1], p, &outCol2); + setRGB0(poly1, outCol.r, outCol.g , outCol.b); + setRGB1(poly1, outCol1.r, outCol1.g, outCol1.b); + setRGB2(poly1, outCol2.r, outCol2.g, outCol2.b); + OTz /= 3; + if ((OTz > 0) && (OTz < OTLEN)) + AddPrim(&ot[db][OTz-2], poly1); + nextpri += sizeof(POLY_G3); + t+=3; + } + // See l.216 + RotMatrix_gte(&lgtang, &rotlgt); + RotMatrix_gte(&Rotate1, &rotcube); + MulMatrix0(&rotcube, &rotlgt, &rotlgt); + MulMatrix0(&lgtmat, &rotlgt, &light); + SetLightMatrix(&light); + // See l.227 + RotMatrix(&Rotate1, &Matrix1); + ScaleMatrix(&Matrix1, &Scale1); + TransMatrix(&Matrix1, &Trans1); + SetRotMatrix(&Matrix1); + SetTransMatrix(&Matrix1); + FntPrint("Hello lightsources !\n"); + FntFlush(-1); + display(); + } + return 0; +} diff --git a/hello_multivag.c b/hello_multivag.c new file mode 100644 index 0000000..ef9d87c --- /dev/null +++ b/hello_multivag.c @@ -0,0 +1,320 @@ +// VAGDEMO2020 by Schnappy +// December 2020 +// Based on VAGDEMO_FIXED by Yagotzirck +// Based on VAGDEMO by Shadow +// based on psyq/addons/sound/TUTO3.C +// +// +// Load two VAG file to SPU sound buffer and play them back alternatively or simultaneously. +// +// WAV creation: use ffmpeg to create a 16-bit ADPCM mono WAV file - change -ar to reduce filesize (and quality) +// $ ffmpeg -i input.mp3 -acodec pcm_s16le -ac 1 -ar 44100 output.wav +// +// WAV to VAG convertion using WAV2VAG : https://github.com/ColdSauce/psxsdk/blob/master/tools/wav2vag.c +// change -freq according to the -ar setting above +// $ wav2vag input.wav output.vag -sraw16 -freq=44100 (-L) +// +// Alternatively, you can use PsyQ VAGEDIT.EXE to change the sampling frequency of an existing VAG file. +// +// Docs : see libformat47.pdf p.209 +// libover47.pdf, p.271 +// libref47.pdf, p.980 +// URLS : http://psx.arthus.net/code/VAG/ + +#include +#include + +#include +#include +#include + + +// Sound system +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +short db = 0; // index of which buffer is used, values 0, 1 + +// Sound stuff + +#define MALLOC_MAX 3 // Max number of time we can call SpuMalloc + +//~ // convert Little endian to Big endian +#define SWAP_ENDIAN32(x) (((x)>>24) | (((x)>>8) & 0xFF00) | (((x)<<8) & 0x00FF0000) | ((x)<<24)) + +typedef struct VAGheader{ // All the values in this header must be big endian + char id[4]; // VAGp 4 bytes -> 1 char * 4 + unsigned int version; // 4 bytes + unsigned int reserved; // 4 bytes + unsigned int dataSize; // (in bytes) 4 bytes + unsigned int samplingFrequency;// 4 bytes + char reserved2[12]; // 12 bytes -> 1 char * 12 + char name[16]; // 16 bytes -> 1 char * 16 + // Waveform data after that +}VAGhdr; + +SpuCommonAttr commonAttributes; // structure for changing common voice attributes +SpuVoiceAttr voiceAttributes ; // structure for changing individual voice attributes + +u_long hello_spu_address; // address allocated in memory for first sound file +u_long poly_spu_address; // address allocated in memory for second sound file + +// DEBUG : these allow printing values for debugging + +u_long hello_spu_start_address; +u_long hello_get_start_addr; +u_long hello_transSize; + +u_long poly_spu_start_address; +u_long poly_get_start_addr; +u_long poly_transSize; + +#define HELLO SPU_0CH // Play first vag on channel 0 +#define POLY SPU_2CH // Play second vag on channel 2 + +// Memory management table ; allow MALLOC_MAX calls to SpuMalloc() - ibref47.pdf p.1044 +char spu_malloc_rec[SPU_MALLOC_RECSIZ * (2 + MALLOC_MAX+1)]; + +// VAG files + +// We're using GrumpyCoder's Nugget wrapper to compile the code with a modern GCC : https://github.com/grumpycoders/pcsx-redux/tree/main/src/mips/psyq +// To include binary files in the exe, add your VAG files to the SRCS variable in Makefile +// and in common.mk, add this rule to include *.vag files : +// +//~ %.o: %.vag + //~ $(PREFIX)-objcopy -I binary --set-section-alignment .data=4 --rename-section .data=.rodata,alloc,load,readonly,data,contents -O elf32-tradlittlemips -B mips $< $@ + + +// hello.vag - 44100 Khz +extern unsigned char _binary_VAG_hello_vag_start[]; // filename must begin with _binary_ followed by the full path, with . and / replaced, and then suffixed with _ and end with _start[]; or end[]; +extern unsigned char _binary_VAG_hello_vag_end[]; // https://discord.com/channels/642647820683444236/663664210525290507/780866265077383189 + +// poly.vag - 44100 Khz +extern unsigned char _binary_VAG_poly_vag_start[]; +extern unsigned char _binary_VAG_poly_vag_end[]; + + +void initGraph(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(8, 60, 304, 200, 0, 500 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + db = !db; + +} + + +// Audio initialisation & functions + +void initSnd(void){ + + SpuInitMalloc(MALLOC_MAX, spu_malloc_rec); // Maximum number of blocks, mem. management table address. + + commonAttributes.mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR); // Mask which attributes to set + commonAttributes.mvol.left = 0x3fff; // Master volume left + commonAttributes.mvol.right = 0x3fff; // see libref47.pdf, p.1058 + + SpuSetCommonAttr(&commonAttributes); // set attributes + + SpuSetIRQ(SPU_OFF); +} + +u_long sendVAGtoRAM(unsigned int VAG_data_size, unsigned char *VAG_data){ + u_long size; + + SpuSetTransferMode(SpuTransByDMA); // DMA transfer; can do other processing during transfer + + size = SpuWrite (VAG_data + sizeof(VAGhdr), VAG_data_size); // transfer VAG_data_size bytes from VAG_data address to sound buffer + + SpuIsTransferCompleted (SPU_TRANSFER_WAIT); // Checks whether transfer is completed and waits for completion + + return size; +} + +void setVoiceAttr(unsigned int pitch, long channel, unsigned long soundAddr ){ + + voiceAttributes.mask= //~ Attributes (bit string, 1 bit per attribute) + ( + SPU_VOICE_VOLL | + SPU_VOICE_VOLR | + SPU_VOICE_PITCH | + SPU_VOICE_WDSA | + SPU_VOICE_ADSR_AMODE | + SPU_VOICE_ADSR_SMODE | + SPU_VOICE_ADSR_RMODE | + SPU_VOICE_ADSR_AR | + SPU_VOICE_ADSR_DR | + SPU_VOICE_ADSR_SR | + SPU_VOICE_ADSR_RR | + SPU_VOICE_ADSR_SL + ); + + voiceAttributes.voice = channel; //~ Voice (low 24 bits are a bit string, 1 bit per voice ) + + voiceAttributes.volume.left = 0x1000; //~ Volume + voiceAttributes.volume.right = 0x1000; //~ Volume + + voiceAttributes.pitch = pitch; //~ Interval (set pitch) + voiceAttributes.addr = soundAddr; //~ Waveform data start address + + voiceAttributes.a_mode = SPU_VOICE_LINEARIncN; //~ Attack rate mode = Linear Increase - see libref47.pdf p.1091 + voiceAttributes.s_mode = SPU_VOICE_LINEARIncN; //~ Sustain rate mode = Linear Increase + voiceAttributes.r_mode = SPU_VOICE_LINEARDecN; //~ Release rate mode = Linear Decrease + + voiceAttributes.ar = 0x0; //~ Attack rate + voiceAttributes.dr = 0x0; //~ Decay rate + voiceAttributes.rr = 0x0; //~ Release rate + voiceAttributes.sr = 0x0; //~ Sustain rate + voiceAttributes.sl = 0xf; //~ Sustain level + + SpuSetVoiceAttr(&voiceAttributes); // set attributes + +} + +void playSFX(unsigned long fx){ + SpuSetKey(SpuOn, fx); +} + +int main(void) +{ + short counter = 0; + + const VAGhdr * HellofileHeader = (VAGhdr *) _binary_VAG_hello_vag_start; // get header of first VAG file + const VAGhdr * PolyfileHeader = (VAGhdr *) _binary_VAG_poly_vag_start; // get header of second VAG file + + // From libover47.pdf : + // The sampling frequency of the original audio file can be used to determine the pitch + // at which to play the VAG. pitch = (sampling frequency << 12)/44100L + // Ex: 44.1kHz=0x1000 22.05kHz=0x800 etc + + unsigned int Hellopitch = (SWAP_ENDIAN32(HellofileHeader->samplingFrequency) << 12) / 44100L; + unsigned int Polypitch = (SWAP_ENDIAN32(PolyfileHeader->samplingFrequency) << 12) / 44100L; + + SpuInit(); // Initialize SPU. Called only once. + + initSnd(); + + // First VAG + + hello_spu_address = SpuMalloc(SWAP_ENDIAN32(HellofileHeader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer. + + hello_spu_start_address = SpuSetTransferStartAddr(hello_spu_address); // Sets a starting address in the sound buffer + + hello_get_start_addr = SpuGetTransferStartAddr(); // SpuGetTransferStartAddr() returns current sound buffer transfer start address. + + hello_transSize = sendVAGtoRAM(SWAP_ENDIAN32(HellofileHeader->dataSize), _binary_VAG_hello_vag_start); + + // First VAG + + poly_spu_address = SpuMalloc(SWAP_ENDIAN32(PolyfileHeader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer. + + poly_spu_start_address = SpuSetTransferStartAddr(poly_spu_address); // Sets a starting address in the sound buffer + + poly_get_start_addr = SpuGetTransferStartAddr(); // SpuGetTransferStartAddr() returns current sound buffer transfer start address. + + poly_transSize = sendVAGtoRAM(SWAP_ENDIAN32(PolyfileHeader->dataSize), _binary_VAG_poly_vag_start); + + + // set VAG to channel + + setVoiceAttr(Hellopitch, HELLO, hello_spu_address); // SPU_0CH == hello + + setVoiceAttr(Polypitch, POLY, poly_spu_address); // SPU_2CH == poly + + initGraph(); + + while (1) + { + + if(!counter){ + playSFX(HELLO); // Play first VAG + counter = 240; + } + if(counter == 160){ + playSFX(POLY); // Play second VAG + } + if(counter == 80){ + playSFX(HELLO|POLY); // Play both VAGs simultaneously + } + + + FntPrint("First VAG:"); + FntPrint("\nPitch : %08x-%dKhz", Hellopitch, (SWAP_ENDIAN32(HellofileHeader->samplingFrequency)) ); + FntPrint("\nSet Start addr : %08x", hello_spu_address); + FntPrint("\nReturn start addr : %08x", hello_spu_start_address); + FntPrint("\nGet Start addr : %08x", hello_get_start_addr); + FntPrint("\nSend size : %08x", SWAP_ENDIAN32(HellofileHeader->dataSize)); + FntPrint("\nReturn size : %08x\n", hello_transSize); + + FntPrint("\nSecond VAG:"); + FntPrint("\nPitch : %08x-%dKhz", Polypitch, (SWAP_ENDIAN32(HellofileHeader->samplingFrequency)) ); + FntPrint("\nSet Start addr : %08x", poly_spu_address); + FntPrint("\nReturn start addr : %08x", poly_spu_start_address); + FntPrint("\nGet Start addr : %08x", poly_get_start_addr); + FntPrint("\nSend size : %08x", SWAP_ENDIAN32(PolyfileHeader->dataSize)); + FntPrint("\nReturn size : %08x\n", poly_transSize); + + + FntPrint("\nCounter : %d\n", counter); + + FntFlush(-1); + + counter --; + + display(); + + } + return 0; + } diff --git a/hello_multivag/hello_multivag.c b/hello_multivag/hello_multivag.c new file mode 100644 index 0000000..63f06a9 --- /dev/null +++ b/hello_multivag/hello_multivag.c @@ -0,0 +1,320 @@ +// VAGDEMO2020 by Schnappy +// December 2020 +// Based on VAGDEMO_FIXED by Yagotzirck +// Based on VAGDEMO by Shadow +// based on psyq/addons/sound/TUTO3.C +// +// +// Load two VAG file to SPU sound buffer and play them back alternatively or simultaneously. +// +// WAV creation: use ffmpeg to create a 16-bit ADPCM mono WAV file - change -ar to reduce filesize (and quality) +// $ ffmpeg -i input.mp3 -acodec pcm_s16le -ac 1 -ar 44100 output.wav +// +// WAV to VAG convertion using WAV2VAG : https://github.com/ColdSauce/psxsdk/blob/master/tools/wav2vag.c +// change -freq according to the -ar setting above +// $ wav2vag input.wav output.vag -sraw16 -freq=44100 (-L) +// +// Alternatively, you can use PsyQ VAGEDIT.EXE to change the sampling frequency of an existing VAG file. +// +// Docs : see libformat47.pdf p.209 +// libover47.pdf, p.271 +// libref47.pdf, p.980 +// URLS : http://psx.arthus.net/code/VAG/ + +#include +#include + +#include +#include +#include + + +// Sound system +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +short db = 0; // index of which buffer is used, values 0, 1 + +// Sound stuff + +#define MALLOC_MAX 3 // Max number of time we can call SpuMalloc + +//~ // convert Little endian to Big endian +#define SWAP_ENDIAN32(x) (((x)>>24) | (((x)>>8) & 0xFF00) | (((x)<<8) & 0x00FF0000) | ((x)<<24)) + +typedef struct VAGheader{ // All the values in this header must be big endian + char id[4]; // VAGp 4 bytes -> 1 char * 4 + unsigned int version; // 4 bytes + unsigned int reserved; // 4 bytes + unsigned int dataSize; // (in bytes) 4 bytes + unsigned int samplingFrequency;// 4 bytes + char reserved2[12]; // 12 bytes -> 1 char * 12 + char name[16]; // 16 bytes -> 1 char * 16 + // Waveform data after that +}VAGhdr; + +SpuCommonAttr commonAttributes; // structure for changing common voice attributes +SpuVoiceAttr voiceAttributes ; // structure for changing individual voice attributes + +u_long hello_spu_address; // address allocated in memory for first sound file +u_long poly_spu_address; // address allocated in memory for second sound file + +// DEBUG : these allow printing values for debugging + +u_long hello_spu_start_address; +u_long hello_get_start_addr; +u_long hello_transSize; + +u_long poly_spu_start_address; +u_long poly_get_start_addr; +u_long poly_transSize; + +#define HELLO SPU_0CH // Play first vag on channel 0 +#define POLY SPU_2CH // Play second vag on channel 2 + +// Memory management table ; allow MALLOC_MAX calls to SpuMalloc() - ibref47.pdf p.1044 +char spu_malloc_rec[SPU_MALLOC_RECSIZ * (2 + MALLOC_MAX+1)]; + +// VAG files + +// We're using GrumpyCoder's Nugget wrapper to compile the code with a modern GCC : https://github.com/grumpycoders/pcsx-redux/tree/main/src/mips/psyq +// To include binary files in the exe, add your VAG files to the SRCS variable in Makefile +// and in common.mk, add this rule to include *.vag files : +// +//~ %.o: %.vag + //~ $(PREFIX)-objcopy -I binary --set-section-alignment .data=4 --rename-section .data=.rodata,alloc,load,readonly,data,contents -O elf32-tradlittlemips -B mips $< $@ + + +// hello.vag - 44100 Khz +extern unsigned char _binary____VAG_hello_vag_start[]; // filename must begin with _binary____ followed by the full path, with . and / replaced, and then suffixed with _ and end with _start[]; or end[]; +extern unsigned char _binary____VAG_hello_vag_end[]; // https://discord.com/channels/642647820683444236/663664210525290507/780866265077383189 + +// poly.vag - 44100 Khz +extern unsigned char _binary____VAG_poly_vag_start[]; +extern unsigned char _binary____VAG_poly_vag_end[]; + + +void initGraph(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(8, 60, 304, 200, 0, 500 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + db = !db; + +} + + +// Audio initialisation & functions + +void initSnd(void){ + + SpuInitMalloc(MALLOC_MAX, spu_malloc_rec); // Maximum number of blocks, mem. management table address. + + commonAttributes.mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR); // Mask which attributes to set + commonAttributes.mvol.left = 0x3fff; // Master volume left + commonAttributes.mvol.right = 0x3fff; // see libref47.pdf, p.1058 + + SpuSetCommonAttr(&commonAttributes); // set attributes + + SpuSetIRQ(SPU_OFF); +} + +u_long sendVAGtoRAM(unsigned int VAG_data_size, unsigned char *VAG_data){ + u_long size; + + SpuSetTransferMode(SpuTransByDMA); // DMA transfer; can do other processing during transfer + + size = SpuWrite (VAG_data + sizeof(VAGhdr), VAG_data_size); // transfer VAG_data_size bytes from VAG_data address to sound buffer + + SpuIsTransferCompleted (SPU_TRANSFER_WAIT); // Checks whether transfer is completed and waits for completion + + return size; +} + +void setVoiceAttr(unsigned int pitch, long channel, unsigned long soundAddr ){ + + voiceAttributes.mask= //~ Attributes (bit string, 1 bit per attribute) + ( + SPU_VOICE_VOLL | + SPU_VOICE_VOLR | + SPU_VOICE_PITCH | + SPU_VOICE_WDSA | + SPU_VOICE_ADSR_AMODE | + SPU_VOICE_ADSR_SMODE | + SPU_VOICE_ADSR_RMODE | + SPU_VOICE_ADSR_AR | + SPU_VOICE_ADSR_DR | + SPU_VOICE_ADSR_SR | + SPU_VOICE_ADSR_RR | + SPU_VOICE_ADSR_SL + ); + + voiceAttributes.voice = channel; //~ Voice (low 24 bits are a bit string, 1 bit per voice ) + + voiceAttributes.volume.left = 0x1000; //~ Volume + voiceAttributes.volume.right = 0x1000; //~ Volume + + voiceAttributes.pitch = pitch; //~ Interval (set pitch) + voiceAttributes.addr = soundAddr; //~ Waveform data start address + + voiceAttributes.a_mode = SPU_VOICE_LINEARIncN; //~ Attack rate mode = Linear Increase - see libref47.pdf p.1091 + voiceAttributes.s_mode = SPU_VOICE_LINEARIncN; //~ Sustain rate mode = Linear Increase + voiceAttributes.r_mode = SPU_VOICE_LINEARDecN; //~ Release rate mode = Linear Decrease + + voiceAttributes.ar = 0x0; //~ Attack rate + voiceAttributes.dr = 0x0; //~ Decay rate + voiceAttributes.rr = 0x0; //~ Release rate + voiceAttributes.sr = 0x0; //~ Sustain rate + voiceAttributes.sl = 0xf; //~ Sustain level + + SpuSetVoiceAttr(&voiceAttributes); // set attributes + +} + +void playSFX(unsigned long fx){ + SpuSetKey(SpuOn, fx); +} + +int main(void) +{ + short counter = 0; + + const VAGhdr * HellofileHeader = (VAGhdr *) _binary____VAG_hello_vag_start; // get header of first VAG file + const VAGhdr * PolyfileHeader = (VAGhdr *) _binary____VAG_poly_vag_start; // get header of second VAG file + + // From libover47.pdf : + // The sampling frequency of the original audio file can be used to determine the pitch + // at which to play the VAG. pitch = (sampling frequency << 12)/44100L + // Ex: 44.1kHz=0x1000 22.05kHz=0x800 etc + + unsigned int Hellopitch = (SWAP_ENDIAN32(HellofileHeader->samplingFrequency) << 12) / 44100L; + unsigned int Polypitch = (SWAP_ENDIAN32(PolyfileHeader->samplingFrequency) << 12) / 44100L; + + SpuInit(); // Initialize SPU. Called only once. + + initSnd(); + + // First VAG + + hello_spu_address = SpuMalloc(SWAP_ENDIAN32(HellofileHeader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer. + + hello_spu_start_address = SpuSetTransferStartAddr(hello_spu_address); // Sets a starting address in the sound buffer + + hello_get_start_addr = SpuGetTransferStartAddr(); // SpuGetTransferStartAddr() returns current sound buffer transfer start address. + + hello_transSize = sendVAGtoRAM(SWAP_ENDIAN32(HellofileHeader->dataSize), _binary____VAG_hello_vag_start); + + // First VAG + + poly_spu_address = SpuMalloc(SWAP_ENDIAN32(PolyfileHeader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer. + + poly_spu_start_address = SpuSetTransferStartAddr(poly_spu_address); // Sets a starting address in the sound buffer + + poly_get_start_addr = SpuGetTransferStartAddr(); // SpuGetTransferStartAddr() returns current sound buffer transfer start address. + + poly_transSize = sendVAGtoRAM(SWAP_ENDIAN32(PolyfileHeader->dataSize), _binary____VAG_poly_vag_start); + + + // set VAG to channel + + setVoiceAttr(Hellopitch, HELLO, hello_spu_address); // SPU_0CH == hello + + setVoiceAttr(Polypitch, POLY, poly_spu_address); // SPU_2CH == poly + + initGraph(); + + while (1) + { + + if(!counter){ + playSFX(HELLO); // Play first VAG + counter = 240; + } + if(counter == 160){ + playSFX(POLY); // Play second VAG + } + if(counter == 80){ + playSFX(HELLO|POLY); // Play both VAGs simultaneously + } + + + FntPrint("First VAG:"); + FntPrint("\nPitch : %08x-%dKhz", Hellopitch, (SWAP_ENDIAN32(HellofileHeader->samplingFrequency)) ); + FntPrint("\nSet Start addr : %08x", hello_spu_address); + FntPrint("\nReturn start addr : %08x", hello_spu_start_address); + FntPrint("\nGet Start addr : %08x", hello_get_start_addr); + FntPrint("\nSend size : %08x", SWAP_ENDIAN32(HellofileHeader->dataSize)); + FntPrint("\nReturn size : %08x\n", hello_transSize); + + FntPrint("\nSecond VAG:"); + FntPrint("\nPitch : %08x-%dKhz", Polypitch, (SWAP_ENDIAN32(HellofileHeader->samplingFrequency)) ); + FntPrint("\nSet Start addr : %08x", poly_spu_address); + FntPrint("\nReturn start addr : %08x", poly_spu_start_address); + FntPrint("\nGet Start addr : %08x", poly_get_start_addr); + FntPrint("\nSend size : %08x", SWAP_ENDIAN32(PolyfileHeader->dataSize)); + FntPrint("\nReturn size : %08x\n", poly_transSize); + + + FntPrint("\nCounter : %d\n", counter); + + FntFlush(-1); + + counter --; + + display(); + + } + return 0; + } diff --git a/hello_pad.c b/hello_pad.c new file mode 100644 index 0000000..aaf2eb9 --- /dev/null +++ b/hello_pad.c @@ -0,0 +1,213 @@ +// hello_pad example +// +// We're using libetc PadInit() and PadRead() that only supports the 16 buttons pad +// but doesn't need the libpad lib. It's fine for prototyping and simple stuff. + +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 32 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 2 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +void init(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + + TILE * PADL; // Tile primitives + TILE * TRIGGERL; + + TILE * PADR; + TILE * TRIGGERR; + + TILE * START, * SELECT; + + int pad = 0; + + init(); + + PadInit(0); // Initialize pad. Mode is always 0 + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + // D-cross + + PADL = (TILE *)nextpri; + + setTile(PADL); + setRGB0(PADL, 0, 0, 255); + setXY0(PADL, CENTERX - 80, CENTERY); + setWH(PADL, 24, 24); + + + addPrim(ot[db], PADL); + + nextpri += sizeof(TILE); + + // L1+L2 + + TRIGGERL = (TILE *)nextpri; + + setTile(TRIGGERL); + setRGB0(TRIGGERL, 255, 0, 0); + setXY0(TRIGGERL, CENTERX - 80, CENTERY - 80); + setWH(TRIGGERL, 24, 24); + + + addPrim(ot[db], TRIGGERL); + + nextpri += sizeof(TILE); + + // /\, X, O, [] + + PADR = (TILE *)nextpri; + + setTile(PADR); + setRGB0(PADR, 0, 255, 0); + setXY0(PADR, CENTERX + 50, CENTERY); + setWH(PADR, 24, 24); + + addPrim(ot[db], PADR); + + nextpri += sizeof(TILE); + + // R1+R2 + + TRIGGERR = (TILE *)nextpri; + + setTile(TRIGGERR); + setRGB0(TRIGGERR, 255, 0, 255); + setXY0(TRIGGERR, CENTERX + 50, CENTERY -80); + setWH(TRIGGERR, 24, 24); + + addPrim(ot[db], TRIGGERR); + + nextpri += sizeof(TILE); + + // START + SELECT + + START = (TILE *)nextpri; + + setTile(START); + setRGB0(START, 240, 240, 240); + setXY0(START, CENTERX - 16, CENTERY - 36); + setWH(START, 24, 24); + + addPrim(ot[db], START); + + nextpri += sizeof(TILE); + + // Pad stuff + + pad = PadRead(0); // Read pads input. id is unused, always 0. + // PadRead() returns a 32 bit value, where input from pad 1 is stored in the low 2 bytes and input from pad 2 is stored in the high 2 bytes. (https://matiaslavik.wordpress.com/2015/02/13/diving-into-psx-development/) + + // D-pad + + if(pad & PADLup) {PADL->y0 = CENTERY - 16;} // 🡩 // To access pad 2, use ( pad >> 16 & PADLup)... + if(pad & PADLdown) {PADL->y0 = CENTERY + 16;} // 🡫 + if(pad & PADLright){PADL->x0 = CENTERX - 64;} // 🡪 + if(pad & PADLleft) {PADL->x0 = CENTERX - 96;} // 🡨 + + // Buttons + + if(pad & PADRup) {PADR->y0 = CENTERY - 16;} // â–³ + if(pad & PADRdown) {PADR->y0 = CENTERY + 16;} // ╳ + if(pad & PADRright){PADR->x0 = CENTERX + 66;} // â­˜ + if(pad & PADRleft) {PADR->x0 = CENTERX + 34;} // ⬜ + + // Shoulder buttons + + if(pad & PADL1){TRIGGERL->y0 = CENTERY - 64;} // L1 + if(pad & PADL2){TRIGGERL->y0 = CENTERY - 96;} // L2 + if(pad & PADR1){TRIGGERR->y0 = CENTERY - 64;} // R1 + if(pad & PADR2){TRIGGERR->y0 = CENTERY - 96;} // R2 + + // Start & Select + + if(pad & PADstart){START->w = 32; START->h = 32;START->x0 -= 4;START->y0 -= 4;} // START + if(pad & PADselect){START->r0 = 0;} // SELECT + + FntPrint("Hello Pad!"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_pad/hello_pad.c b/hello_pad/hello_pad.c new file mode 100644 index 0000000..aaf2eb9 --- /dev/null +++ b/hello_pad/hello_pad.c @@ -0,0 +1,213 @@ +// hello_pad example +// +// We're using libetc PadInit() and PadRead() that only supports the 16 buttons pad +// but doesn't need the libpad lib. It's fine for prototyping and simple stuff. + +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 32 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 2 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +void init(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + + TILE * PADL; // Tile primitives + TILE * TRIGGERL; + + TILE * PADR; + TILE * TRIGGERR; + + TILE * START, * SELECT; + + int pad = 0; + + init(); + + PadInit(0); // Initialize pad. Mode is always 0 + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + // D-cross + + PADL = (TILE *)nextpri; + + setTile(PADL); + setRGB0(PADL, 0, 0, 255); + setXY0(PADL, CENTERX - 80, CENTERY); + setWH(PADL, 24, 24); + + + addPrim(ot[db], PADL); + + nextpri += sizeof(TILE); + + // L1+L2 + + TRIGGERL = (TILE *)nextpri; + + setTile(TRIGGERL); + setRGB0(TRIGGERL, 255, 0, 0); + setXY0(TRIGGERL, CENTERX - 80, CENTERY - 80); + setWH(TRIGGERL, 24, 24); + + + addPrim(ot[db], TRIGGERL); + + nextpri += sizeof(TILE); + + // /\, X, O, [] + + PADR = (TILE *)nextpri; + + setTile(PADR); + setRGB0(PADR, 0, 255, 0); + setXY0(PADR, CENTERX + 50, CENTERY); + setWH(PADR, 24, 24); + + addPrim(ot[db], PADR); + + nextpri += sizeof(TILE); + + // R1+R2 + + TRIGGERR = (TILE *)nextpri; + + setTile(TRIGGERR); + setRGB0(TRIGGERR, 255, 0, 255); + setXY0(TRIGGERR, CENTERX + 50, CENTERY -80); + setWH(TRIGGERR, 24, 24); + + addPrim(ot[db], TRIGGERR); + + nextpri += sizeof(TILE); + + // START + SELECT + + START = (TILE *)nextpri; + + setTile(START); + setRGB0(START, 240, 240, 240); + setXY0(START, CENTERX - 16, CENTERY - 36); + setWH(START, 24, 24); + + addPrim(ot[db], START); + + nextpri += sizeof(TILE); + + // Pad stuff + + pad = PadRead(0); // Read pads input. id is unused, always 0. + // PadRead() returns a 32 bit value, where input from pad 1 is stored in the low 2 bytes and input from pad 2 is stored in the high 2 bytes. (https://matiaslavik.wordpress.com/2015/02/13/diving-into-psx-development/) + + // D-pad + + if(pad & PADLup) {PADL->y0 = CENTERY - 16;} // 🡩 // To access pad 2, use ( pad >> 16 & PADLup)... + if(pad & PADLdown) {PADL->y0 = CENTERY + 16;} // 🡫 + if(pad & PADLright){PADL->x0 = CENTERX - 64;} // 🡪 + if(pad & PADLleft) {PADL->x0 = CENTERX - 96;} // 🡨 + + // Buttons + + if(pad & PADRup) {PADR->y0 = CENTERY - 16;} // â–³ + if(pad & PADRdown) {PADR->y0 = CENTERY + 16;} // ╳ + if(pad & PADRright){PADR->x0 = CENTERX + 66;} // â­˜ + if(pad & PADRleft) {PADR->x0 = CENTERX + 34;} // ⬜ + + // Shoulder buttons + + if(pad & PADL1){TRIGGERL->y0 = CENTERY - 64;} // L1 + if(pad & PADL2){TRIGGERL->y0 = CENTERY - 96;} // L2 + if(pad & PADR1){TRIGGERR->y0 = CENTERY - 64;} // R1 + if(pad & PADR2){TRIGGERR->y0 = CENTERY - 96;} // R2 + + // Start & Select + + if(pad & PADstart){START->w = 32; START->h = 32;START->x0 -= 4;START->y0 -= 4;} // START + if(pad & PADselect){START->r0 = 0;} // SELECT + + FntPrint("Hello Pad!"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_poly.c b/hello_poly.c new file mode 100644 index 0000000..0fd5bff --- /dev/null +++ b/hello_poly.c @@ -0,0 +1,197 @@ +// With help from Nicolas Noble, Jaby smoll Seamonstah +// Based on Lameguy64's tutorial series : http://lameguy64.net/svn/pstutorials/chapter1/2-graphics.html +// +// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C : +// + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ + +#include +#include +#include +#include +#include +#include + + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 // Screen height + +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + + +MATRIX identity(int num) // generate num x num matrix +{ + int row, col; + MATRIX matrix; + + for (row = 0; row < num; row++) + { + for (col = 0; col < num; col++) + { + if (row == col) + matrix.m[row][col] = 4096; + else + matrix.m[row][col] = 0; + } + } + return matrix; +} + +void init(void) +{ + ResetGraph(0); + + // Initialize and setup the GTE + InitGeom(); + SetGeomOffset(CENTERX,CENTERY); + SetGeomScreen(CENTERX); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + + MATRIX IDMATRIX = identity(3); // Generate 3x3 identity matrix + + POLY_F4 *poly = {0}; // pointer to a POLY_F4 + SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z} + VECTOR MovVector = {0, 0, CENTERX, 0}; // Initialize translation vector {x, y, z} + VECTOR ScaleVector ={ONE, ONE, ONE}; // ONE is define as 4096 in libgte.h + + SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg + {-32, -32, 1 }, // Vert 1 + {-32, 32, 1 }, // Vert 2 + { 32, -32, 1 }, // Vert 3 + { 32, 32, 1 } // Vert 4 + }; + MATRIX PolyMatrix = IDMATRIX; + + long polydepth; + long polyflag; + long OTz; + + init(); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + poly = (POLY_F4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer + + // Set transform matrices for this polygon + + RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix + TransMatrix(&PolyMatrix, &MovVector); + ScaleMatrix(&PolyMatrix, &ScaleVector); // Apply translation matrix + + SetRotMatrix(&PolyMatrix); // Set default rotation matrix + SetTransMatrix(&PolyMatrix); // Set default transformation matrix + + setPolyF4(poly); // Initialize poly as a POLY_F4 + setRGB0(poly, 255, 0, 255); // Set poly color + + + // RotTransPers + + //~ OTz = RotTransPers(&VertPos[0], (long*)&poly->x0, &polydepth, &polyflag); + //~ RotTransPers(&VertPos[1], (long*)&poly->x1, &polydepth, &polyflag); + //~ RotTransPers(&VertPos[2], (long*)&poly->x2, &polydepth, &polyflag); + //~ RotTransPers(&VertPos[3], (long*)&poly->x3, &polydepth, &polyflag); + + // RotTransPers4 equivalent + + OTz = RotTransPers4( + &VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3], + (long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3, + &polydepth, + &polyflag + ); // Perform coordinate and perspective transformation for 4 vertices + + RotVector.vy += 4; + RotVector.vz += 4; // Apply rotation on Z-axis. On PSX, the Z-axis is pointing away from the screen. + + addPrim(ot[db], poly); // add poly to the Ordering table + + nextpri += sizeof(POLY_F4); // increment nextpri address with size of a POLY_F4 struct + + FntPrint("Hello Poly !"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_poly/hello_poly.c b/hello_poly/hello_poly.c new file mode 100644 index 0000000..0fd5bff --- /dev/null +++ b/hello_poly/hello_poly.c @@ -0,0 +1,197 @@ +// With help from Nicolas Noble, Jaby smoll Seamonstah +// Based on Lameguy64's tutorial series : http://lameguy64.net/svn/pstutorials/chapter1/2-graphics.html +// +// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C : +// + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ + +#include +#include +#include +#include +#include +#include + + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 // Screen height + +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + + +MATRIX identity(int num) // generate num x num matrix +{ + int row, col; + MATRIX matrix; + + for (row = 0; row < num; row++) + { + for (col = 0; col < num; col++) + { + if (row == col) + matrix.m[row][col] = 4096; + else + matrix.m[row][col] = 0; + } + } + return matrix; +} + +void init(void) +{ + ResetGraph(0); + + // Initialize and setup the GTE + InitGeom(); + SetGeomOffset(CENTERX,CENTERY); + SetGeomScreen(CENTERX); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + + MATRIX IDMATRIX = identity(3); // Generate 3x3 identity matrix + + POLY_F4 *poly = {0}; // pointer to a POLY_F4 + SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z} + VECTOR MovVector = {0, 0, CENTERX, 0}; // Initialize translation vector {x, y, z} + VECTOR ScaleVector ={ONE, ONE, ONE}; // ONE is define as 4096 in libgte.h + + SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg + {-32, -32, 1 }, // Vert 1 + {-32, 32, 1 }, // Vert 2 + { 32, -32, 1 }, // Vert 3 + { 32, 32, 1 } // Vert 4 + }; + MATRIX PolyMatrix = IDMATRIX; + + long polydepth; + long polyflag; + long OTz; + + init(); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + poly = (POLY_F4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer + + // Set transform matrices for this polygon + + RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix + TransMatrix(&PolyMatrix, &MovVector); + ScaleMatrix(&PolyMatrix, &ScaleVector); // Apply translation matrix + + SetRotMatrix(&PolyMatrix); // Set default rotation matrix + SetTransMatrix(&PolyMatrix); // Set default transformation matrix + + setPolyF4(poly); // Initialize poly as a POLY_F4 + setRGB0(poly, 255, 0, 255); // Set poly color + + + // RotTransPers + + //~ OTz = RotTransPers(&VertPos[0], (long*)&poly->x0, &polydepth, &polyflag); + //~ RotTransPers(&VertPos[1], (long*)&poly->x1, &polydepth, &polyflag); + //~ RotTransPers(&VertPos[2], (long*)&poly->x2, &polydepth, &polyflag); + //~ RotTransPers(&VertPos[3], (long*)&poly->x3, &polydepth, &polyflag); + + // RotTransPers4 equivalent + + OTz = RotTransPers4( + &VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3], + (long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3, + &polydepth, + &polyflag + ); // Perform coordinate and perspective transformation for 4 vertices + + RotVector.vy += 4; + RotVector.vz += 4; // Apply rotation on Z-axis. On PSX, the Z-axis is pointing away from the screen. + + addPrim(ot[db], poly); // add poly to the Ordering table + + nextpri += sizeof(POLY_F4); // increment nextpri address with size of a POLY_F4 struct + + FntPrint("Hello Poly !"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_poly_ft.c b/hello_poly_ft.c new file mode 100644 index 0000000..d237215 --- /dev/null +++ b/hello_poly_ft.c @@ -0,0 +1,231 @@ +// With help from Nicolas Noble, Jaby smoll Seamonstah +// Based on Lameguy64's tutorial series : http://lameguy64.net/svn/pstutorials/chapter1/2-graphics.html +// +// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C : +// + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ + +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 // Screen height + +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y + +#define MARGINX 32 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 5 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +// 16bpp TIM +extern unsigned long _binary_TIM_bousai_tim_start[]; +extern unsigned long _binary_TIM_bousai_tim_end[]; +extern unsigned long _binary_TIM_bousai_tim_length; + +TIM_IMAGE bousai; + + +MATRIX identity(int num) // generate num x num matrix +{ + int row, col; + MATRIX matrix; + + for (row = 0; row < num; row++) + { + for (col = 0; col < num; col++) + { + if (row == col) + matrix.m[row][col] = 4096; + else + matrix.m[row][col] = 0; + } + } + return matrix; +} + +void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous + OpenTIM(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 + } +} + +void init(void) +{ + ResetGraph(0); + + // Initialize and setup the GTE + + InitGeom(); + SetGeomOffset(CENTERX,CENTERY); + SetGeomScreen(CENTERX); + + 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; + } + + setRGB0(&draw[0], 128, 128, 128); + setRGB0(&draw[1], 128, 128, 128); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + + MATRIX IDMATRIX = identity(3); // Generate 3x3 identity matrix + + POLY_FT4 *poly = {0}; // pointer to a POLY_G4 + SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z} + VECTOR MovVector = {0, 0, CENTERX/2, 0}; // Initialize translation vector {x, y, z, pad} + VECTOR ScaleVector = {ONE, ONE, ONE}; // ONE is define as 4096 in libgte.h + + SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg + {-32, -32, 1 }, // Vert 1 + {-32, 32, 1 }, // Vert 2 + { 32, -32, 1 }, // Vert 3 + { 32, 32, 1 } // Vert 4 + }; + MATRIX PolyMatrix = IDMATRIX; + + long polydepth; + long polyflag; + + int ping = 0; + + init(); + + LoadTexture(_binary_TIM_bousai_tim_start, &bousai); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + poly = (POLY_FT4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer + + // Set transform matrices for this polygon + + RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix + TransMatrix(&PolyMatrix, &MovVector); // Apply translation matrix + ScaleMatrix(&PolyMatrix, &ScaleVector); // Apply scale matrix + + SetRotMatrix(&PolyMatrix); // Set default rotation matrix + SetTransMatrix(&PolyMatrix); // Set default transformation matrix + + setPolyFT4(poly); // Initialize poly as a POLY_F4 + + poly->tpage = getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y); // Get Tpage coordinates from the TIM_IMAGE mode and prect members. + + setRGB0(poly, 128, 128, 128); // Set poly color (neutra here) + + RotTransPers4( + &VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3], + (long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3, + &polydepth, + &polyflag + ); // Perform coordinate and perspective transformation for 4 vertices + + setUV4(poly, 0, 0, 0, 144, 144, 0, 144, 144); // Set UV coordinates in order Top Left, Bottom Left, Top Right, Bottom Right + + + // Let's have some fun on the Z axis + + if(!ping){ + if (MovVector.vz < CENTERX){ // While Poly position on Z axis is < 160, push it + MovVector.vz += 1; // Push on Z axis + } else { + ping = !ping; // Switch ping value + } + } + + if(ping){ + if (MovVector.vz > CENTERX/2){ // While Poly position on Z axis is > 80, pull it + MovVector.vz -= 1; // Pull on Z axis + } else { + ping = !ping; // Switch ping value + } + } + + + addPrim(ot[db], poly); // add poly to the Ordering table + + nextpri += sizeof(POLY_FT4); // increment nextpri address with size of a POLY_F4 struct + + FntPrint("Hello textured poly !"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_poly_ft/hello_poly_ft.c b/hello_poly_ft/hello_poly_ft.c new file mode 100644 index 0000000..f55f6ee --- /dev/null +++ b/hello_poly_ft/hello_poly_ft.c @@ -0,0 +1,231 @@ +// With help from Nicolas Noble, Jaby smoll Seamonstah +// Based on Lameguy64's tutorial series : http://lameguy64.net/svn/pstutorials/chapter1/2-graphics.html +// +// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C : +// + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ + +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 // Screen height + +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y + +#define MARGINX 32 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 5 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +// 16bpp TIM +extern unsigned long _binary____TIM_bousai_tim_start[]; +extern unsigned long _binary____TIM_bousai_tim_end[]; +extern unsigned long _binary____TIM_bousai_tim_length; + +TIM_IMAGE bousai; + + +MATRIX identity(int num) // generate num x num matrix +{ + int row, col; + MATRIX matrix; + + for (row = 0; row < num; row++) + { + for (col = 0; col < num; col++) + { + if (row == col) + matrix.m[row][col] = 4096; + else + matrix.m[row][col] = 0; + } + } + return matrix; +} + +void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous + OpenTIM(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 + } +} + +void init(void) +{ + ResetGraph(0); + + // Initialize and setup the GTE + + InitGeom(); + SetGeomOffset(CENTERX,CENTERY); + SetGeomScreen(CENTERX); + + 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; + } + + setRGB0(&draw[0], 128, 128, 128); + setRGB0(&draw[1], 128, 128, 128); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + + MATRIX IDMATRIX = identity(3); // Generate 3x3 identity matrix + + POLY_FT4 *poly = {0}; // pointer to a POLY_G4 + SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z} + VECTOR MovVector = {0, 0, CENTERX/2, 0}; // Initialize translation vector {x, y, z, pad} + VECTOR ScaleVector = {ONE, ONE, ONE}; // ONE is define as 4096 in libgte.h + + SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg + {-32, -32, 1 }, // Vert 1 + {-32, 32, 1 }, // Vert 2 + { 32, -32, 1 }, // Vert 3 + { 32, 32, 1 } // Vert 4 + }; + MATRIX PolyMatrix = IDMATRIX; + + long polydepth; + long polyflag; + + int ping = 0; + + init(); + + LoadTexture(_binary____TIM_bousai_tim_start, &bousai); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + poly = (POLY_FT4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer + + // Set transform matrices for this polygon + + RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix + TransMatrix(&PolyMatrix, &MovVector); // Apply translation matrix + ScaleMatrix(&PolyMatrix, &ScaleVector); // Apply scale matrix + + SetRotMatrix(&PolyMatrix); // Set default rotation matrix + SetTransMatrix(&PolyMatrix); // Set default transformation matrix + + setPolyFT4(poly); // Initialize poly as a POLY_F4 + + poly->tpage = getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y); // Get Tpage coordinates from the TIM_IMAGE mode and prect members. + + setRGB0(poly, 128, 128, 128); // Set poly color (neutra here) + + RotTransPers4( + &VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3], + (long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3, + &polydepth, + &polyflag + ); // Perform coordinate and perspective transformation for 4 vertices + + setUV4(poly, 0, 0, 0, 144, 144, 0, 144, 144); // Set UV coordinates in order Top Left, Bottom Left, Top Right, Bottom Right + + + // Let's have some fun on the Z axis + + if(!ping){ + if (MovVector.vz < CENTERX){ // While Poly position on Z axis is < 160, push it + MovVector.vz += 1; // Push on Z axis + } else { + ping = !ping; // Switch ping value + } + } + + if(ping){ + if (MovVector.vz > CENTERX/2){ // While Poly position on Z axis is > 80, pull it + MovVector.vz -= 1; // Pull on Z axis + } else { + ping = !ping; // Switch ping value + } + } + + + addPrim(ot[db], poly); // add poly to the Ordering table + + nextpri += sizeof(POLY_FT4); // increment nextpri address with size of a POLY_F4 struct + + FntPrint("Hello textured poly !"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_poly_fun/hello_poly_fun.c b/hello_poly_fun/hello_poly_fun.c new file mode 100644 index 0000000..afc4a83 --- /dev/null +++ b/hello_poly_fun/hello_poly_fun.c @@ -0,0 +1,618 @@ +// Having fun with polygons, matrices and vectors +// Credits : Schnappy +//With great help from Jaby smoll Seamonstah, Nicolas Noble, NDR008, paul, sickle on https://discord.com/invite/Zd82yXvs +// 11/2020 + +#include +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SPIN 16 // Rotation speed increment + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 10 // margins for text display +#define MARGINY 4 + +#define FONTSIZE 8 * 7 // Text Field Height + +#define OTLEN 16 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +CVECTOR BgColor[3] = {20, 20, 20}; + +struct polygon + { + POLY_F4 * poly_f4; + CVECTOR color; + short width; + short height; + //~ VECTOR PosV_L; // Not used anymore + SVECTOR RotV_L; + VECTOR TransV_L; + VECTOR ScaleV_L; + SVECTOR PivotV_L; + SVECTOR Verts[4]; + MATRIX Matrix; + long depth; + long flag; + short rotSpeed; + int otz; + }; + +void init(void) +{ + ResetGraph(0); + + // Initialize and setup the GTE : Not needed ? + + InitGeom(); + SetGeomOffset(CENTERX,CENTERY); + SetGeomScreen(CENTERX); + + PadInit(0); + + 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; + } + + setRGB0(&draw[0], BgColor->r, BgColor->g, BgColor->b); + setRGB0(&draw[1], BgColor->r, BgColor->g, BgColor->b); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +void pivotPoint(SVECTOR VertPos[3],short width,short height, SVECTOR pivot){ + + // Not very efficient I think + + VertPos[0].vx = -pivot.vx; + VertPos[0].vy = -pivot.vy; + VertPos[0].vz = 1; + + VertPos[1].vx = width - pivot.vx; + VertPos[1].vy = -pivot.vy; + VertPos[1].vz = 1; + + VertPos[2].vx = -pivot.vx; + VertPos[2].vy = height-pivot.vy; + VertPos[2].vz = 1; + + VertPos[3].vx = width - pivot.vx; + VertPos[3].vy = height - pivot.vy; + VertPos[3].vz = 1; +} + + +MATRIX identity(int num) +{ + int row, col; + MATRIX matrix; + + for (row = 0; row < num; row++) + { + for (col = 0; col < num; col++) + { + if (row == col) + matrix.m[row][col] = 4096; + else + matrix.m[row][col] = 0; + } + } + return matrix; +} + +int main(void) +{ + + MATRIX IDMATRIX = identity(3); + + u_short BtnTimer = 0; // Timer to limit pad input rate + u_short polyCount = 1; // current polygon index + + int otz; // z-index + + struct polygon *CurrentPoly; // points to the address of selected polygon + + + // White cursor : shows which polygon is selected + + struct polygon cursorS = { + cursorS.poly_f4, + {255, 255, 255}, // color + 30, 30, // width, height + {0,0,0}, // RotV_L + {0,0,0, 0}, // TransV_L + {4096,4096,4096}, // ScaleV_L + {1,1,1}, // PivotV + { // Verts[4] + {-1, -1, 1}, + { 1, -1, 1}, + {-1, 1, 1}, + { 1, 1, 1} + }, + IDMATRIX // Matrix + }; + + //Red + + struct polygon polyS = { + polyS.poly_f4, + {255, 0, 0}, // color + 30, 30, // width, height + {0,0,0}, // RotV_L + {-48, -30, 0, 0}, // TransV_L + {4096,4096,4096}, // ScaleV_L + {15,15,1}, // PivotV + { // Verts[4] + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0} + }, + IDMATRIX, // Matrix + 0,0, // depth, flag + 8, // rotSpeed + 1 // z-index + }; + + + //Yellow + + struct polygon poly1S = { + poly1S.poly_f4, + {255, 187, 0}, // color + 28, 28, // width, height + {0,0,0}, // RotV_L + {-20, 10, 0, 0}, // TransV_L + {4096,4096,4096}, // ScaleV_L + {4,4,1}, // PivotV + { // Verts[4] + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0} + }, + IDMATRIX, // Matrix + 0,0, // depth, flag + -12, // rotSpeed + 2 // z-index + }; + + + //Green + + struct polygon poly2S = { + poly2S.poly_f4, + {0, 255, 153}, // color + 24, 24, // width, height + {0,0,0}, // RotV_L + {36, -10, 0, 0}, // TransV_L + {4096,4096,4096}, // ScaleV_L + {12,12,1}, // PivotV + { // Verts[4] + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0} + }, + IDMATRIX, // Matrix + 0,0, // depth, flag + -6, // rotSpeed + 3 // z-index + }; + + + //Blue + + struct polygon poly3S = { + poly3S.poly_f4, + {112, 254, 254}, // color + 26, 26, // width, height + {0,0,0}, // RotV_L + {20, 20, 0, 0}, // TransV_L + {4096,4096,4096}, // ScaleV_L + {13,13,1}, // PivotV + { // Verts[4] + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0} + }, + IDMATRIX, // Matrix + 0,0, //depth, flag + 256, //rotSpeed + 4 // z-index + }; + + + ///// + + CurrentPoly = &polyS; + + pivotPoint(polyS.Verts, polyS.width, polyS.height, polyS.PivotV_L); + pivotPoint(poly1S.Verts, poly1S.width, poly1S.height, poly1S.PivotV_L); + pivotPoint(poly2S.Verts, poly2S.width, poly2S.height, poly2S.PivotV_L); + pivotPoint(poly3S.Verts, poly3S.width, poly3S.height, poly3S.PivotV_L); + + init(); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + cursorS.poly_f4 = (POLY_F4 *)nextpri; + + RotMatrix(&cursorS.RotV_L , &cursorS.Matrix); + TransMatrix(&cursorS.Matrix, &CurrentPoly->TransV_L); + + SetRotMatrix(&cursorS.Matrix); + SetTransMatrix(&cursorS.Matrix); + + setPolyF4(cursorS.poly_f4); + setRGB0(cursorS.poly_f4,cursorS.color.r,cursorS.color.g,cursorS.color.b); + //~ setXY4(cursorS, MovVector.vx-1, MovVector.vy-1 ,MovVector.vx + 1, MovVector.vy -1,MovVector.vx-1, MovVector.vy+1,MovVector.vx+1, MovVector.vy+1); + + RotTransPers4( + &cursorS.Verts[0], &cursorS.Verts[1], &cursorS.Verts[2], &cursorS.Verts[3], + (long*)&cursorS.poly_f4->x0, (long*)&cursorS.poly_f4->x1, (long*)&cursorS.poly_f4->x2, (long*)&cursorS.poly_f4->x3, + &cursorS.depth, + &cursorS.flag + ); + + addPrim(ot[db], cursorS.poly_f4); + + nextpri += sizeof(POLY_F4); + + ///// Red + + polyS.poly_f4 = (POLY_F4 *)nextpri; + + polyS.RotV_L.vz += polyS.rotSpeed; + + RotMatrix(&polyS.RotV_L, &polyS.Matrix); + TransMatrix(&polyS.Matrix, &polyS.TransV_L); + ScaleMatrix(&polyS.Matrix, &polyS.ScaleV_L); + + SetRotMatrix(&polyS.Matrix); + SetTransMatrix(&polyS.Matrix); + + setPolyF4(polyS.poly_f4); + setRGB0(polyS.poly_f4, polyS.color.r,polyS.color.g,polyS.color.b); + RotTransPers4( + &polyS.Verts[0], &polyS.Verts[1], &polyS.Verts[2], &polyS.Verts[3], + (long*)&polyS.poly_f4->x0, (long*)&polyS.poly_f4->x1, (long*)&polyS.poly_f4->x2, (long*)&polyS.poly_f4->x3, + &polyS.depth, + &polyS.flag + ); + + addPrim(ot[db]+polyS.otz, polyS.poly_f4); + + nextpri += sizeof(POLY_F4); + + ///// Yellow + + poly1S.poly_f4 = (POLY_F4 *)nextpri; + + poly1S.RotV_L.vz += poly1S.rotSpeed; + + RotMatrix(&poly1S.RotV_L, &poly1S.Matrix); + TransMatrix(&poly1S.Matrix, &poly1S.TransV_L); + ScaleMatrix(&poly1S.Matrix, &poly1S.ScaleV_L); + + + SetRotMatrix(&poly1S.Matrix); + SetTransMatrix(&poly1S.Matrix); + + setPolyF4(poly1S.poly_f4); + setRGB0(poly1S.poly_f4, poly1S.color.r,poly1S.color.g,poly1S.color.b); + RotTransPers4( + &poly1S.Verts[0], &poly1S.Verts[1], &poly1S.Verts[2], &poly1S.Verts[3], + (long*)&poly1S.poly_f4->x0, (long*)&poly1S.poly_f4->x1, (long*)&poly1S.poly_f4->x2, (long*)&poly1S.poly_f4->x3, + &poly1S.depth, + &poly1S.flag + ); + + addPrim(ot[db]+poly1S.otz, poly1S.poly_f4); + + nextpri += sizeof(POLY_F4); + + + ///// Green + + poly2S.poly_f4 = (POLY_F4 *)nextpri; + + poly2S.RotV_L.vz += poly2S.rotSpeed; + + RotMatrix(&poly2S.RotV_L, &poly2S.Matrix); + TransMatrix(&poly2S.Matrix, &poly2S.TransV_L); + ScaleMatrix(&poly2S.Matrix, &poly2S.ScaleV_L); + + + SetRotMatrix(&poly2S.Matrix); + SetTransMatrix(&poly2S.Matrix); + + setPolyF4(poly2S.poly_f4); + setRGB0(poly2S.poly_f4, poly2S.color.r,poly2S.color.g,poly2S.color.b); + RotTransPers4( + &poly2S.Verts[0], &poly2S.Verts[1], &poly2S.Verts[2], &poly2S.Verts[3], + (long*)&poly2S.poly_f4->x0, (long*)&poly2S.poly_f4->x1, (long*)&poly2S.poly_f4->x2, (long*)&poly2S.poly_f4->x3, + &poly2S.depth, + &poly2S.flag + ); + + addPrim(ot[db]+poly2S.otz, poly2S.poly_f4); + + nextpri += sizeof(POLY_F4); + + ///// Blue + + poly3S.poly_f4 = (POLY_F4 *)nextpri; + + poly3S.RotV_L.vz += poly3S.rotSpeed; + + RotMatrix(&poly3S.RotV_L, &poly3S.Matrix); + TransMatrix(&poly3S.Matrix, &poly3S.TransV_L); + ScaleMatrix(&poly3S.Matrix, &poly3S.ScaleV_L); + + SetRotMatrix(&poly3S.Matrix); + SetTransMatrix(&poly3S.Matrix); + + setPolyF4(poly3S.poly_f4); + setRGB0(poly3S.poly_f4, poly3S.color.r,poly3S.color.g,poly3S.color.b); + RotTransPers4( + &poly3S.Verts[0], &poly3S.Verts[1], &poly3S.Verts[2], &poly3S.Verts[3], + (long*)&poly3S.poly_f4->x0, (long*)&poly3S.poly_f4->x1, (long*)&poly3S.poly_f4->x2, (long*)&poly3S.poly_f4->x3, + &poly3S.depth, + &poly3S.flag + ); + + addPrim(ot[db]+poly3S.otz, poly3S.poly_f4); + + nextpri += sizeof(POLY_F4); + + + // Pad stuff + + + int pad = PadRead(0); // init pad + + // Right D-pad + + if(pad & PADRup){ + if (CurrentPoly->PivotV_L.vy >= 0){ + CurrentPoly->PivotV_L.vy -= 1; + pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L); + } + else { + CurrentPoly->PivotV_L.vy = CurrentPoly->PivotV_L.vy; + } + }; + + if(pad & PADRdown){ + if (CurrentPoly->PivotV_L.vy <= CurrentPoly->height ){ + CurrentPoly->PivotV_L.vy += 1; + pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L); + } + else { + CurrentPoly->PivotV_L.vy = CurrentPoly->PivotV_L.vy; + } + }; + + if(pad & PADRleft){ + if (CurrentPoly->PivotV_L.vx >= 0){ + CurrentPoly->PivotV_L.vx -= 1; + pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L); + } + else { + CurrentPoly->PivotV_L.vx = CurrentPoly->PivotV_L.vx; + } + }; + + if(pad & PADRright){ + if (CurrentPoly->PivotV_L.vx <= CurrentPoly->width ){ + CurrentPoly->PivotV_L.vx += 1; + pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L); + } + else { + CurrentPoly->PivotV_L.vx = CurrentPoly->PivotV_L.vx; + } + }; + + // R1, R2, L2, L2 + + if(pad & PADR1){ + + if(BtnTimer == 0){ + + if (polyCount < 4){ + CurrentPoly -= 1; + BtnTimer = 10; + polyCount++; + } + else { + CurrentPoly = &polyS + 1; + polyCount = 0; + } + } + } + + if(pad & PADR2){ + + if(BtnTimer == 0){ + if(CurrentPoly->otz < 5 ){ + CurrentPoly->otz += 1; + BtnTimer = 10; + } else { + CurrentPoly->otz = 1; + BtnTimer = 10; + } + } + } + + if(pad & PADL1){ + + if(BtnTimer == 0){ + if (CurrentPoly->rotSpeed <= 320){ + CurrentPoly->rotSpeed += 8; + } + BtnTimer = 10; + } + } + + if(pad & PADL2){ + + if(BtnTimer == 0){ + if (CurrentPoly->rotSpeed >= -320){ + CurrentPoly->rotSpeed -= 8; + } + BtnTimer = 10; + } + } + + // Left D-Pad + + if(pad & PADLup){ + + if(BtnTimer == 0){ + CurrentPoly->TransV_L.vy -= 1; + //~ BtnTimer = 2; + } + } + if(pad & PADLdown){ + + if(BtnTimer == 0){ + CurrentPoly->TransV_L.vy += 1; + //~ BtnTimer = 2; + } + } + if(pad & PADLleft){ + + if(BtnTimer == 0){ + CurrentPoly->TransV_L.vx -= 1; + //~ BtnTimer = 2; + } + } + if(pad & PADLright){ + + if(BtnTimer == 0){ + CurrentPoly->TransV_L.vx += 1; + //~ BtnTimer = 2; + } + } + if(pad & PADstart){ + + if(BtnTimer == 0){ + CurrentPoly->ScaleV_L.vx += 100; + CurrentPoly->ScaleV_L.vy += 100; + //~ CurrentPoly->TransV_L.vz += 1; + + } + } + if(pad & PADselect){ + + if(BtnTimer == 0){ + CurrentPoly->ScaleV_L.vx -= 100; + CurrentPoly->ScaleV_L.vy -= 100; + //~ CurrentPoly->TransV_L.vz -= 1; + + } + } + + // Btn_timer decrement + + if(BtnTimer > 0){ + BtnTimer -= 1; + } + + // Debug stuff + + // Display Rotation matrix + + //~ FntPrint("Rotmatrix:\n%d %d %d\n%d %d %d\n%d %d %d \n", + //~ Poly1Matrix.m[0][0], Poly1Matrix.m[0][1], Poly1Matrix.m[0][2], + //~ Poly1Matrix.m[1][0], Poly1Matrix.m[1][1], Poly1Matrix.m[1][2], + //~ Poly1Matrix.m[2][0], Poly1Matrix.m[2][1], Poly1Matrix.m[2][2]); + + // Display Mem adress and values of verticess + //~ FntPrint("cur:%x\n 0:%x\n 1:%x\n 2:%x\n 3:%x\n", CurrentPoly, &polyS, &poly1S, &poly2S, &poly3S); + //~ FntPrint("timer:%d polyCount:%d speed:%d", BtnTimer, polyCount, CurrentPoly->rotSpeed ); + + //~ FntPrint("&poly->x0 Addr:%x Value:%d \n&poly->y0 Addr:%x Value:%d \n&poly->x1 Addr:%x Value:%d", + //~ (long)&poly->x0, poly->x0, + //~ (long)&poly->y0, poly->y0, + //~ (long)&poly->x1, poly->x1); + + //~ FntPrint("otz : %d\n" , CurrentPoly->rotSpeed); + + // On-screen instructions + + FntPrint("\ +D-Pad:move polygon.\n\ +[],X,O,\/\\ : Move pivot point.\n\ +L1,L2 : Rotations speed +/-\n\ +R1 : select polygon\n\ +R2 : change z-index\n\ +Start,Select : Scale polygon +/-\ +"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_poly_gt.c b/hello_poly_gt.c new file mode 100644 index 0000000..315e838 --- /dev/null +++ b/hello_poly_gt.c @@ -0,0 +1,215 @@ +// With help from Nicolas Noble, Jaby smoll Seamonstah +// Based on Lameguy64's tutorial series : http://lameguy64.net/svn/pstutorials/chapter1/2-graphics.html +// +// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C : +// + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ + + +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 // Screen height + +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y + +#define MARGINX 32 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 5 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +// 16bpp TIM +extern unsigned long _binary_TIM_bousai_tim_start[]; +extern unsigned long _binary_TIM_bousai_tim_end[]; +extern unsigned long _binary_TIM_bousai_tim_length; + +TIM_IMAGE bousai; + + +MATRIX identity(int num) // generate num x num matrix +{ + int row, col; + MATRIX matrix; + + for (row = 0; row < num; row++) + { + for (col = 0; col < num; col++) + { + if (row == col) + matrix.m[row][col] = 4096; + else + matrix.m[row][col] = 0; + } + } + return matrix; +} + +void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous + OpenTIM(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 + } +} + +void init(void) +{ + ResetGraph(0); + + // Initialize and setup the GTE + + InitGeom(); + SetGeomOffset(CENTERX,CENTERY); + SetGeomScreen(CENTERX); + + 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; + } + + setRGB0(&draw[0], 128, 128, 128); + setRGB0(&draw[1], 128, 128, 128); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + int i; + + MATRIX IDMATRIX = identity(3); // Generate 3x3 identity matrix + + POLY_GT4 *poly = {0}; // pointer to a POLY_G4 + SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z} + VECTOR MovVector = {0, 0, 120, 0}; // Initialize translation vector {x, y, z} + + SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg + {-32, -32, 0 }, // Vert 1 + {-32, 32, 0 }, // Vert 2 + { 32, -32, 0 }, // Vert 3 + { 32, 32, 0 } // Vert 4 + }; + MATRIX PolyMatrix = IDMATRIX; + + DR_TPAGE * bousai_tpage; + + long polydepth; + long polyflag; + + init(); + + LoadTexture(_binary_TIM_bousai_tim_start, &bousai); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + poly = (POLY_GT4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer + + // Set transform matrices for this polygon + + RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix + TransMatrix(&PolyMatrix, &MovVector); // Apply translation matrix + + SetRotMatrix(&PolyMatrix); // Set default rotation matrix + SetTransMatrix(&PolyMatrix); // Set default transformation matrix + + setPolyGT4(poly); // Initialize poly as a POLY_F4 + poly->tpage = getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y); + + setRGB0(poly, 128, 128, 128); // Set vertice 1 color + setRGB1(poly, 255, 0, 0); // Set vertice 2 color + setRGB2(poly, 0, 255, 0); // Set vertice 3 color + setRGB3(poly, 0, 0, 255); // Set vertice 4 color + + RotTransPers4( + &VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3], + (long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3, + &polydepth, + &polyflag + ); // Perform coordinate and perspective transformation for 4 vertices + + setUV4(poly, 0, 0, 0, 144, 144, 0, 144, 144); // Set UV coordinates in order Top Left, Bottom Left, Top Right, Bottom Right + + RotVector.vx += 8; // Apply rotation on Z-axis. On PSX, the Z-axis is pointing away from the screen. + + addPrim(ot[db], poly); // add poly to the Ordering table + + nextpri += sizeof(POLY_GT4); // increment nextpri address with size of a POLY_F4 struct + + FntPrint("Hello textured shaded !"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_poly_gt/hello_poly_gt.c b/hello_poly_gt/hello_poly_gt.c new file mode 100644 index 0000000..904a8a4 --- /dev/null +++ b/hello_poly_gt/hello_poly_gt.c @@ -0,0 +1,215 @@ +// With help from Nicolas Noble, Jaby smoll Seamonstah +// Based on Lameguy64's tutorial series : http://lameguy64.net/svn/pstutorials/chapter1/2-graphics.html +// +// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C : +// + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ + + +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 // Screen height + +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y + +#define MARGINX 32 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 5 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +// 16bpp TIM +extern unsigned long _binary____TIM_bousai_tim_start[]; +extern unsigned long _binary____TIM_bousai_tim_end[]; +extern unsigned long _binary____TIM_bousai_tim_length; + +TIM_IMAGE bousai; + + +MATRIX identity(int num) // generate num x num matrix +{ + int row, col; + MATRIX matrix; + + for (row = 0; row < num; row++) + { + for (col = 0; col < num; col++) + { + if (row == col) + matrix.m[row][col] = 4096; + else + matrix.m[row][col] = 0; + } + } + return matrix; +} + +void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous + OpenTIM(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 + } +} + +void init(void) +{ + ResetGraph(0); + + // Initialize and setup the GTE + + InitGeom(); + SetGeomOffset(CENTERX,CENTERY); + SetGeomScreen(CENTERX); + + 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; + } + + setRGB0(&draw[0], 128, 128, 128); + setRGB0(&draw[1], 128, 128, 128); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + int i; + + MATRIX IDMATRIX = identity(3); // Generate 3x3 identity matrix + + POLY_GT4 *poly = {0}; // pointer to a POLY_G4 + SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z} + VECTOR MovVector = {0, 0, 120, 0}; // Initialize translation vector {x, y, z} + + SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg + {-32, -32, 0 }, // Vert 1 + {-32, 32, 0 }, // Vert 2 + { 32, -32, 0 }, // Vert 3 + { 32, 32, 0 } // Vert 4 + }; + MATRIX PolyMatrix = IDMATRIX; + + DR_TPAGE * bousai_tpage; + + long polydepth; + long polyflag; + + init(); + + LoadTexture(_binary____TIM_bousai_tim_start, &bousai); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + poly = (POLY_GT4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer + + // Set transform matrices for this polygon + + RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix + TransMatrix(&PolyMatrix, &MovVector); // Apply translation matrix + + SetRotMatrix(&PolyMatrix); // Set default rotation matrix + SetTransMatrix(&PolyMatrix); // Set default transformation matrix + + setPolyGT4(poly); // Initialize poly as a POLY_F4 + poly->tpage = getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y); + + setRGB0(poly, 128, 128, 128); // Set vertice 1 color + setRGB1(poly, 255, 0, 0); // Set vertice 2 color + setRGB2(poly, 0, 255, 0); // Set vertice 3 color + setRGB3(poly, 0, 0, 255); // Set vertice 4 color + + RotTransPers4( + &VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3], + (long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3, + &polydepth, + &polyflag + ); // Perform coordinate and perspective transformation for 4 vertices + + setUV4(poly, 0, 0, 0, 144, 144, 0, 144, 144); // Set UV coordinates in order Top Left, Bottom Left, Top Right, Bottom Right + + RotVector.vx += 8; // Apply rotation on Z-axis. On PSX, the Z-axis is pointing away from the screen. + + addPrim(ot[db], poly); // add poly to the Ordering table + + nextpri += sizeof(POLY_GT4); // increment nextpri address with size of a POLY_F4 struct + + FntPrint("Hello textured shaded !"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_poly_gt_tw.c b/hello_poly_gt_tw.c new file mode 100644 index 0000000..7c282e5 --- /dev/null +++ b/hello_poly_gt_tw.c @@ -0,0 +1,234 @@ +// With help from Nicolas Noble, Jaby smoll Seamonstah +// Based on Lameguy64's tutorial series : http://lameguy64.net/svn/pstutorials/chapter1/2-graphics.html +// +// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C : +// + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ + + +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 // Screen height + +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y + +#define MARGINX 32 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 5 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +// 16bpp TIM +extern unsigned long _binary_TIM_bousai_tim_start[]; +extern unsigned long _binary_TIM_bousai_tim_end[]; +extern unsigned long _binary_TIM_bousai_tim_length; + +TIM_IMAGE bousai; + + +MATRIX identity(int num) // generate num x num matrix +{ + int row, col; + MATRIX matrix; + + for (row = 0; row < num; row++) + { + for (col = 0; col < num; col++) + { + if (row == col) + matrix.m[row][col] = 4096; + else + matrix.m[row][col] = 0; + } + } + return matrix; +} + +void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous + OpenTIM(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 + } +} + +void init(void) +{ + ResetGraph(0); + + // Initialize and setup the GTE + + InitGeom(); + SetGeomOffset(CENTERX,CENTERY); + SetGeomScreen(CENTERX); + + 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; + } + + setRGB0(&draw[0], 128, 128, 128); + setRGB0(&draw[1], 128, 128, 128); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + int i; + + MATRIX IDMATRIX = identity(3); // Generate 3x3 identity matrix + + POLY_GT4 *poly = {0}; // pointer to a POLY_G4 + SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z} + VECTOR MovVector = {0, 0, 120, 0}; // Initialize translation vector {x, y, z} + + SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg + {-32, -32, 0 }, // Vert 1 + {-32, 32, 0 }, // Vert 2 + { 32, -32, 0 }, // Vert 3 + { 32, 32, 0 } // Vert 4 + }; + MATRIX PolyMatrix = IDMATRIX; + + DR_TPAGE * bousai_tpage; + + long polydepth; + long polyflag; + + // Texture window + + DR_MODE * dr_mode; // Pointer to dr_mode prim + + RECT tws = {64, 32, 32, 32}; // Texture window coordinates : x, y, w, h + // See libref47.pdf, p.242, 7-6, table 7-2 for possible values + init(); + + LoadTexture(_binary_TIM_bousai_tim_start, &bousai); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + + poly = (POLY_GT4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer + + // Set transform matrices for this polygon + + RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix + TransMatrix(&PolyMatrix, &MovVector); // Apply translation matrix + + SetRotMatrix(&PolyMatrix); // Set default rotation matrix + SetTransMatrix(&PolyMatrix); // Set default transformation matrix + + setPolyGT4(poly); // Initialize poly as a POLY_F4 + poly->tpage = getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y); + + setRGB0(poly, 128, 128, 128); // Set vertice 1 color + setRGB1(poly, 255, 0, 0); // Set vertice 2 color + setRGB2(poly, 0, 255, 0); // Set vertice 3 color + setRGB3(poly, 0, 0, 255); // Set vertice 4 color + + RotTransPers4( + &VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3], + (long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3, + &polydepth, + &polyflag + ); // Perform coordinate and perspective transformation for 4 vertices + + setUV4(poly, 0, 0, 0, 144, 144, 0, 144, 144); // Set UV coordinates in order Top Left, Bottom Left, Top Right, Bottom Right + + RotVector.vy += 14; // Apply rotation on Z-axis. On PSX, the Z-axis is pointing away from the screen. + + addPrim(ot[db], poly); // add poly to the Ordering table + + nextpri += sizeof(POLY_GT4); // increment nextpri address with size of a POLY_GT4 struct + + // drawing mode primitive + + dr_mode = (DR_MODE *)nextpri; // initialize drawing mode primitive + + setDrawMode(dr_mode, 1, 0, getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y), &tws); //set texture window + + addPrim(ot[db], dr_mode); + + nextpri += sizeof(DR_MODE); // increment nextpri address with size of a DR_MODE struct + + + + FntPrint("Hello textured shaded !"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_poly_gt_tw/hello_poly_gt_tw.c b/hello_poly_gt_tw/hello_poly_gt_tw.c new file mode 100644 index 0000000..f01566d --- /dev/null +++ b/hello_poly_gt_tw/hello_poly_gt_tw.c @@ -0,0 +1,234 @@ +// With help from Nicolas Noble, Jaby smoll Seamonstah +// Based on Lameguy64's tutorial series : http://lameguy64.net/svn/pstutorials/chapter1/2-graphics.html +// +// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C : +// + /* PSX screen coordinate system + * + * Z+ + * / + * / + * +------X+ + * /| + * / | + * / Y+ + * eye */ + + +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 // Screen height + +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y + +#define MARGINX 32 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 5 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +// 16bpp TIM +extern unsigned long _binary____TIM_bousai_tim_start[]; +extern unsigned long _binary____TIM_bousai_tim_end[]; +extern unsigned long _binary____TIM_bousai_tim_length; + +TIM_IMAGE bousai; + + +MATRIX identity(int num) // generate num x num matrix +{ + int row, col; + MATRIX matrix; + + for (row = 0; row < num; row++) + { + for (col = 0; col < num; col++) + { + if (row == col) + matrix.m[row][col] = 4096; + else + matrix.m[row][col] = 0; + } + } + return matrix; +} + +void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous + OpenTIM(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 + } +} + +void init(void) +{ + ResetGraph(0); + + // Initialize and setup the GTE + + InitGeom(); + SetGeomOffset(CENTERX,CENTERY); + SetGeomScreen(CENTERX); + + 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; + } + + setRGB0(&draw[0], 128, 128, 128); + setRGB0(&draw[1], 128, 128, 128); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + int i; + + MATRIX IDMATRIX = identity(3); // Generate 3x3 identity matrix + + POLY_GT4 *poly = {0}; // pointer to a POLY_G4 + SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z} + VECTOR MovVector = {0, 0, 120, 0}; // Initialize translation vector {x, y, z} + + SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg + {-32, -32, 0 }, // Vert 1 + {-32, 32, 0 }, // Vert 2 + { 32, -32, 0 }, // Vert 3 + { 32, 32, 0 } // Vert 4 + }; + MATRIX PolyMatrix = IDMATRIX; + + DR_TPAGE * bousai_tpage; + + long polydepth; + long polyflag; + + // Texture window + + DR_MODE * dr_mode; // Pointer to dr_mode prim + + RECT tws = {64, 32, 32, 32}; // Texture window coordinates : x, y, w, h + // See libref47.pdf, p.242, 7-6, table 7-2 for possible values + init(); + + LoadTexture(_binary____TIM_bousai_tim_start, &bousai); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + + poly = (POLY_GT4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer + + // Set transform matrices for this polygon + + RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix + TransMatrix(&PolyMatrix, &MovVector); // Apply translation matrix + + SetRotMatrix(&PolyMatrix); // Set default rotation matrix + SetTransMatrix(&PolyMatrix); // Set default transformation matrix + + setPolyGT4(poly); // Initialize poly as a POLY_F4 + poly->tpage = getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y); + + setRGB0(poly, 128, 128, 128); // Set vertice 1 color + setRGB1(poly, 255, 0, 0); // Set vertice 2 color + setRGB2(poly, 0, 255, 0); // Set vertice 3 color + setRGB3(poly, 0, 0, 255); // Set vertice 4 color + + RotTransPers4( + &VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3], + (long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3, + &polydepth, + &polyflag + ); // Perform coordinate and perspective transformation for 4 vertices + + setUV4(poly, 0, 0, 0, 144, 144, 0, 144, 144); // Set UV coordinates in order Top Left, Bottom Left, Top Right, Bottom Right + + RotVector.vy += 14; // Apply rotation on Z-axis. On PSX, the Z-axis is pointing away from the screen. + + addPrim(ot[db], poly); // add poly to the Ordering table + + nextpri += sizeof(POLY_GT4); // increment nextpri address with size of a POLY_GT4 struct + + // drawing mode primitive + + dr_mode = (DR_MODE *)nextpri; // initialize drawing mode primitive + + setDrawMode(dr_mode, 1, 0, getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y), &tws); //set texture window + + addPrim(ot[db], dr_mode); + + nextpri += sizeof(DR_MODE); // increment nextpri address with size of a DR_MODE struct + + + + FntPrint("Hello textured shaded !"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_poly_inline.c b/hello_poly_inline.c new file mode 100644 index 0000000..c2b83f6 --- /dev/null +++ b/hello_poly_inline.c @@ -0,0 +1,214 @@ +// Hello poly ! Inline / DMPSX version +// +// Ref : /psyq/DOCS/Devrefs/Inlinref.pdf, p.18 +// https://psx-spx.consoledev.net/geometrytransformationenginegte/ +// PSX / Z+ +// screen / +//coordinate +-----X+ +//system / | +// eye | Y+ +// +// Credits, thanks : Nicolas Noble, Sickle, Lameguy64 @ psxdev discord : https://discord.com/invite/N2mmwp +// https://discord.com/channels/642647820683444236/663664210525290507/834831466100949002 +#include +#include +#include +#include +#include +// OldWorld PsyQ has a inline_c.h file for inline GTE functions. We have to use the one at https://github.com/grumpycoders/pcsx-redux/blob/07f9b02d1dbb68f57a9f5b9773041813c55a4913/src/mips/psyq/include/inline_n.h +// because the real GTE commands are needed in nugget : https://psx-spx.consoledev.net/geometrytransformationenginegte/#gte-coordinate-calculation-commands +#include +//~ #include // gtemac contains macro versions of the libgte functions, worth checking out to see the operations order. + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256 +#define CENTERX ( SCREENXRES >> 1 ) // Center of screen on x +#define CENTERY ( SCREENYRES >> 1 ) // Center of screen on y +#define MARGINX 0 // margins for text display +#define MARGINY 32 +#define FONTSIZE 8 * 7 // Text Field Height +#define OTLEN 10 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] +short db = 0; // index of which buffer is used, values 0, 1 + +void init(void) +{ + ResetGraph(0); + // Initialize and setup the GTE + InitGeom(); + //~ SetGeomOffset(CENTERX,CENTERY); + gte_SetGeomOffset(CENTERX,CENTERY); + gte_SetGeomScreen(CENTERX); + // Set display environment + SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES); + SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); + // Set draw environment + SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); + SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES); + // If PAL, use 320x256, hence 256 - 240 = 16 / 2 = 8 px vertical offset + if (VMODE) + { + SetVideoMode(MODE_PAL); + disp[0].screen.y += 8; + disp[1].screen.y += 8; + } + SetDispMask(1); + + // Set background color + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + draw[0].isbg = 1; + draw[1].isbg = 1; + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); +} + +void display(void) +{ + // Wait for drawing + DrawSync(0); + // Wait for vsync + VSync(0); + // Flip DISP and DRAW env + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + //~ SetDispMask(1); + DrawOTag(ot[db] + OTLEN - 1); + // Flip db index + db = !db; + // Get next primitive in buffer + nextpri = primbuff[db]; +} + +int main(void) +{ + long p, flag, OTz; + SVECTOR rotVector, rotVector4 = {0}; // Initialize rotation vector {x, y, z} + VECTOR transVector = {0, 0, CENTERX, 0}; // Initialize translation vector {x, y, z} + SVECTOR vertPos[4] = { + { 0, -32, 0, 0 }, // Vert 1 + { 32, 0, 0, 0 }, // Vert 2 + { -32, 0, 0, 0 }, + { 0, 32, 0, 0 } + }; // Vert 3 + MATRIX workMatrix = {0}; + POLY_F3 * poly = {0}; // pointer to a POLY_F4 + POLY_F4 * poly4 = {0}; // pointer to a POLY_F4 + init(); + while (1) + { + // Set Ordering table + ClearOTagR(ot[db], OTLEN); + + + // Draw on the left part of the screen + transVector.vx = -CENTERX/2; + // Increment rotation angle on Y axis + rotVector.vy += 8; + rotVector.vx -= 4 ; + // Find rotation matrix from vector, store in + RotMatrix_gte(&rotVector, &workMatrix); + // Ditto for translation + TransMatrix(&workMatrix, &transVector); + // Set the matrices we just found + gte_SetRotMatrix(&workMatrix); + gte_SetTransMatrix(&workMatrix); + + // Cast next primitive in buffer as a POLY_F4 (see display() ) + poly = (POLY_F3 *)nextpri; + + // Draw a Tri + + // Initialize poly as a POLY_F3 + setPolyF3(poly); + // Set poly color - Hot pink + setRGB0(poly, 255, 0, 255); + // Store vertex positions for current polygon in registers v0,v1,v2 + // Can be replaced by one gte_ldv3 call : + // gte_ldv3(&vertPos[0], &vertPos[1], &vertPos[2]); + gte_ldv0(&vertPos[0]); + gte_ldv1(&vertPos[1]); + gte_ldv2(&vertPos[2]); + + // RotTransPers3 : Perform coordinate and perspective transformation for three vertices. + // Use gte_rtps() for one vertex. + gte_rtpt(); + // Get screen coordinates from cop2 registers XY0,XY1,XY2 and store them in primitive's x0, y0, x1, y1, x2, y2 members. + // Can be replace with one gte_stsxy3() call : + // gte_stsxy3(&poly->x0, &poly->x1, &poly->x2); + // Can also be replaced with a primitive type dependant version : + // gte_stsxy3_f3(poly); + gte_stsxy0(&poly->x0); + gte_stsxy1(&poly->x1); + gte_stsxy2(&poly->x2); + // Get depth interpolation coefficient p + gte_stdp(&p); + // Get the flag - see libover47.pdf, p.143 for details on ppossible values + gte_stflg(&flag); + // Get screen coordinate Z/4 + gte_stszotz(&OTz); + // GTE macro version - needs 'gtemac.h' to be included - uncomment l.21 + //~ gte_RotTransPers3( &VertPos[0], &VertPos[1], &VertPos[2], + //~ &poly->x0, &poly->x1, &poly->x2, + //~ &p, &flag, &OTz ); + // add poly to the Ordering table + addPrim(ot[db], poly); + // increment nextpri address with size of a POLY_F3 struct + nextpri += sizeof(POLY_F3); + + // Draw a Quad + // + // The GTE rtpt can only transform 3 vertices at a time, so we have to do all operations as 3 + 1. + + // Move to right of screen + transVector.vx = CENTERX/2; + // Increment rot on X/Y axis + rotVector4.vy -= 8 ; + rotVector4.vx -= 4 ; + // Set matrices + RotMatrix_gte(&rotVector4, &workMatrix); + TransMatrix(&workMatrix, &transVector); + gte_SetRotMatrix(&workMatrix); + gte_SetTransMatrix(&workMatrix); + + // Cast a POLY_F4 at the address we just incremented. + poly4 = (POLY_F4 *)nextpri; + + // Initialize poly as a POLY_F4 + setPolyF4(poly4); + // Set Poly color - Blue + setRGB0(poly4, 0, 255, 255); + + // Transform 3 first vertices + gte_ldv3(&vertPos[0], &vertPos[1], &vertPos[2]); + gte_rtpt(); + gte_stsxy3_f4(poly4); + // Transform remaining vertex + gte_ldv0(&vertPos[3]); + gte_rtps(); + // SXY3 is set with gte_stsxy() or gte_stsxy2() ¯\_(ツ)_/¯ + gte_stsxy(&poly4->x3); + // Get p, flag and OTz + gte_stdp(&p); + gte_stflg(&flag); + gte_stszotz(&OTz); + + addPrim(ot[db], poly4); // add poly to the Ordering table + nextpri += sizeof(POLY_F4); // increment nextpri address with size of a POLY_F3 struct + + // Display text + FntPrint("Hello Inline GTE !\n"); + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_poly_inline/hello_poly_inline.c b/hello_poly_inline/hello_poly_inline.c new file mode 100644 index 0000000..c2b83f6 --- /dev/null +++ b/hello_poly_inline/hello_poly_inline.c @@ -0,0 +1,214 @@ +// Hello poly ! Inline / DMPSX version +// +// Ref : /psyq/DOCS/Devrefs/Inlinref.pdf, p.18 +// https://psx-spx.consoledev.net/geometrytransformationenginegte/ +// PSX / Z+ +// screen / +//coordinate +-----X+ +//system / | +// eye | Y+ +// +// Credits, thanks : Nicolas Noble, Sickle, Lameguy64 @ psxdev discord : https://discord.com/invite/N2mmwp +// https://discord.com/channels/642647820683444236/663664210525290507/834831466100949002 +#include +#include +#include +#include +#include +// OldWorld PsyQ has a inline_c.h file for inline GTE functions. We have to use the one at https://github.com/grumpycoders/pcsx-redux/blob/07f9b02d1dbb68f57a9f5b9773041813c55a4913/src/mips/psyq/include/inline_n.h +// because the real GTE commands are needed in nugget : https://psx-spx.consoledev.net/geometrytransformationenginegte/#gte-coordinate-calculation-commands +#include +//~ #include // gtemac contains macro versions of the libgte functions, worth checking out to see the operations order. + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256 +#define CENTERX ( SCREENXRES >> 1 ) // Center of screen on x +#define CENTERY ( SCREENYRES >> 1 ) // Center of screen on y +#define MARGINX 0 // margins for text display +#define MARGINY 32 +#define FONTSIZE 8 * 7 // Text Field Height +#define OTLEN 10 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] +short db = 0; // index of which buffer is used, values 0, 1 + +void init(void) +{ + ResetGraph(0); + // Initialize and setup the GTE + InitGeom(); + //~ SetGeomOffset(CENTERX,CENTERY); + gte_SetGeomOffset(CENTERX,CENTERY); + gte_SetGeomScreen(CENTERX); + // Set display environment + SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES); + SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); + // Set draw environment + SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); + SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES); + // If PAL, use 320x256, hence 256 - 240 = 16 / 2 = 8 px vertical offset + if (VMODE) + { + SetVideoMode(MODE_PAL); + disp[0].screen.y += 8; + disp[1].screen.y += 8; + } + SetDispMask(1); + + // Set background color + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + draw[0].isbg = 1; + draw[1].isbg = 1; + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); +} + +void display(void) +{ + // Wait for drawing + DrawSync(0); + // Wait for vsync + VSync(0); + // Flip DISP and DRAW env + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + //~ SetDispMask(1); + DrawOTag(ot[db] + OTLEN - 1); + // Flip db index + db = !db; + // Get next primitive in buffer + nextpri = primbuff[db]; +} + +int main(void) +{ + long p, flag, OTz; + SVECTOR rotVector, rotVector4 = {0}; // Initialize rotation vector {x, y, z} + VECTOR transVector = {0, 0, CENTERX, 0}; // Initialize translation vector {x, y, z} + SVECTOR vertPos[4] = { + { 0, -32, 0, 0 }, // Vert 1 + { 32, 0, 0, 0 }, // Vert 2 + { -32, 0, 0, 0 }, + { 0, 32, 0, 0 } + }; // Vert 3 + MATRIX workMatrix = {0}; + POLY_F3 * poly = {0}; // pointer to a POLY_F4 + POLY_F4 * poly4 = {0}; // pointer to a POLY_F4 + init(); + while (1) + { + // Set Ordering table + ClearOTagR(ot[db], OTLEN); + + + // Draw on the left part of the screen + transVector.vx = -CENTERX/2; + // Increment rotation angle on Y axis + rotVector.vy += 8; + rotVector.vx -= 4 ; + // Find rotation matrix from vector, store in + RotMatrix_gte(&rotVector, &workMatrix); + // Ditto for translation + TransMatrix(&workMatrix, &transVector); + // Set the matrices we just found + gte_SetRotMatrix(&workMatrix); + gte_SetTransMatrix(&workMatrix); + + // Cast next primitive in buffer as a POLY_F4 (see display() ) + poly = (POLY_F3 *)nextpri; + + // Draw a Tri + + // Initialize poly as a POLY_F3 + setPolyF3(poly); + // Set poly color - Hot pink + setRGB0(poly, 255, 0, 255); + // Store vertex positions for current polygon in registers v0,v1,v2 + // Can be replaced by one gte_ldv3 call : + // gte_ldv3(&vertPos[0], &vertPos[1], &vertPos[2]); + gte_ldv0(&vertPos[0]); + gte_ldv1(&vertPos[1]); + gte_ldv2(&vertPos[2]); + + // RotTransPers3 : Perform coordinate and perspective transformation for three vertices. + // Use gte_rtps() for one vertex. + gte_rtpt(); + // Get screen coordinates from cop2 registers XY0,XY1,XY2 and store them in primitive's x0, y0, x1, y1, x2, y2 members. + // Can be replace with one gte_stsxy3() call : + // gte_stsxy3(&poly->x0, &poly->x1, &poly->x2); + // Can also be replaced with a primitive type dependant version : + // gte_stsxy3_f3(poly); + gte_stsxy0(&poly->x0); + gte_stsxy1(&poly->x1); + gte_stsxy2(&poly->x2); + // Get depth interpolation coefficient p + gte_stdp(&p); + // Get the flag - see libover47.pdf, p.143 for details on ppossible values + gte_stflg(&flag); + // Get screen coordinate Z/4 + gte_stszotz(&OTz); + // GTE macro version - needs 'gtemac.h' to be included - uncomment l.21 + //~ gte_RotTransPers3( &VertPos[0], &VertPos[1], &VertPos[2], + //~ &poly->x0, &poly->x1, &poly->x2, + //~ &p, &flag, &OTz ); + // add poly to the Ordering table + addPrim(ot[db], poly); + // increment nextpri address with size of a POLY_F3 struct + nextpri += sizeof(POLY_F3); + + // Draw a Quad + // + // The GTE rtpt can only transform 3 vertices at a time, so we have to do all operations as 3 + 1. + + // Move to right of screen + transVector.vx = CENTERX/2; + // Increment rot on X/Y axis + rotVector4.vy -= 8 ; + rotVector4.vx -= 4 ; + // Set matrices + RotMatrix_gte(&rotVector4, &workMatrix); + TransMatrix(&workMatrix, &transVector); + gte_SetRotMatrix(&workMatrix); + gte_SetTransMatrix(&workMatrix); + + // Cast a POLY_F4 at the address we just incremented. + poly4 = (POLY_F4 *)nextpri; + + // Initialize poly as a POLY_F4 + setPolyF4(poly4); + // Set Poly color - Blue + setRGB0(poly4, 0, 255, 255); + + // Transform 3 first vertices + gte_ldv3(&vertPos[0], &vertPos[1], &vertPos[2]); + gte_rtpt(); + gte_stsxy3_f4(poly4); + // Transform remaining vertex + gte_ldv0(&vertPos[3]); + gte_rtps(); + // SXY3 is set with gte_stsxy() or gte_stsxy2() ¯\_(ツ)_/¯ + gte_stsxy(&poly4->x3); + // Get p, flag and OTz + gte_stdp(&p); + gte_stflg(&flag); + gte_stszotz(&OTz); + + addPrim(ot[db], poly4); // add poly to the Ordering table + nextpri += sizeof(POLY_F4); // increment nextpri address with size of a POLY_F3 struct + + // Display text + FntPrint("Hello Inline GTE !\n"); + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_sio.c b/hello_sio.c new file mode 100644 index 0000000..10aeca7 --- /dev/null +++ b/hello_sio.c @@ -0,0 +1,219 @@ +// hello_sio example +// +// This example will display the RX data in a 64 char rolling buffer. +// +// Use minicom or any other serial comm program and a serial/USB cable. +// +// Relevant doc is libref47.pdf, l.1120-1127 +// +// Schnappy - 04/2021 +// +// Based on : ../psyq/psx/sample/serial/SIO +// +// sio echo back +// 1.00 Jan.28.1997 shino + +#include +#include +#include +#include +#include + +// Needed for SIO operations + +#include + +// Needed for manipulating strings + +#include + +// Display stuff (see hello_tile for the basics) + +#define VMODE 0 + +#define SCREENXRES 320 + +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 + +#define CENTERY SCREENYRES/2 + +#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] = {0}; // Primitive list // That's our prim buffer + +char * nextpri = primbuff[0]; // Primitive counter + +short db = 0; // Current buffer counter + +// SIO + +#define MAX_CHARS 64 + +u_char SIO = 1; // Is SIO enabled ? + +u_char SIOinit = 0; // Is SIO initialized ? + +// Prototypes + +void init(void); + +void display(void); + +void init(){ + + // Reset the GPU before doing anything and the controller + + ResetGraph(0); + + // 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 in PAL mode, add vertical offset + + if (VMODE) + { + + SetVideoMode(MODE_PAL); + + disp[0].screen.y += 8; + + disp[1].screen.y += 8; + } + + setRGB0(&draw[0], 0, 0, 255); + + setRGB0(&draw[1], 0, 0, 255); + + draw[0].isbg = 1; + + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + + PutDrawEnv(&draw[db]); + + // Init font system + + FntLoad(960, 0); + + FntOpen(16, 16, 196, 64, 0, 256); + + } + +void display(void){ + + DrawSync(0); + + VSync(0); + + PutDispEnv(&disp[db]); + + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + db = !db; + + } + +int main() { + + init(); + + // Main loop + + while (1) { + + // Buffer for the RX data of size MAX_CHARS + + static char buffer[ MAX_CHARS ] = {0}; + + // If SIO flag is set, initialize and get data + + if( SIO ){ + + // Is SIO is not initialized, dot it + + if( ! SIOinit ){ + + ResetCallback(); + + // Load SIO driver at 115200bps + + AddSIO(115200); + + ResetGraph(0); + + // Use _sio_control to clear driver status error-related bits + // See psyq's libref47.pdf, p.1125 for the commands and status tables + + _sio_control(2,1,0); + + SIOinit = 1; + + } + + // Limit input buffer to MAX_CHARS chars, making it a rolling buffer + + if( strlen(buffer) > MAX_CHARS ){ + + // If that limit is reached, remove first char in string + + memmove(buffer, buffer + 1, strlen(buffer)); + + } + + // Check if sio driver is able to write communications data + // If so, this means reading is not occuring + + if( _sio_control(0,0,0) & SR_RXRDY ){ // SR_RXRDY == 0x2 + + // Read byte + + char c = _sio_control(0,4,0); + + // Add to buffer + + strncat(buffer, &c, 1); + + } + + + } + + // END SIO FUN + + FntPrint("Hello Serial!\n\n"); + + if( buffer ){ + + FntPrint("%s", buffer); + + } + + FntFlush(-1); + + display(); + + } + return 0; +} diff --git a/hello_sio/hello_sio.c b/hello_sio/hello_sio.c new file mode 100644 index 0000000..10aeca7 --- /dev/null +++ b/hello_sio/hello_sio.c @@ -0,0 +1,219 @@ +// hello_sio example +// +// This example will display the RX data in a 64 char rolling buffer. +// +// Use minicom or any other serial comm program and a serial/USB cable. +// +// Relevant doc is libref47.pdf, l.1120-1127 +// +// Schnappy - 04/2021 +// +// Based on : ../psyq/psx/sample/serial/SIO +// +// sio echo back +// 1.00 Jan.28.1997 shino + +#include +#include +#include +#include +#include + +// Needed for SIO operations + +#include + +// Needed for manipulating strings + +#include + +// Display stuff (see hello_tile for the basics) + +#define VMODE 0 + +#define SCREENXRES 320 + +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 + +#define CENTERY SCREENYRES/2 + +#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] = {0}; // Primitive list // That's our prim buffer + +char * nextpri = primbuff[0]; // Primitive counter + +short db = 0; // Current buffer counter + +// SIO + +#define MAX_CHARS 64 + +u_char SIO = 1; // Is SIO enabled ? + +u_char SIOinit = 0; // Is SIO initialized ? + +// Prototypes + +void init(void); + +void display(void); + +void init(){ + + // Reset the GPU before doing anything and the controller + + ResetGraph(0); + + // 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 in PAL mode, add vertical offset + + if (VMODE) + { + + SetVideoMode(MODE_PAL); + + disp[0].screen.y += 8; + + disp[1].screen.y += 8; + } + + setRGB0(&draw[0], 0, 0, 255); + + setRGB0(&draw[1], 0, 0, 255); + + draw[0].isbg = 1; + + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + + PutDrawEnv(&draw[db]); + + // Init font system + + FntLoad(960, 0); + + FntOpen(16, 16, 196, 64, 0, 256); + + } + +void display(void){ + + DrawSync(0); + + VSync(0); + + PutDispEnv(&disp[db]); + + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + db = !db; + + } + +int main() { + + init(); + + // Main loop + + while (1) { + + // Buffer for the RX data of size MAX_CHARS + + static char buffer[ MAX_CHARS ] = {0}; + + // If SIO flag is set, initialize and get data + + if( SIO ){ + + // Is SIO is not initialized, dot it + + if( ! SIOinit ){ + + ResetCallback(); + + // Load SIO driver at 115200bps + + AddSIO(115200); + + ResetGraph(0); + + // Use _sio_control to clear driver status error-related bits + // See psyq's libref47.pdf, p.1125 for the commands and status tables + + _sio_control(2,1,0); + + SIOinit = 1; + + } + + // Limit input buffer to MAX_CHARS chars, making it a rolling buffer + + if( strlen(buffer) > MAX_CHARS ){ + + // If that limit is reached, remove first char in string + + memmove(buffer, buffer + 1, strlen(buffer)); + + } + + // Check if sio driver is able to write communications data + // If so, this means reading is not occuring + + if( _sio_control(0,0,0) & SR_RXRDY ){ // SR_RXRDY == 0x2 + + // Read byte + + char c = _sio_control(0,4,0); + + // Add to buffer + + strncat(buffer, &c, 1); + + } + + + } + + // END SIO FUN + + FntPrint("Hello Serial!\n\n"); + + if( buffer ){ + + FntPrint("%s", buffer); + + } + + FntFlush(-1); + + display(); + + } + return 0; +} diff --git a/hello_sprt.c b/hello_sprt.c new file mode 100644 index 0000000..260d6e7 --- /dev/null +++ b/hello_sprt.c @@ -0,0 +1,232 @@ +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 32 // margins for text display +#define MARGINY 44 + +#define FONTSIZE 8 * 3 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +// Embed TIM files + +// See https://github.com/ABelliqueux/nolibgs_hello_worlds#embedding-binary-data-in-a-ps-exe + +// 16bpp TIM +extern unsigned long _binary_TIM_TIM16_tim_start[]; +extern unsigned long _binary_TIM_TIM16_tim_end[]; +extern unsigned long _binary_TIM_TIM16_tim_length; + +// 8bpp TIM +extern unsigned long _binary_TIM_TIM8_tim_start[]; +extern unsigned long _binary_TIM_TIM8_tim_end[]; +extern unsigned long _binary_TIM_TIM8_TIM_length; + +// 4bpp TIM +extern unsigned long _binary_TIM_TIM4_tim_start[]; +extern unsigned long _binary_TIM_TIM4_tim_end[]; +extern unsigned long _binary_TIM_TIM4_tim_length; + + +TIM_IMAGE TIM_16; +TIM_IMAGE TIM_8; +TIM_IMAGE TIM_4; + + +void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous + OpenTIM(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 + } +} + +void init(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + + SPRT * sprt_16b; // Define 3 pointers to SPRT struct + SPRT * sprt_8b; + SPRT * sprt_4b; + + DR_TPAGE * tpage_16b; // Define 3 pointers to DR_TPAGE struct. We need three because our images are on three + DR_TPAGE * tpage_8b; // different texture pages. + DR_TPAGE * tpage_4b; + + init(); + + LoadTexture(_binary_TIM_TIM16_tim_start, &TIM_16); // Load everything to vram + LoadTexture(_binary_TIM_TIM8_tim_start, &TIM_8); + LoadTexture(_binary_TIM_TIM4_tim_start, &TIM_4); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + // Loading a 16 bit TIM + + sprt_16b = (SPRT *)nextpri; // Cast whats at nexpri as a SPRT named sprt_16b + + setSprt(sprt_16b); // Initialize the SPRT struct + setRGB0(sprt_16b, 128, 128, 128); // Set RGB color. 128,128,128 is neutral. You can color the image by adjusting these values + setXY0(sprt_16b, 28, MARGINY); // Set sprite position + setWH(sprt_16b, 64, 128 ); // Set sprite width and height + + addPrim(ot[db], sprt_16b); // add the sprite primitive to the ordering table + + nextpri += sizeof(SPRT); // increment nextpri so that it points just after sprt_16b in the primitive buffer + + // Set Texture page for the 16bit tim : 768, 0 - No CLUT + + // Note : You need to use setDrawTPage each time you want to use a texture that's on a different texture page + + tpage_16b = (DR_TPAGE*)nextpri; + + setDrawTPage(tpage_16b, 0, 1, // Set the Texture Page the texture we want resides on. + getTPage(TIM_16.mode&0x3, 0, // Here we are using bitmasking to deduce the picture mode : &0x3 + TIM_16.prect->x, TIM_16.prect->y)); // In binary, 3 is 11, so we only keep the first two bits + // Values can be 00 (0), 01 (1), 10(2), respectively, 4bpp, 8bpp, 15bpp, 24bpp. See Fileformat47.pdf, p.180 + // Similarly, we could use bitmasking to deduce if there is a CLUT by bitmasking the 4th bit : if(TIM_IMAGE.mode & 0x8) LoadImage... : + + addPrim(ot[db], tpage_16b); // add the sprite primitive to the ordering table + + nextpri += sizeof(DR_TPAGE); // Advance next primitive address + + // Loading a 8 bit TIM + + sprt_8b = (SPRT *)nextpri; + + setSprt(sprt_8b); + setRGB0(sprt_8b, 128, 128, 128); + setXY0(sprt_8b, sprt_16b->x0 + sprt_16b->w + 32, MARGINY); + setWH(sprt_8b, 64, 128 ); + setClut(sprt_8b, TIM_8.crect->x, TIM_8.crect->y); // Only difference here is we set the CLUT to the position of the VRAM we loaded the palette earlier (see LoadTexture()) + + addPrim(ot[db], sprt_8b); + + nextpri += sizeof(SPRT); + + // Set Texture page for the 8bit tim : 512, 256 - CLUT is at 0, 480 + + tpage_8b = (DR_TPAGE*)nextpri; + + setDrawTPage(tpage_8b, 0, 1, + getTPage(TIM_8.mode&0x3, 0, + TIM_8.prect->x, TIM_8.prect->y)); + + addPrim(ot[db], tpage_8b); + nextpri += sizeof(DR_TPAGE); + + // Loading a 4 bit TIM + + sprt_4b = (SPRT *)nextpri; + + setSprt(sprt_4b); + setRGB0(sprt_4b, 128, 128, 128); + setXY0(sprt_4b, sprt_8b->x0 + sprt_8b->w + 32, MARGINY); + setWH(sprt_4b, 64, 128 ); + setClut(sprt_4b, TIM_4.crect->x, TIM_4.crect->y); + + addPrim(ot[db], sprt_4b); + + nextpri += sizeof(SPRT); + + // Set Texture page for the 4bit tim : 512, 256 - CLUT is at 0, 480 + + tpage_4b = (DR_TPAGE*)nextpri; + + setDrawTPage(tpage_4b, 0, 1, + getTPage(TIM_4.mode&0x3, 0, + TIM_4.prect->x, TIM_4.prect->y)); + + addPrim(ot[db], tpage_4b); + + nextpri += sizeof(DR_TPAGE); + + FntPrint("16 Bit! "); + FntPrint("8 Bit! "); + FntPrint("4 Bit!\n\n"); + FntPrint("Check VRAM in emu to see the dif"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_sprt/hello_sprt.c b/hello_sprt/hello_sprt.c new file mode 100644 index 0000000..5b1f11e --- /dev/null +++ b/hello_sprt/hello_sprt.c @@ -0,0 +1,232 @@ +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 32 // margins for text display +#define MARGINY 44 + +#define FONTSIZE 8 * 3 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +// Embed TIM files + +// See https://github.com/ABelliqueux/nolibgs_hello_worlds#embedding-binary-data-in-a-ps-exe + +// 16bpp TIM +extern unsigned long _binary____TIM_TIM16_tim_start[]; +extern unsigned long _binary____TIM_TIM16_tim_end[]; +extern unsigned long _binary____TIM_TIM16_tim_length; + +// 8bpp TIM +extern unsigned long _binary____TIM_TIM8_tim_start[]; +extern unsigned long _binary____TIM_TIM8_tim_end[]; +extern unsigned long _binary____TIM_TIM8_TIM_length; + +// 4bpp TIM +extern unsigned long _binary____TIM_TIM4_tim_start[]; +extern unsigned long _binary____TIM_TIM4_tim_end[]; +extern unsigned long _binary____TIM_TIM4_tim_length; + + +TIM_IMAGE TIM_16; +TIM_IMAGE TIM_8; +TIM_IMAGE TIM_4; + + +void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous + OpenTIM(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 + } +} + +void init(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + DrawOTag(ot[db] + OTLEN - 1); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + + SPRT * sprt_16b; // Define 3 pointers to SPRT struct + SPRT * sprt_8b; + SPRT * sprt_4b; + + DR_TPAGE * tpage_16b; // Define 3 pointers to DR_TPAGE struct. We need three because our images are on three + DR_TPAGE * tpage_8b; // different texture pages. + DR_TPAGE * tpage_4b; + + init(); + + LoadTexture(_binary____TIM_TIM16_tim_start, &TIM_16); // Load everything to vram + LoadTexture(_binary____TIM_TIM8_tim_start, &TIM_8); + LoadTexture(_binary____TIM_TIM4_tim_start, &TIM_4); + + while (1) + { + ClearOTagR(ot[db], OTLEN); + + // Loading a 16 bit TIM + + sprt_16b = (SPRT *)nextpri; // Cast whats at nexpri as a SPRT named sprt_16b + + setSprt(sprt_16b); // Initialize the SPRT struct + setRGB0(sprt_16b, 128, 128, 128); // Set RGB color. 128,128,128 is neutral. You can color the image by adjusting these values + setXY0(sprt_16b, 28, MARGINY); // Set sprite position + setWH(sprt_16b, 64, 128 ); // Set sprite width and height + + addPrim(ot[db], sprt_16b); // add the sprite primitive to the ordering table + + nextpri += sizeof(SPRT); // increment nextpri so that it points just after sprt_16b in the primitive buffer + + // Set Texture page for the 16bit tim : 768, 0 - No CLUT + + // Note : You need to use setDrawTPage each time you want to use a texture that's on a different texture page + + tpage_16b = (DR_TPAGE*)nextpri; + + setDrawTPage(tpage_16b, 0, 1, // Set the Texture Page the texture we want resides on. + getTPage(TIM_16.mode&0x3, 0, // Here we are using bitmasking to deduce the picture mode : &0x3 + TIM_16.prect->x, TIM_16.prect->y)); // In binary, 3 is 11, so we only keep the first two bits + // Values can be 00 (0), 01 (1), 10(2), respectively, 4bpp, 8bpp, 15bpp, 24bpp. See Fileformat47.pdf, p.180 + // Similarly, we could use bitmasking to deduce if there is a CLUT by bitmasking the 4th bit : if(TIM_IMAGE.mode & 0x8) LoadImage... : + + addPrim(ot[db], tpage_16b); // add the sprite primitive to the ordering table + + nextpri += sizeof(DR_TPAGE); // Advance next primitive address + + // Loading a 8 bit TIM + + sprt_8b = (SPRT *)nextpri; + + setSprt(sprt_8b); + setRGB0(sprt_8b, 128, 128, 128); + setXY0(sprt_8b, sprt_16b->x0 + sprt_16b->w + 32, MARGINY); + setWH(sprt_8b, 64, 128 ); + setClut(sprt_8b, TIM_8.crect->x, TIM_8.crect->y); // Only difference here is we set the CLUT to the position of the VRAM we loaded the palette earlier (see LoadTexture()) + + addPrim(ot[db], sprt_8b); + + nextpri += sizeof(SPRT); + + // Set Texture page for the 8bit tim : 512, 256 - CLUT is at 0, 480 + + tpage_8b = (DR_TPAGE*)nextpri; + + setDrawTPage(tpage_8b, 0, 1, + getTPage(TIM_8.mode&0x3, 0, + TIM_8.prect->x, TIM_8.prect->y)); + + addPrim(ot[db], tpage_8b); + nextpri += sizeof(DR_TPAGE); + + // Loading a 4 bit TIM + + sprt_4b = (SPRT *)nextpri; + + setSprt(sprt_4b); + setRGB0(sprt_4b, 128, 128, 128); + setXY0(sprt_4b, sprt_8b->x0 + sprt_8b->w + 32, MARGINY); + setWH(sprt_4b, 64, 128 ); + setClut(sprt_4b, TIM_4.crect->x, TIM_4.crect->y); + + addPrim(ot[db], sprt_4b); + + nextpri += sizeof(SPRT); + + // Set Texture page for the 4bit tim : 512, 256 - CLUT is at 0, 480 + + tpage_4b = (DR_TPAGE*)nextpri; + + setDrawTPage(tpage_4b, 0, 1, + getTPage(TIM_4.mode&0x3, 0, + TIM_4.prect->x, TIM_4.prect->y)); + + addPrim(ot[db], tpage_4b); + + nextpri += sizeof(DR_TPAGE); + + FntPrint("16 Bit! "); + FntPrint("8 Bit! "); + FntPrint("4 Bit!\n\n"); + FntPrint("Check VRAM in emu to see the dif"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_tile.c b/hello_tile.c new file mode 100644 index 0000000..bed4da6 --- /dev/null +++ b/hello_tile.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1};// double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +void init(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + // We're using a reverse OT, so we want to display the last item first. See PsyQ's LibRef47.pdf, p.277 + DrawOTag(ot[db] + OTLEN - 1); + // Uncomment the following line to use a regular oredered OT. Uncomment l.100 accordingly + //~ DrawOTag(ot[db]); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + // These two tiles are added at the same OT index + TILE * blue_tile; + TILE * pink_tile; + // This one is added at a different OT index + TILE * yellow_tile; + + init(); + + while (1) + { + // Initialize the reversed ordering table. This means the elements at index OTLEN - 1 is drawn first. + ClearOTagR(ot[db], OTLEN); + // Use regular order OT, uncomment l.77 accordingly + //~ ClearOTag(ot[db], OTLEN); + + // yellow_tile is before pink and blue tile in the code, + // and it displays behind because it is added to a different ot index (od[db] + OTLEN - 1) + // Using a Regular or Reverse OT will have an effect on drawing order. (See lines 77 and 100) + + yellow_tile = (TILE * ) nextpri; // yellow_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a TILE struc. + + setTile(yellow_tile); // initialize the TILE structure ( fill the length and tag(?) value ) + setXY0(yellow_tile, CENTERX - 32 , CENTERY - 48); // Set X,Y + setWH(yellow_tile, 128, 40); // Set Width, Height + setRGB0(yellow_tile, 255, 255, 0); // Set color + addPrim(ot[db] + OTLEN - 1, yellow_tile); // Add primitive to ordering table + + nextpri += sizeof(TILE); + + // blue_tile added at od[db] + OTLEN - 2 + + blue_tile = (TILE * ) nextpri; // blue_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a blue_tile struc. + + setTile(blue_tile); // initialize the blue_tile structure ( fill the length and tag(?) value ) + setXY0(blue_tile, CENTERX - 16, CENTERY - 32); // Set X,Y + setWH(blue_tile, 32, 64); // Set Width, Height + setRGB0(blue_tile, 60, 180, 255); // Set color + addPrim(ot[db] + OTLEN - 2, blue_tile); // Add primitive to ordering table + + nextpri += sizeof(TILE); // Increment the adress nextpri points to by the size of TILE struct + + // pink_tile is after blue_tile in the code, + // so it is drawn before, thus under blue_tile. + // However, it is added at the same ot index (od[db] + OTLEN - 2) + // so using a Regular or Reverse OT won't have an effect on drawing order. + + pink_tile = (TILE * ) nextpri; // pink_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a TILE struc. + + setTile(pink_tile); // initialize the TILE structure ( fill the length and tag(?) value ) + setXY0(pink_tile, CENTERX, CENTERY - 64); // Set X,Y + setWH(pink_tile, 64, 64); // Set Width, Height + setRGB0(pink_tile, 255, 32, 255); // Set color + addPrim(ot[db] + OTLEN - 2, pink_tile); // Add primitive to ordering table + + nextpri += sizeof(TILE); + + FntPrint("Hello tile !"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_tile/hello_tile.c b/hello_tile/hello_tile.c new file mode 100644 index 0000000..bed4da6 --- /dev/null +++ b/hello_tile/hello_tile.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +#define OTLEN 8 // Ordering Table Length + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes + +char primbuff[2][32768] = {1};// double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes + +char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0] + +short db = 0; // index of which buffer is used, values 0, 1 + +void init(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + // We're using a reverse OT, so we want to display the last item first. See PsyQ's LibRef47.pdf, p.277 + DrawOTag(ot[db] + OTLEN - 1); + // Uncomment the following line to use a regular oredered OT. Uncomment l.100 accordingly + //~ DrawOTag(ot[db]); + + db = !db; + + nextpri = primbuff[db]; +} + + +int main(void) +{ + // These two tiles are added at the same OT index + TILE * blue_tile; + TILE * pink_tile; + // This one is added at a different OT index + TILE * yellow_tile; + + init(); + + while (1) + { + // Initialize the reversed ordering table. This means the elements at index OTLEN - 1 is drawn first. + ClearOTagR(ot[db], OTLEN); + // Use regular order OT, uncomment l.77 accordingly + //~ ClearOTag(ot[db], OTLEN); + + // yellow_tile is before pink and blue tile in the code, + // and it displays behind because it is added to a different ot index (od[db] + OTLEN - 1) + // Using a Regular or Reverse OT will have an effect on drawing order. (See lines 77 and 100) + + yellow_tile = (TILE * ) nextpri; // yellow_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a TILE struc. + + setTile(yellow_tile); // initialize the TILE structure ( fill the length and tag(?) value ) + setXY0(yellow_tile, CENTERX - 32 , CENTERY - 48); // Set X,Y + setWH(yellow_tile, 128, 40); // Set Width, Height + setRGB0(yellow_tile, 255, 255, 0); // Set color + addPrim(ot[db] + OTLEN - 1, yellow_tile); // Add primitive to ordering table + + nextpri += sizeof(TILE); + + // blue_tile added at od[db] + OTLEN - 2 + + blue_tile = (TILE * ) nextpri; // blue_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a blue_tile struc. + + setTile(blue_tile); // initialize the blue_tile structure ( fill the length and tag(?) value ) + setXY0(blue_tile, CENTERX - 16, CENTERY - 32); // Set X,Y + setWH(blue_tile, 32, 64); // Set Width, Height + setRGB0(blue_tile, 60, 180, 255); // Set color + addPrim(ot[db] + OTLEN - 2, blue_tile); // Add primitive to ordering table + + nextpri += sizeof(TILE); // Increment the adress nextpri points to by the size of TILE struct + + // pink_tile is after blue_tile in the code, + // so it is drawn before, thus under blue_tile. + // However, it is added at the same ot index (od[db] + OTLEN - 2) + // so using a Regular or Reverse OT won't have an effect on drawing order. + + pink_tile = (TILE * ) nextpri; // pink_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a TILE struc. + + setTile(pink_tile); // initialize the TILE structure ( fill the length and tag(?) value ) + setXY0(pink_tile, CENTERX, CENTERY - 64); // Set X,Y + setWH(pink_tile, 64, 64); // Set Width, Height + setRGB0(pink_tile, 255, 32, 255); // Set color + addPrim(ot[db] + OTLEN - 2, pink_tile); // Add primitive to ordering table + + nextpri += sizeof(TILE); + + FntPrint("Hello tile !"); + + FntFlush(-1); + + display(); + } + return 0; + } diff --git a/hello_vag.c b/hello_vag.c new file mode 100644 index 0000000..b70a143 --- /dev/null +++ b/hello_vag.c @@ -0,0 +1,274 @@ +// VAGDEMO2020 by Schnappy +// December 2020 +// Based on VAGDEMO_FIXED by Yagotzirck +// Based on VAGDEMO by Shadow +// based on psyq/addons/sound/TUTO3.C +// +// +// Load a VAG file to SPU sound buffer and play it back. +// +// WAV creation: use ffmpeg to create a 16-bit ADPCM mono WAV file - change -ar to reduce filesize (and quality) +// $ ffmpeg -i input.mp3 -acodec pcm_s16le -ac 1 -ar 44100 output.wav +// +// WAV to VAG convertion using WAV2VAG : https://github.com/ColdSauce/psxsdk/blob/master/tools/wav2vag.c +// change -freq according to the -ar setting above +// $ wav2vag input.wav output.vag -sraw16 -freq=44100 (-L) +// +// Alternatively, you can use PsyQ VAGEDIT.EXE to change the sampling frequency of an existing VAG file. +// +// Docs : see libformat47.pdf p.209 +// libover47.pdf, p.271 +// libref47.pdf, p.980 +// URLS : http://psx.arthus.net/code/VAG/ + +#include +#include + +#include +#include +#include + + +// Sound system +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +short db = 0; // index of which buffer is used, values 0, 1 + +// Sound stuff + +#define MALLOC_MAX 3 // Max number of time we can call SpuMalloc + +//~ // convert Little endian to Big endian +#define SWAP_ENDIAN32(x) (((x)>>24) | (((x)>>8) & 0xFF00) | (((x)<<8) & 0x00FF0000) | ((x)<<24)) + +typedef struct VAGheader{ // All the values in this header must be big endian + char id[4]; // VAGp 4 bytes -> 1 char * 4 + unsigned int version; // 4 bytes + unsigned int reserved; // 4 bytes + unsigned int dataSize; // (in bytes) 4 bytes + unsigned int samplingFrequency;// 4 bytes + char reserved2[12]; // 12 bytes -> 1 char * 12 + char name[16]; // 16 bytes -> 1 char * 16 + // Waveform data after that +}VAGhdr; + +SpuCommonAttr commonAttributes; // structure for changing common voice attributes +SpuVoiceAttr voiceAttributes ; // structure for changing individual voice attributes + +u_long vag_spu_address; // address allocated in memory for first sound file + +// DEBUG : these allow printing values for debugging + +u_long spu_start_address; +u_long get_start_addr; +u_long transSize; + +// Memory management table ; allow MALLOC_MAX calls to SpuMalloc() - ibref47.pdf p.1044 +char spu_malloc_rec[SPU_MALLOC_RECSIZ * (2 + MALLOC_MAX+1)]; + +// VAG files + +// We're using GrumpyCoder's Nugget wrapper to compile the code with a modern GCC : https://github.com/grumpycoders/pcsx-redux/tree/main/src/mips/psyq +// To include binary files in the exe, add your VAG files to the SRCS variable in Makefile +// and in common.mk, add this rule to include *.vag files : +// +//~ %.o: %.vag + //~ $(PREFIX)-objcopy -I binary --set-section-alignment .data=4 --rename-section .data=.rodata,alloc,load,readonly,data,contents -O elf32-tradlittlemips -B mips $< $@ + + +// hello_poly.vag - 44100 Khz +extern unsigned char _binary_VAG_hello_poly_vag_start[]; // filename must begin with _binary_ followed by the full path, with . and / replaced, and then suffixed with _ and end with _start[]; or end[]; +extern unsigned char _binary_VAG_hello_poly_vag_end[]; // https://discord.com/channels/642647820683444236/663664210525290507/780866265077383189 + +void initGraph(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(8, 60, 304, 200, 0, 500 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + db = !db; + +} + + +// Audio initialisation & functions + +void initSnd(void){ + + SpuInitMalloc(MALLOC_MAX, spu_malloc_rec); // Maximum number of blocks, mem. management table address. + + commonAttributes.mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR); // Mask which attributes to set + commonAttributes.mvol.left = 0x3fff; // Master volume left + commonAttributes.mvol.right = 0x3fff; // see libref47.pdf, p.1058 + + SpuSetCommonAttr(&commonAttributes); // set attributes + + SpuSetIRQ(SPU_OFF); +} + +u_long sendVAGtoRAM(unsigned int VAG_data_size, unsigned char *VAG_data){ + u_long size; + + SpuSetTransferMode(SpuTransByDMA); // DMA transfer; can do other processing during transfer + + size = SpuWrite (VAG_data + sizeof(VAGhdr), VAG_data_size); // transfer VAG_data_size bytes from VAG_data address to sound buffer + + SpuIsTransferCompleted (SPU_TRANSFER_WAIT); // Checks whether transfer is completed and waits for completion + + return size; +} + +void setVoiceAttr(unsigned int pitch, long channel, unsigned long soundAddr ){ + + voiceAttributes.mask= //~ Attributes (bit string, 1 bit per attribute) + ( + SPU_VOICE_VOLL | + SPU_VOICE_VOLR | + SPU_VOICE_PITCH | + SPU_VOICE_WDSA | + SPU_VOICE_ADSR_AMODE | + SPU_VOICE_ADSR_SMODE | + SPU_VOICE_ADSR_RMODE | + SPU_VOICE_ADSR_AR | + SPU_VOICE_ADSR_DR | + SPU_VOICE_ADSR_SR | + SPU_VOICE_ADSR_RR | + SPU_VOICE_ADSR_SL + ); + + voiceAttributes.voice = channel; //~ Voice (low 24 bits are a bit string, 1 bit per voice ) + + voiceAttributes.volume.left = 0x1000; //~ Volume + voiceAttributes.volume.right = 0x1000; //~ Volume + + voiceAttributes.pitch = pitch; //~ Interval (set pitch) + voiceAttributes.addr = soundAddr; //~ Waveform data start address + + voiceAttributes.a_mode = SPU_VOICE_LINEARIncN; //~ Attack rate mode = Linear Increase - see libref47.pdf p.1091 + voiceAttributes.s_mode = SPU_VOICE_LINEARIncN; //~ Sustain rate mode = Linear Increase + voiceAttributes.r_mode = SPU_VOICE_LINEARDecN; //~ Release rate mode = Linear Decrease + + voiceAttributes.ar = 0x0; //~ Attack rate + voiceAttributes.dr = 0x0; //~ Decay rate + voiceAttributes.rr = 0x0; //~ Release rate + voiceAttributes.sr = 0x0; //~ Sustain rate + voiceAttributes.sl = 0xf; //~ Sustain level + + SpuSetVoiceAttr(&voiceAttributes); // set attributes + +} + +void playSFX(void){ + SpuSetKey(SpuOn,SPU_0CH); // Set several channels by ORing each channel bit ; ex : SpuSetKey(SpuOn,SPU_0CH | SPU_3CH | SPU_8CH); channels 0, 3, 8 are on. +} + +int main(void) +{ + short counter = 0; + + const VAGhdr * VAGfileHeader = (VAGhdr *) _binary_VAG_hello_poly_vag_start; // get header of VAG file + + // From libover47.pdf : + // The sampling frequency of the original audio file can be used to determine the pitch + // at which to play the VAG. pitch = (sampling frequency << 12)/44100L + // Ex: 44.1kHz=0x1000 22.05kHz=0x800 etc + + unsigned int pitch = (SWAP_ENDIAN32(VAGfileHeader->samplingFrequency) << 12) / 44100L; + + SpuInit(); // Initialize SPU. Called only once. + + initSnd(); + + //~ // First VAG + + vag_spu_address = SpuMalloc(SWAP_ENDIAN32(VAGfileHeader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer. + + spu_start_address = SpuSetTransferStartAddr(vag_spu_address); // Sets a starting address in the sound buffer + + get_start_addr = SpuGetTransferStartAddr(); // SpuGetTransferStartAddr() returns current sound buffer transfer start address. + + transSize = sendVAGtoRAM(SWAP_ENDIAN32(VAGfileHeader->dataSize), _binary_VAG_hello_poly_vag_start); + + // set VAG to channel + + setVoiceAttr(pitch, SPU_0CH, vag_spu_address); + + initGraph(); + + while (1) + { + + if(!counter){ + playSFX(); + counter = 180; + } + + FntPrint("\nPitch : %08x-%dKhz", pitch, (SWAP_ENDIAN32(VAGfileHeader->samplingFrequency)) ); + FntPrint("\nSet Start addr : %08x", vag_spu_address); + FntPrint("\nReturn start addr : %08x", spu_start_address); + FntPrint("\nGet Start addr : %08x", get_start_addr); + FntPrint("\nSend size : %08x", SWAP_ENDIAN32(VAGfileHeader->dataSize)); + FntPrint("\nReturn size : %08x\n", transSize); + FntPrint("\nCounter : %d\n", counter); + + FntFlush(-1); + + counter --; + + display(); + + } + return 0; + } diff --git a/hello_vag/hello_vag.c b/hello_vag/hello_vag.c new file mode 100644 index 0000000..582b497 --- /dev/null +++ b/hello_vag/hello_vag.c @@ -0,0 +1,275 @@ +// VAGDEMO2020 by Schnappy +// December 2020 +// Based on VAGDEMO_FIXED by Yagotzirck +// Based on VAGDEMO by Shadow +// based on psyq/addons/sound/TUTO3.C +// +// +// Load a VAG file to SPU sound buffer and play it back. +// +// WAV creation: use ffmpeg to create a 16-bit ADPCM mono WAV file - change -ar to reduce filesize (and quality) +// $ ffmpeg -i input.mp3 -acodec pcm_s16le -ac 1 -ar 44100 output.wav +// +// WAV to VAG convertion using WAV2VAG : https://github.com/ColdSauce/psxsdk/blob/master/tools/wav2vag.c +// change -freq according to the -ar setting above +// $ wav2vag input.wav output.vag -sraw16 -freq=44100 (-L) +// +// Alternatively, you can use PsyQ VAGEDIT.EXE to change the sampling frequency of an existing VAG file. +// +// Docs : see libformat47.pdf p.209 +// libover47.pdf, p.271 +// libref47.pdf, p.980 +// URLS : http://psx.arthus.net/code/VAG/ + +#include +#include + +#include +#include +#include + + +// Sound system +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 +#define SCREENYRES 240 + +#define CENTERX SCREENXRES/2 +#define CENTERY SCREENYRES/2 + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +short db = 0; // index of which buffer is used, values 0, 1 + +// Sound stuff + +#define MALLOC_MAX 3 // Max number of time we can call SpuMalloc + +//~ // convert Little endian to Big endian +#define SWAP_ENDIAN32(x) (((x)>>24) | (((x)>>8) & 0xFF00) | (((x)<<8) & 0x00FF0000) | ((x)<<24)) + +typedef struct VAGheader{ // All the values in this header must be big endian + char id[4]; // VAGp 4 bytes -> 1 char * 4 + unsigned int version; // 4 bytes + unsigned int reserved; // 4 bytes + unsigned int dataSize; // (in bytes) 4 bytes + unsigned int samplingFrequency;// 4 bytes + char reserved2[12]; // 12 bytes -> 1 char * 12 + char name[16]; // 16 bytes -> 1 char * 16 + // Waveform data after that +}VAGhdr; + +SpuCommonAttr commonAttributes; // structure for changing common voice attributes +SpuVoiceAttr voiceAttributes ; // structure for changing individual voice attributes + +u_long vag_spu_address; // address allocated in memory for first sound file + +// DEBUG : these allow printing values for debugging + +u_long spu_start_address; +u_long get_start_addr; +u_long transSize; + +// Memory management table ; allow MALLOC_MAX calls to SpuMalloc() - ibref47.pdf p.1044 +char spu_malloc_rec[SPU_MALLOC_RECSIZ * (2 + MALLOC_MAX+1)]; + +// VAG files + +// We're using GrumpyCoder's Nugget wrapper to compile the code with a modern GCC : https://github.com/grumpycoders/pcsx-redux/tree/main/src/mips/psyq +// To include binary files in the exe, add your VAG files to the SRCS variable in Makefile +// and in common.mk, add this rule to include *.vag files : +// +//~ %.o: %.vag + //~ $(PREFIX)-objcopy -I binary --set-section-alignment .data=4 --rename-section .data=.rodata,alloc,load,readonly,data,contents -O elf32-tradlittlemips -B mips $< $@ + + +// hello_poly.vag - 44100 Khz +extern unsigned char _binary____VAG_hello_poly_vag_start[]; // filename must begin with _binary_ followed by the full path, with . and / replaced, and then suffixed with _ and end with _start[]; or end[]; +extern unsigned char _binary____VAG_hello_poly_vag_end[]; // Going up one directory level is 4 '_' : ____ as ./ is replaced by __ + // https://discord.com/channels/642647820683444236/663664210525290507/780866265077383189 + +void initGraph(void) +{ + ResetGraph(0); + + 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; + } + + setRGB0(&draw[0], 50, 50, 50); + setRGB0(&draw[1], 50, 50, 50); + + draw[0].isbg = 1; + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); + FntOpen(8, 60, 304, 200, 0, 500 ); + +} + +void display(void) +{ + DrawSync(0); + VSync(0); + + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + SetDispMask(1); + + db = !db; + +} + + +// Audio initialisation & functions + +void initSnd(void){ + + SpuInitMalloc(MALLOC_MAX, spu_malloc_rec); // Maximum number of blocks, mem. management table address. + + commonAttributes.mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR); // Mask which attributes to set + commonAttributes.mvol.left = 0x3fff; // Master volume left + commonAttributes.mvol.right = 0x3fff; // see libref47.pdf, p.1058 + + SpuSetCommonAttr(&commonAttributes); // set attributes + + SpuSetIRQ(SPU_OFF); +} + +u_long sendVAGtoRAM(unsigned int VAG_data_size, unsigned char *VAG_data){ + u_long size; + + SpuSetTransferMode(SpuTransByDMA); // DMA transfer; can do other processing during transfer + + size = SpuWrite (VAG_data + sizeof(VAGhdr), VAG_data_size); // transfer VAG_data_size bytes from VAG_data address to sound buffer + + SpuIsTransferCompleted (SPU_TRANSFER_WAIT); // Checks whether transfer is completed and waits for completion + + return size; +} + +void setVoiceAttr(unsigned int pitch, long channel, unsigned long soundAddr ){ + + voiceAttributes.mask= //~ Attributes (bit string, 1 bit per attribute) + ( + SPU_VOICE_VOLL | + SPU_VOICE_VOLR | + SPU_VOICE_PITCH | + SPU_VOICE_WDSA | + SPU_VOICE_ADSR_AMODE | + SPU_VOICE_ADSR_SMODE | + SPU_VOICE_ADSR_RMODE | + SPU_VOICE_ADSR_AR | + SPU_VOICE_ADSR_DR | + SPU_VOICE_ADSR_SR | + SPU_VOICE_ADSR_RR | + SPU_VOICE_ADSR_SL + ); + + voiceAttributes.voice = channel; //~ Voice (low 24 bits are a bit string, 1 bit per voice ) + + voiceAttributes.volume.left = 0x1000; //~ Volume + voiceAttributes.volume.right = 0x1000; //~ Volume + + voiceAttributes.pitch = pitch; //~ Interval (set pitch) + voiceAttributes.addr = soundAddr; //~ Waveform data start address + + voiceAttributes.a_mode = SPU_VOICE_LINEARIncN; //~ Attack rate mode = Linear Increase - see libref47.pdf p.1091 + voiceAttributes.s_mode = SPU_VOICE_LINEARIncN; //~ Sustain rate mode = Linear Increase + voiceAttributes.r_mode = SPU_VOICE_LINEARDecN; //~ Release rate mode = Linear Decrease + + voiceAttributes.ar = 0x0; //~ Attack rate + voiceAttributes.dr = 0x0; //~ Decay rate + voiceAttributes.rr = 0x0; //~ Release rate + voiceAttributes.sr = 0x0; //~ Sustain rate + voiceAttributes.sl = 0xf; //~ Sustain level + + SpuSetVoiceAttr(&voiceAttributes); // set attributes + +} + +void playSFX(void){ + SpuSetKey(SpuOn,SPU_0CH); // Set several channels by ORing each channel bit ; ex : SpuSetKey(SpuOn,SPU_0CH | SPU_3CH | SPU_8CH); channels 0, 3, 8 are on. +} + +int main(void) +{ + short counter = 0; + + const VAGhdr * VAGfileHeader = (VAGhdr *) _binary____VAG_hello_poly_vag_start; // get header of VAG file + + // From libover47.pdf : + // The sampling frequency of the original audio file can be used to determine the pitch + // at which to play the VAG. pitch = (sampling frequency << 12)/44100L + // Ex: 44.1kHz=0x1000 22.05kHz=0x800 etc + + unsigned int pitch = (SWAP_ENDIAN32(VAGfileHeader->samplingFrequency) << 12) / 44100L; + + SpuInit(); // Initialize SPU. Called only once. + + initSnd(); + + //~ // First VAG + + vag_spu_address = SpuMalloc(SWAP_ENDIAN32(VAGfileHeader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer. + + spu_start_address = SpuSetTransferStartAddr(vag_spu_address); // Sets a starting address in the sound buffer + + get_start_addr = SpuGetTransferStartAddr(); // SpuGetTransferStartAddr() returns current sound buffer transfer start address. + + transSize = sendVAGtoRAM(SWAP_ENDIAN32(VAGfileHeader->dataSize), _binary____VAG_hello_poly_vag_start); + + // set VAG to channel + + setVoiceAttr(pitch, SPU_0CH, vag_spu_address); + + initGraph(); + + while (1) + { + + if(!counter){ + playSFX(); + counter = 180; + } + + FntPrint("\nPitch : %08x-%dKhz", pitch, (SWAP_ENDIAN32(VAGfileHeader->samplingFrequency)) ); + FntPrint("\nSet Start addr : %08x", vag_spu_address); + FntPrint("\nReturn start addr : %08x", spu_start_address); + FntPrint("\nGet Start addr : %08x", get_start_addr); + FntPrint("\nSend size : %08x", SWAP_ENDIAN32(VAGfileHeader->dataSize)); + FntPrint("\nReturn size : %08x\n", transSize); + FntPrint("\nCounter : %d\n", counter); + + FntFlush(-1); + + counter --; + + display(); + + } + return 0; + } diff --git a/hello_world.c b/hello_world.c new file mode 100644 index 0000000..ac96f85 --- /dev/null +++ b/hello_world.c @@ -0,0 +1,86 @@ +// This is stolen from Lameguy64 tutorial : http://lameguy64.net/svn/pstutorials/chapter1/1-display.html + +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 // Screen height + +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +short db = 0; // index of which buffer is used, values 0, 1 + +void init(void) +{ + ResetGraph(0); // Initialize drawing engine with a complete reset (0) + + SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1] + SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1] + + SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1] + SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1] + + if (VMODE) // PAL + { + SetVideoMode(MODE_PAL); + disp[0].screen.y += 8; // add offset : 240 + 8 + 8 = 256 + disp[1].screen.y += 8; + } + + setRGB0(&draw[0], 50, 50, 50); // set color for first draw area + setRGB0(&draw[1], 50, 50, 50); // set color for second draw area + + draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); // set the disp and draw environnments + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); // Load font to vram at 960,0(+128) + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars + +} + +void display(void) +{ + DrawSync(0); // Wait for all drawing to terminate + VSync(0); // Wait for the next vertical blank + + PutDispEnv(&disp[db]); // set alternate disp and draw environnments + PutDrawEnv(&draw[db]); + + SetDispMask(1); // Display on screen + + db = !db; // flip db value (0 or 1) +} + + +int main(void) +{ + + init(); // execute init() + + while (1) // infinite loop + { + FntPrint("Hello world !"); // Send string to print stream + + FntFlush(-1); // Draw printe stream + + display(); // Execute display() + } + return 0; + } diff --git a/hello_world/hello_world.c b/hello_world/hello_world.c new file mode 100644 index 0000000..ac96f85 --- /dev/null +++ b/hello_world/hello_world.c @@ -0,0 +1,86 @@ +// This is stolen from Lameguy64 tutorial : http://lameguy64.net/svn/pstutorials/chapter1/1-display.html + +#include +#include +#include +#include +#include + +#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL + +#define SCREENXRES 320 // Screen width +#define SCREENYRES 240 // Screen height + +#define CENTERX SCREENXRES/2 // Center of screen on x +#define CENTERY SCREENYRES/2 // Center of screen on y + +#define MARGINX 0 // margins for text display +#define MARGINY 32 + +#define FONTSIZE 8 * 7 // Text Field Height + +DISPENV disp[2]; // Double buffered DISPENV and DRAWENV +DRAWENV draw[2]; + +short db = 0; // index of which buffer is used, values 0, 1 + +void init(void) +{ + ResetGraph(0); // Initialize drawing engine with a complete reset (0) + + SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1] + SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1] + + SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1] + SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1] + + if (VMODE) // PAL + { + SetVideoMode(MODE_PAL); + disp[0].screen.y += 8; // add offset : 240 + 8 + 8 = 256 + disp[1].screen.y += 8; + } + + setRGB0(&draw[0], 50, 50, 50); // set color for first draw area + setRGB0(&draw[1], 50, 50, 50); // set color for second draw area + + draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame + draw[1].isbg = 1; + + PutDispEnv(&disp[db]); // set the disp and draw environnments + PutDrawEnv(&draw[db]); + + FntLoad(960, 0); // Load font to vram at 960,0(+128) + FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars + +} + +void display(void) +{ + DrawSync(0); // Wait for all drawing to terminate + VSync(0); // Wait for the next vertical blank + + PutDispEnv(&disp[db]); // set alternate disp and draw environnments + PutDrawEnv(&draw[db]); + + SetDispMask(1); // Display on screen + + db = !db; // flip db value (0 or 1) +} + + +int main(void) +{ + + init(); // execute init() + + while (1) // infinite loop + { + FntPrint("Hello world !"); // Send string to print stream + + FntFlush(-1); // Draw printe stream + + display(); // Execute display() + } + return 0; + }