aboutsummaryrefslogtreecommitdiff
path: root/examples/graphics
diff options
context:
space:
mode:
authorJohn Wilbert M. Villamor <lameguy64@gmail.com>2020-04-24 19:01:28 +0800
committerJohn Wilbert M. Villamor <lameguy64@gmail.com>2020-04-24 19:01:28 +0800
commit1aa0e17df7c325a41de8cf8a57f52ed853f08bf3 (patch)
tree5ec7f69ca0104f2b0a41e2ee7d3cb0cf0c9c54c5 /examples/graphics
parente82da2abe4c264d4b48a48d79cf9b8e4c4fb8ab6 (diff)
downloadpsn00bsdk-1aa0e17df7c325a41de8cf8a57f52ed853f08bf3.tar.gz
Refined toolchain instructions, organized examples, added automatic retry for CdRead(), added FIOCSCAN ioctl in psxsio TTY driver, added tty and console examples.
Diffstat (limited to 'examples/graphics')
-rw-r--r--examples/graphics/balls/ball16c.h16
-rw-r--r--examples/graphics/balls/ball16c.timbin0 -> 192 bytes
-rw-r--r--examples/graphics/balls/main.c228
-rw-r--r--examples/graphics/balls/makefile60
-rw-r--r--examples/graphics/billboard/billboard.c260
-rw-r--r--examples/graphics/billboard/makefile39
-rw-r--r--examples/graphics/billboard/texture64.timbin0 -> 2112 bytes
-rw-r--r--examples/graphics/billboard/tim.s7
-rw-r--r--examples/graphics/fpscam/clip.c108
-rw-r--r--examples/graphics/fpscam/clip.h27
-rw-r--r--examples/graphics/fpscam/lookat.c40
-rw-r--r--examples/graphics/fpscam/lookat.h19
-rw-r--r--examples/graphics/fpscam/main.c678
-rw-r--r--examples/graphics/fpscam/makefile39
-rw-r--r--examples/graphics/gte/main.c313
-rw-r--r--examples/graphics/gte/makefile39
-rw-r--r--examples/graphics/render2tex/blendpattern-16c.pngbin0 -> 385 bytes
-rw-r--r--examples/graphics/render2tex/blendpattern-16c.timbin0 -> 2112 bytes
-rw-r--r--examples/graphics/render2tex/blendpattern.pngbin0 -> 826 bytes
-rw-r--r--examples/graphics/render2tex/main.c649
-rw-r--r--examples/graphics/render2tex/makefile39
-rw-r--r--examples/graphics/render2tex/texture.s9
-rw-r--r--examples/graphics/rgb24/bunpattern.timbin0 -> 921620 bytes
-rw-r--r--examples/graphics/rgb24/main.c52
-rw-r--r--examples/graphics/rgb24/makefile41
-rw-r--r--examples/graphics/rgb24/tim.s7
26 files changed, 2670 insertions, 0 deletions
diff --git a/examples/graphics/balls/ball16c.h b/examples/graphics/balls/ball16c.h
new file mode 100644
index 0000000..c79f273
--- /dev/null
+++ b/examples/graphics/balls/ball16c.h
@@ -0,0 +1,16 @@
+unsigned int ball16c_size=192;
+unsigned char ball16c[] = {
+0x10,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0xc0,0x03,0x10,
+0x01,0x10,0x00,0x01,0x00,0x00,0x00,0x31,0xc6,0x73,0xce,0x94,0xd2,0x07,0x9d,
+0xd6,0xda,0x38,0xe3,0xef,0xbd,0x9b,0xef,0x8c,0xb1,0xc6,0x98,0xde,0xfb,0x4a,
+0xa9,0xa4,0x90,0xad,0xb5,0x00,0x00,0x8c,0x00,0x00,0x00,0xc0,0x03,0x00,0x01,
+0x04,0x00,0x10,0x00,0x00,0x00,0x10,0x22,0x12,0x02,0x00,0x00,0x00,0x10,0x32,
+0x33,0x23,0x11,0x04,0x00,0x00,0x23,0x55,0x66,0x35,0x72,0x47,0x00,0x20,0x52,
+0x86,0x68,0x36,0x12,0x97,0x0a,0x20,0x65,0xbb,0x8b,0x36,0x12,0x91,0x04,0x31,
+0x85,0xbb,0x68,0x35,0x12,0x97,0xdc,0x32,0x86,0x8b,0x56,0x35,0x73,0x97,0xa4,
+0x32,0x66,0x68,0x55,0x23,0x71,0x9e,0xac,0x32,0x65,0x56,0x33,0x13,0x71,0xce,
+0xa4,0x21,0x33,0x33,0x23,0x11,0xe7,0xc9,0xd4,0x12,0x22,0x22,0x13,0x71,0xe7,
+0xc9,0xda,0x10,0x17,0x11,0x77,0x77,0x9e,0x4c,0x0d,0x40,0x77,0x71,0xe7,0x9e,
+0xc9,0xd4,0x0d,0x00,0x94,0x99,0x99,0xcc,0x4c,0xda,0x00,0x00,0xa0,0xc4,0xc4,
+0x44,0xda,0x0d,0x00,0x00,0x00,0xd0,0xaa,0xda,0x0d,0x00,0x00
+};
diff --git a/examples/graphics/balls/ball16c.tim b/examples/graphics/balls/ball16c.tim
new file mode 100644
index 0000000..e2a5d17
--- /dev/null
+++ b/examples/graphics/balls/ball16c.tim
Binary files differ
diff --git a/examples/graphics/balls/main.c b/examples/graphics/balls/main.c
new file mode 100644
index 0000000..89c8063
--- /dev/null
+++ b/examples/graphics/balls/main.c
@@ -0,0 +1,228 @@
+/*
+ * LibPSn00b Example Programs
+ *
+ * Balls Example
+ * 2019 Meido-Tek Productions / PSn00bSDK Project
+ *
+ * Draws a bunch of ball sprites that bounce around the screen,
+ * along with a ball snake that might be difficult to see.
+ *
+ *
+ * Example by Lameguy64
+ *
+ * Changelog:
+ *
+ * November 20, 2018 - Initial version.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <psxetc.h>
+#include <psxgte.h>
+#include <psxgpu.h>
+#include "ball16c.h"
+
+
+#define MAX_BALLS 1024
+
+#define OT_LEN 8
+
+#define SCREEN_XRES 640
+#define SCREEN_YRES 480
+
+#define CENTER_X SCREEN_XRES/2
+#define CENTER_Y SCREEN_YRES/2
+
+
+/* Display and drawing environments */
+DISPENV disp;
+DRAWENV draw;
+
+char pribuff[2][65536]; /* Primitive packet buffers */
+unsigned int ot[2][OT_LEN]; /* Ordering tables */
+char *nextpri; /* Pointer to next packet buffer offset */
+int db = 0; /* Double buffer index */
+
+
+/* Ball struct and array */
+typedef struct {
+ short x,y;
+ short xdir,ydir;
+ unsigned char r,g,b,p;
+} BALL_TYPE;
+
+BALL_TYPE balls[MAX_BALLS];
+
+
+/* TIM image parameters for loading the ball texture and drawing sprites */
+TIM_IMAGE tim;
+
+
+void init() {
+
+ int i;
+
+ /* Reset GPU (also installs event handler for VSync) */
+ printf("Init GPU... ");
+ ResetGraph( 0 );
+ printf("Done.\n");
+
+
+ printf("Set video mode... ");
+
+ /* Set display and draw environment parameters */
+ SetDefDispEnv( &disp, 0, 0, SCREEN_XRES, SCREEN_YRES );
+ SetDefDrawEnv( &draw, 0, 0, SCREEN_XRES, SCREEN_YRES );
+ disp.isinter = 1; /* Enable interlace (required for hires) */
+
+ /* Set clear color, area clear and dither processing */
+ setRGB0( &draw, 63, 0, 127 );
+ draw.isbg = 1;
+ draw.dtd = 1;
+
+ /* Apply the display and drawing environments */
+ PutDispEnv( &disp );
+ PutDrawEnv( &draw );
+
+ /* Enable video output */
+ SetDispMask( 1 );
+
+ printf("Done.\n");
+
+
+ /* Upload the ball texture */
+ printf("Upload texture... ");
+ GetTimInfo( (unsigned int*)ball16c, &tim ); /* Get TIM parameters */
+
+ LoadImage( tim.prect, tim.paddr ); /* Upload texture to VRAM */
+ if( tim.mode & 0x8 ) {
+ LoadImage( tim.crect, tim.caddr ); /* Upload CLUT if present */
+ }
+
+ printf("Done.\n");
+
+
+ /* Calculate ball positions */
+ printf("Calculating balls... ");
+
+ for(i=0; i<MAX_BALLS; i++) {
+
+ balls[i].x = (rand()%624);
+ balls[i].y = (rand()%464);
+ balls[i].xdir = 1-(rand()%3);
+ balls[i].ydir = 1-(rand()%3);
+ if( !balls[i].xdir ) balls[i].xdir = 1;
+ if( !balls[i].ydir ) balls[i].ydir = 1;
+ balls[i].xdir *= 2;
+ balls[i].ydir *= 2;
+ balls[i].r = (rand()%256);
+ balls[i].g = (rand()%256);
+ balls[i].b = (rand()%256);
+
+ }
+
+ printf("Done.\n");
+
+}
+
+int main(int argc, const char* argv[]) {
+
+ SPRT_16 *sprt;
+ DR_TPAGE *tpri;
+
+ int i,counter=0;
+
+
+ /* Init graphics and stuff before doing anything else */
+ init();
+
+
+ /* Main loop */
+ printf("Entering loop...\n");
+
+ while(1) {
+
+ /* Clear ordering table and set start address of primitive */
+ /* buffer for next frame */
+ ClearOTagR( ot[db], OT_LEN );
+ nextpri = pribuff[db];
+
+ /* Sort a balls snake */
+ sprt = (SPRT_16*)nextpri;
+ srand( 64 );
+ for( i=0; i<32; i++ ) {
+
+ setSprt16( sprt );
+ setXY0( sprt,
+ (CENTER_X-8)+(isin((counter-(i<<4))<<3)>>5),
+ (CENTER_Y-8)-(icos((counter-(i<<2))<<3)>>5) );
+ setRGB0( sprt, rand()%256, rand()%256, rand()%256 );
+ setUV0( sprt, 0, 0 );
+ setClut( sprt, tim.crect->x, tim.crect->y );
+
+ addPrim( ot[db]+(OT_LEN-1), sprt );
+ sprt++;
+
+ }
+
+ /* Sort the balls */
+ for( i=0; i<MAX_BALLS; i++ ) {
+
+ setSprt16( sprt );
+ setXY0( sprt, balls[i].x, balls[i].y );
+ setRGB0( sprt, balls[i].r, balls[i].g, balls[i].b );
+ setUV0( sprt, 0, 0 );
+ setClut( sprt, tim.crect->x, tim.crect->y );
+
+ addPrim( ot[db]+(OT_LEN-1), sprt );
+ sprt++;
+
+ balls[i].x += balls[i].xdir;
+ balls[i].y += balls[i].ydir;
+
+ if( ( balls[i].x+16 ) > 640 ) {
+ balls[i].xdir = -2;
+ } else if( balls[i].x < 0 ) {
+ balls[i].xdir = 2;
+ }
+
+ if( ( balls[i].y+16 ) > 480 ) {
+ balls[i].ydir = -2;
+ } else if( balls[i].y < 0 ) {
+ balls[i].ydir = 2;
+ }
+
+ }
+ nextpri = (char*)sprt;
+
+
+ /* Sort a TPage primitive so the sprites will draw pixels from */
+ /* the correct texture page in VRAM */
+ tpri = (DR_TPAGE*)nextpri;
+ setDrawTPage( tpri, 0, 0,
+ getTPage(0, 0, tim.prect->x, tim.prect->y ));
+ addPrim( ot[db]+(OT_LEN-1), tpri );
+ nextpri += sizeof(DR_TPAGE);
+
+ /* Wait for GPU and VSync */
+ DrawSync( 0 );
+ VSync( 0 );
+
+ /* Since draw.isbg is non-zero this clears the screen */
+ PutDrawEnv( &draw );
+
+ /* Begin drawing the new frame */
+ DrawOTag( ot[db]+(OT_LEN-1) );
+
+ /* Alternate to the next buffer */
+ db = !db;
+
+ /* Increment counter for the snake animation */
+ counter++;
+
+ }
+
+ return 0;
+
+}
diff --git a/examples/graphics/balls/makefile b/examples/graphics/balls/makefile
new file mode 100644
index 0000000..50ed0f1
--- /dev/null
+++ b/examples/graphics/balls/makefile
@@ -0,0 +1,60 @@
+include ../../sdk-common.mk
+
+# Project target name
+TARGET = balls.elf
+
+# Searches for C, C++ and S (assembler) files in local directory
+CFILES = $(notdir $(wildcard *.c))
+CPPFILES = $(notdir $(wildcard *.cpp))
+AFILES = $(notdir $(wildcard *.s))
+
+# Determine object files
+OFILES = $(addprefix build/,$(CFILES:.c=.o)) \
+ $(addprefix build/,$(CPPFILES:.cpp=.o)) \
+ $(addprefix build/,$(AFILES:.s=.o))
+
+# Project specific include and library directories
+# (use -I for include dirs, -L for library dirs)
+INCLUDE +=
+LIBDIRS +=
+
+# Libraries to link
+LIBS = -lpsxetc -lpsxgpu -lpsxgte -lpsxspu -lpsxapi -lc
+
+# C compiler flags
+CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections
+
+# C++ compiler flags
+CPPFLAGS = $(CFLAGS) -fno-exceptions
+
+# Assembler flags
+AFLAGS = -g -msoft-float
+
+# Linker flags
+LDFLAGS = -g -Ttext=0x80010000 -gc-sections \
+ -T $(GCC_BASE)/mipsel-unknown-elf/lib/ldscripts/elf32elmip.x
+
+# Toolchain programs
+CC = $(PREFIX)gcc
+CXX = $(PREFIX)g++
+AS = $(PREFIX)as
+LD = $(PREFIX)ld
+
+all: $(OFILES)
+ $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET)
+ elf2x -q $(TARGET)
+
+build/%.o: %.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.cpp
+ @mkdir -p $(dir $@)
+ $(CXX) $(AFLAGS) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.s
+ @mkdir -p $(dir $@)
+ $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+
+clean:
+ rm -rf build $(TARGET) $(TARGET:.elf=.exe)
diff --git a/examples/graphics/billboard/billboard.c b/examples/graphics/billboard/billboard.c
new file mode 100644
index 0000000..bba5dda
--- /dev/null
+++ b/examples/graphics/billboard/billboard.c
@@ -0,0 +1,260 @@
+/*
+ * LibPSn00b Example Programs
+ *
+ * GTE Billboarding Sprites Example
+ * 2019 Meido-Tek Productions / PSn00bSDK Project
+ *
+ * Displays a bunch of sprites placed on the screen using 3D coordinates
+ * that scale according to the distance from the screen. This is a quick
+ * modification of the GTE cube example.
+ *
+ * Billboard sprites are useful for 2D projectiles flying across 3D space,
+ * particle effects such as smoke as well as characters and objects
+ * represented as 2D sprites.
+ *
+ * Example by Lameguy64
+ *
+ * Changelog:
+ *
+ * Sep 24, 2019 - Initial version.
+ *
+ */
+
+#include <stdio.h>
+#include <psxgpu.h>
+#include <psxgte.h>
+#include <inline_c.h>
+
+/* OT and Packet Buffer sizes */
+#define OT_LEN 256
+#define PACKET_LEN 1024
+
+/* Screen resolution */
+#define SCREEN_XRES 320
+#define SCREEN_YRES 240
+
+/* Screen center position */
+#define CENTERX SCREEN_XRES>>1
+#define CENTERY SCREEN_YRES>>1
+
+
+/* Double buffer structure */
+typedef struct {
+ DISPENV disp; /* Display environment */
+ DRAWENV draw; /* Drawing environment */
+ int ot[OT_LEN]; /* Ordering table */
+ char p[PACKET_LEN]; /* Packet buffer */
+} DB;
+
+/* Double buffer variables */
+DB db[2];
+int db_active = 0;
+char *db_nextpri;
+
+extern int tim_image[];
+TIM_IMAGE tim;
+
+/* For easier handling of vertex indices */
+typedef struct {
+ short v0,v1,v2,v3;
+} INDEX;
+
+/* Sprite position vertices */
+SVECTOR verts[] = {
+ { -50, -50, -50, 0 },
+ { 50, -50, -50, 0 },
+ { -50, 50, -50, 0 },
+ { 50, 50, -50, 0 },
+ { 50, -50, 50, 0 },
+ { -50, -50, 50, 0 },
+ { 50, 50, 50, 0 },
+ { -50, 50, 50, 0 }
+};
+
+
+/* Function declarations */
+void init();
+void display();
+
+
+/* Main function */
+int main() {
+
+ int i,p,sz;
+
+ SVECTOR rot = { 0 }; /* Rotation vector for Rotmatrix */
+ VECTOR pos = { 0, 0, 160 }; /* Translation vector for TransMatrix */
+ MATRIX mtx,lmtx; /* Rotation matrices for geometry and lighting */
+
+ POLY_FT4 *quad; /* Flat shaded quad primitive pointer */
+ SVECTOR spos;
+
+ /* Init graphics and GTE */
+ init();
+
+
+ /* Main loop */
+ while( 1 ) {
+
+ /* Set rotation and translation to the matrix */
+ RotMatrix( &rot, &mtx );
+ TransMatrix( &mtx, &pos );
+
+ /* Set rotation and translation matrix */
+ gte_SetRotMatrix( &mtx );
+ gte_SetTransMatrix( &mtx );
+
+ /* Make the sprites revolve around */
+ rot.vy += 16;
+ rot.vz += 16;
+
+ /* Draw the sprites */
+ quad = (POLY_FT4*)db_nextpri;
+
+ for( i=0; i<8; i++ ) {
+
+ // Load the 3D coordinate of the sprite to GTE
+ gte_ldv0(&verts[i]);
+
+ // Rotation, Translation and Perspective Single
+ gte_rtps();
+
+ // Store depth
+ gte_stsz(&p);
+
+ // Don't sort sprite if depth is zero
+ // (or divide by zero will happen later)
+ if( p > 0 ) {
+
+ // Store result to position vector
+ gte_stsxy2(&spos);
+
+ // Calculate sprite size, the divide operation might be a
+ // performance killer but it's likely faster than performing
+ // a lookat operation between sprite and camera, which some
+ // billboard sprite implementations use.
+ sz = (16*CENTERX)/p;
+
+ // Prepare quad primitive
+ setPolyFT4(quad);
+
+ // Set quad coordinates
+ setXY4(quad,
+ spos.vx-sz, spos.vy-sz,
+ spos.vx+sz, spos.vy-sz,
+ spos.vx-sz, spos.vy+sz,
+ spos.vx+sz, spos.vy+sz);
+
+ // Set color
+ setRGB0(quad, 128, 128, 128);
+
+ // Set tpage
+ quad->tpage = getTPage(tim.mode&0x8, 0, tim.prect->x, tim.prect->y);
+
+ // Set CLUT
+ setClut(quad, tim.crect->x, tim.crect->y);
+
+ // Set texture coordinates
+ setUVWH(quad, 0, 0, 64, 64);
+
+ /* Sort primitive to the ordering table */
+ addPrim(db[db_active].ot+(p>>2), quad);
+
+ /* Advance to make another primitive */
+ quad++;
+
+ }
+ }
+
+ /* Update nextpri variable */
+ /* (IMPORTANT if you plan to sort more primitives after this) */
+ db_nextpri = (char*)quad;
+
+ /* Swap buffers and draw the primitives */
+ display();
+
+ }
+
+ return 0;
+
+}
+
+void init() {
+
+ /* Reset the GPU, also installs a VSync event handler */
+ ResetGraph( 0 );
+
+ /* Set display and draw environment areas */
+ /* (display and draw areas must be separate, otherwise hello flicker) */
+ SetDefDispEnv( &db[0].disp, 0, 0, SCREEN_XRES, SCREEN_YRES );
+ SetDefDrawEnv( &db[0].draw, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES );
+
+ /* Enable draw area clear and dither processing */
+ setRGB0( &db[0].draw, 63, 0, 127 );
+ db[0].draw.isbg = 1;
+ db[0].draw.dtd = 1;
+
+
+ /* Define the second set of display/draw environments */
+ SetDefDispEnv( &db[1].disp, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES );
+ SetDefDrawEnv( &db[1].draw, 0, 0, SCREEN_XRES, SCREEN_YRES );
+
+ setRGB0( &db[1].draw, 63, 0, 127 );
+ db[1].draw.isbg = 1;
+ db[1].draw.dtd = 1;
+
+
+ /* Apply the drawing environment of the first double buffer */
+ PutDrawEnv( &db[0].draw );
+
+
+ /* Clear both ordering tables to make sure they are clean at the start */
+ ClearOTagR( db[0].ot, OT_LEN );
+ ClearOTagR( db[1].ot, OT_LEN );
+
+ /* Set primitive pointer address */
+ db_nextpri = db[0].p;
+
+ /* Initialize the GTE */
+ InitGeom();
+
+ /* Set GTE offset (recommended method of centering) */
+ gte_SetGeomOffset( CENTERX, CENTERY );
+
+ /* Set screen depth (basically FOV control, W/2 works best) */
+ gte_SetGeomScreen( CENTERX );
+
+ GetTimInfo(tim_image, &tim);
+
+ LoadImage(tim.prect, tim.paddr);
+ DrawSync(0);
+
+ LoadImage(tim.crect, tim.caddr);
+ DrawSync(0);
+
+}
+
+void display() {
+
+ /* Wait for GPU to finish drawing and vertical retrace */
+ DrawSync( 0 );
+ VSync( 0 );
+
+ /* Swap buffers */
+ db_active ^= 1;
+ db_nextpri = db[db_active].p;
+
+ /* Clear the OT of the next frame */
+ ClearOTagR( db[db_active].ot, OT_LEN );
+
+ /* Apply display/drawing environments */
+ PutDrawEnv( &db[db_active].draw );
+ PutDispEnv( &db[db_active].disp );
+
+ /* Enable display */
+ SetDispMask( 1 );
+
+ /* Start drawing the OT of the last buffer */
+ DrawOTag( db[1-db_active].ot+(OT_LEN-1) );
+
+} \ No newline at end of file
diff --git a/examples/graphics/billboard/makefile b/examples/graphics/billboard/makefile
new file mode 100644
index 0000000..d6add7a
--- /dev/null
+++ b/examples/graphics/billboard/makefile
@@ -0,0 +1,39 @@
+include ../../sdk-common.mk
+
+TARGET = billboard.elf
+
+CFILES = $(notdir $(wildcard *.c))
+CPPFILES = $(notdir $(wildcard *.cpp))
+AFILES = $(notdir $(wildcard *.s))
+
+OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o))
+
+INCLUDE +=
+LIBDIRS +=
+
+LIBS = -lpsxetc -lpsxgpu -lpsxgte -lpsxspu -lpsxapi -lc
+
+CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections
+CPPFLAGS = $(CFLAGS) -fno-exceptions
+AFLAGS = -g -msoft-float
+LDFLAGS = -g -Ttext=0x80010000 -gc-sections -T $(GCC_BASE)/mipsel-unknown-elf/lib/ldscripts/elf32elmip.x
+
+CC = $(PREFIX)gcc
+CXX = $(PREFIX)g++
+AS = $(PREFIX)as
+LD = $(PREFIX)ld
+
+all: $(OFILES)
+ $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET)
+ elf2x -q $(TARGET)
+
+build/%.o: %.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.s
+ @mkdir -p $(dir $@)
+ $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+
+clean:
+ rm -rf build $(TARGET) $(TARGET:.elf=.exe)
diff --git a/examples/graphics/billboard/texture64.tim b/examples/graphics/billboard/texture64.tim
new file mode 100644
index 0000000..d3aff3a
--- /dev/null
+++ b/examples/graphics/billboard/texture64.tim
Binary files differ
diff --git a/examples/graphics/billboard/tim.s b/examples/graphics/billboard/tim.s
new file mode 100644
index 0000000..1fa8d69
--- /dev/null
+++ b/examples/graphics/billboard/tim.s
@@ -0,0 +1,7 @@
+.section .data
+
+.global tim_image
+.type tim_image, @object
+tim_image:
+ .incbin "texture64.tim"
+ \ No newline at end of file
diff --git a/examples/graphics/fpscam/clip.c b/examples/graphics/fpscam/clip.c
new file mode 100644
index 0000000..7f2b780
--- /dev/null
+++ b/examples/graphics/fpscam/clip.c
@@ -0,0 +1,108 @@
+/* Polygon clip detection code
+ *
+ * The polygon clipping logic is based on the Cohen-Sutherland algorithm, but
+ * only the off-screen detection logic is used to determine which polygon edges
+ * are off-screen.
+ *
+ * In tri_clip, the following edges are checked as follows:
+ *
+ * |\
+ * | \
+ * | \
+ * | \
+ * |--------
+ *
+ * In quad_clip, the following edges are checked as follows:
+ *
+ * |---------|
+ * | \ / |
+ * | \ / |
+ * | / \ |
+ * | / \ |
+ * |---------|
+ *
+ * The inner portion of the quad is checked, otherwise the quad will be
+ * culled out if the camera faces right into it, where all four edges
+ * are off-screen at once.
+ *
+ */
+
+#include "clip.h"
+
+#define CLIP_LEFT 1
+#define CLIP_RIGHT 2
+#define CLIP_TOP 4
+#define CLIP_BOTTOM 8
+
+int test_clip(RECT *clip, short x, short y) {
+
+ // Tests which corners of the screen a point lies outside of
+
+ int result = 0;
+
+ if ( x < clip->x ) {
+ result |= CLIP_LEFT;
+ }
+
+ if ( x >= (clip->x+(clip->w-1)) ) {
+ result |= CLIP_RIGHT;
+ }
+
+ if ( y < clip->y ) {
+ result |= CLIP_TOP;
+ }
+
+ if ( y >= (clip->y+(clip->h-1)) ) {
+ result |= CLIP_BOTTOM;
+ }
+
+ return result;
+
+}
+
+int tri_clip(RECT *clip, DVECTOR *v0, DVECTOR *v1, DVECTOR *v2) {
+
+ // Returns non-zero if a triangle is outside the screen boundaries
+
+ short c[3];
+
+ c[0] = test_clip(clip, v0->vx, v0->vy);
+ c[1] = test_clip(clip, v1->vx, v1->vy);
+ c[2] = test_clip(clip, v2->vx, v2->vy);
+
+ if ( ( c[0] & c[1] ) == 0 )
+ return 0;
+ if ( ( c[1] & c[2] ) == 0 )
+ return 0;
+ if ( ( c[2] & c[0] ) == 0 )
+ return 0;
+
+ return 1;
+}
+
+int quad_clip(RECT *clip, DVECTOR *v0, DVECTOR *v1, DVECTOR *v2, DVECTOR *v3) {
+
+ // Returns non-zero if a quad is outside the screen boundaries
+
+ short c[4];
+
+ c[0] = test_clip(clip, v0->vx, v0->vy);
+ c[1] = test_clip(clip, v1->vx, v1->vy);
+ c[2] = test_clip(clip, v2->vx, v2->vy);
+ c[3] = test_clip(clip, v3->vx, v3->vy);
+
+ if ( ( c[0] & c[1] ) == 0 )
+ return 0;
+ if ( ( c[1] & c[2] ) == 0 )
+ return 0;
+ if ( ( c[2] & c[3] ) == 0 )
+ return 0;
+ if ( ( c[3] & c[0] ) == 0 )
+ return 0;
+ if ( ( c[0] & c[2] ) == 0 )
+ return 0;
+ if ( ( c[1] & c[3] ) == 0 )
+ return 0;
+
+ return 1;
+} \ No newline at end of file
diff --git a/examples/graphics/fpscam/clip.h b/examples/graphics/fpscam/clip.h
new file mode 100644
index 0000000..3b428bb
--- /dev/null
+++ b/examples/graphics/fpscam/clip.h
@@ -0,0 +1,27 @@
+#ifndef _CLIP_H
+#define _CLIP_H
+
+#include <psxgte.h>
+#include <psxgpu.h>
+
+/* tri_clip
+ *
+ * Returns non-zero if a triangle (v0, v1, v2) is outside 'clip'.
+ *
+ * clip - Clipping area
+ * v0,v1,v2 - Triangle coordinates
+ *
+ */
+int tri_clip(RECT *clip, DVECTOR *v0, DVECTOR *v1, DVECTOR *v2);
+
+/* quad_clip
+ *
+ * Returns non-zero if a quad (v0, v1, v2, v3) is outside 'clip'.
+ *
+ * clip - Clipping area
+ * v0,v1,v2,v3 - Quad coordinates
+ *
+ */
+int quad_clip(RECT *clip, DVECTOR *v0, DVECTOR *v1, DVECTOR *v2, DVECTOR *v3);
+
+#endif // _CLIP_H \ No newline at end of file
diff --git a/examples/graphics/fpscam/lookat.c b/examples/graphics/fpscam/lookat.c
new file mode 100644
index 0000000..d7c9ce4
--- /dev/null
+++ b/examples/graphics/fpscam/lookat.c
@@ -0,0 +1,40 @@
+// LookAt matrix code (may be implemented into libpsxgte soon)
+
+#include "lookat.h"
+
+void crossProduct(SVECTOR *v0, SVECTOR *v1, VECTOR *out) {
+
+ out->vx = ((v0->vy*v1->vz)-(v0->vz*v1->vy))>>12;
+ out->vy = ((v0->vz*v1->vx)-(v0->vx*v1->vz))>>12;
+ out->vz = ((v0->vx*v1->vy)-(v0->vy*v1->vx))>>12;
+
+}
+
+void LookAt(VECTOR *eye, VECTOR *at, SVECTOR *up, MATRIX *mtx) {
+
+ VECTOR taxis;
+ SVECTOR zaxis;
+ SVECTOR xaxis;
+ SVECTOR yaxis;
+ VECTOR pos;
+ VECTOR vec;
+
+ setVector(&taxis, at->vx-eye->vx, at->vy-eye->vy, at->vz-eye->vz);
+ VectorNormalS(&taxis, &zaxis);
+ crossProduct(&zaxis, up, &taxis);
+ VectorNormalS(&taxis, &xaxis);
+ crossProduct(&zaxis, &xaxis, &taxis);
+ VectorNormalS(&taxis, &yaxis);
+
+ mtx->m[0][0] = xaxis.vx; mtx->m[1][0] = yaxis.vx; mtx->m[2][0] = zaxis.vx;
+ mtx->m[0][1] = xaxis.vy; mtx->m[1][1] = yaxis.vy; mtx->m[2][1] = zaxis.vy;
+ mtx->m[0][2] = xaxis.vz; mtx->m[1][2] = yaxis.vz; mtx->m[2][2] = zaxis.vz;
+
+ pos.vx = -eye->vx;;
+ pos.vy = -eye->vy;;
+ pos.vz = -eye->vz;;
+
+ ApplyMatrixLV(mtx, &pos, &vec);
+ TransMatrix(mtx, &vec);
+
+} \ No newline at end of file
diff --git a/examples/graphics/fpscam/lookat.h b/examples/graphics/fpscam/lookat.h
new file mode 100644
index 0000000..c57e50a
--- /dev/null
+++ b/examples/graphics/fpscam/lookat.h
@@ -0,0 +1,19 @@
+#ifndef _LOOKAT_H
+#define _LOOKAT_H
+
+#include <psxgte.h>
+#include <psxgpu.h>
+
+/* LookAt
+ *
+ * Generates a matrix that looks from 'eye' to 'at'.
+ *
+ * eye - Position of viewpoint
+ * at - Position to 'look at' from viewpoint
+ * up - Vector that defines the 'up' direction
+ * mtx - Matrix output
+ *
+ */
+void LookAt(VECTOR *eye, VECTOR *at, SVECTOR *up, MATRIX *mtx);
+
+#endif // _LOOKAT_H \ No newline at end of file
diff --git a/examples/graphics/fpscam/main.c b/examples/graphics/fpscam/main.c
new file mode 100644
index 0000000..9dedf06
--- /dev/null
+++ b/examples/graphics/fpscam/main.c
@@ -0,0 +1,678 @@
+/*
+ * LibPSn00b Example Programs
+ *
+ * First-Person and Look-At Camera Example
+ * 2019 Meido-Tek Productions / PSn00bSDK Project
+ *
+ * Demonstrates both a first person perspective camera implementation with
+ * full six degrees of movement using fixed point integer math and a look-at
+ * tracking perspective. This example also shows how to use BIOS controller
+ * functions and how to parse analog controller input.
+ *
+ * Controls:
+ * Up - Look up
+ * Down - Look down
+ * Left - Look left
+ * Right - Look right
+ * Triangle - Move forward
+ * Cross - Move backward
+ * Square - Strafe left
+ * Circle - Strafe right
+ * R1 - Slide up
+ * R2 - Slide down
+ * L1 - Look at cube
+ * Select - Exit program (only works with CD loaders)
+ *
+ *
+ * Example by Lameguy64
+ *
+ * Changelog:
+ *
+ * July 18, 2019 - Initial version.
+ *
+ * Sep 24, 2019 - Added camera position display and _boot() exit.
+ *
+ */
+
+#include <stdio.h>
+#include <psxgpu.h>
+#include <psxgte.h>
+#include <psxpad.h>
+#include <psxapi.h>
+#include <psxetc.h>
+#include <inline_c.h>
+
+#include "clip.h"
+#include "lookat.h"
+
+// OT and Packet Buffer sizes
+#define OT_LEN 1024
+#define PACKET_LEN 8192
+
+// Screen resolution
+#define SCREEN_XRES 320
+#define SCREEN_YRES 240
+
+// Screen center position
+#define CENTERX SCREEN_XRES>>1
+#define CENTERY SCREEN_YRES>>1
+
+
+// Double buffer structure
+typedef struct {
+ DISPENV disp; // Display environment
+ DRAWENV draw; // Drawing environment
+ int ot[OT_LEN]; // Ordering table
+ char p[PACKET_LEN]; // Packet buffer
+} DB;
+
+// Double buffer variables
+DB db[2];
+int db_active = 0;
+char *db_nextpri;
+RECT screen_clip;
+
+// Pad data buffer
+char pad_buff[2][34];
+
+
+// For easier handling of vertex indexes
+typedef struct {
+ short v0,v1,v2,v3;
+} INDEX;
+
+// Cube vertices
+SVECTOR cube_verts[] = {
+ { -100, -100, -100, 0 },
+ { 100, -100, -100, 0 },
+ { -100, 100, -100, 0 },
+ { 100, 100, -100, 0 },
+ { 100, -100, 100, 0 },
+ { -100, -100, 100, 0 },
+ { 100, 100, 100, 0 },
+ { -100, 100, 100, 0 }
+};
+
+// Cube face normals
+SVECTOR cube_norms[] = {
+ { 0, 0, -ONE, 0 },
+ { 0, 0, ONE, 0 },
+ { 0, -ONE, 0, 0 },
+ { 0, ONE, 0, 0 },
+ { -ONE, 0, 0, 0 },
+ { ONE, 0, 0, 0 }
+};
+
+// Cube vertex indices
+INDEX cube_indices[] = {
+ { 0, 1, 2, 3 },
+ { 4, 5, 6, 7 },
+ { 5, 4, 0, 1 },
+ { 6, 7, 3, 2 },
+ { 0, 2, 5, 7 },
+ { 3, 1, 6, 4 }
+};
+
+// Number of faces of cube
+#define CUBE_FACES 6
+
+
+// Light color matrix
+// Each column represents the color matrix of each light source and is
+// used as material color when using gte_ncs() or multiplied by a
+// source color when using gte_nccs(). 4096 is 1.0 in this matrix
+// A column of zeroes effectively disables the light source.
+MATRIX color_mtx = {
+ ONE, 0, 0, // Red
+ 0, 0, 0, // Green
+ ONE, 0, 0 // Blue
+};
+
+// Light matrix
+// Each row represents a vector direction of each light source.
+// An entire row of zeroes effectively disables the light source.
+MATRIX light_mtx = {
+ /* X, Y, Z */
+ -2048 , -2048 , -2048,
+ 0 , 0 , 0,
+ 0 , 0 , 0
+};
+
+
+// Function declarations
+void init();
+void display();
+void sort_cube(MATRIX *mtx, VECTOR *pos, SVECTOR *rot);
+
+
+// Main function
+int main() {
+
+ int i,p,xy_temp;
+ int px,py;
+
+ SVECTOR rot; // Rotation vector for cube
+ VECTOR pos; // Position vector for cube
+
+ SVECTOR verts[17][17]; // Vertex array for floor
+
+ VECTOR cam_pos; // Camera position (in fixed point integers)
+ VECTOR cam_rot; // Camera view angle (in fixed point integers)
+ int cam_mode; // Camera mode (between first-person and look-at)
+
+ VECTOR tpos; // Translation value for matrix calculations
+ SVECTOR trot; // Rotation value for matrix calculations
+ MATRIX mtx,lmtx; // Rotation matrices for geometry and lighting
+
+ PADTYPE *pad; // Pad structure pointer for parsing controller
+
+ POLY_F4 *pol4; // Flat shaded quad primitive pointer
+
+
+ // Init graphics and GTE
+ init();
+
+
+ // Set coordinates to the vertex array for the floor
+ for( py=0; py<17; py++ ) {
+ for( px=0; px<17; px++ ) {
+
+ setVector( &verts[py][px],
+ (100*(px-8))-50,
+ 0,
+ (100*(py-8))-50 );
+
+ }
+ }
+
+
+ // Camera default coordinates
+ setVector( &cam_pos, 0, ONE*-200, 0 );
+ setVector( &cam_rot, 0, 0, 0 );
+
+
+ // Main loop
+ while( 1 ) {
+
+ // Set pad buffer data to pad pointer
+ pad = (PADTYPE*)&pad_buff[0][0];
+
+ // Parse controller input
+ cam_mode = 0;
+
+ // Divide out fractions of camera rotation
+ trot.vx = cam_rot.vx >> 12;
+ trot.vy = cam_rot.vy >> 12;
+ trot.vz = cam_rot.vz >> 12;
+
+ if( pad->stat == 0 ) {
+
+ // For digital pad, dual-analog and dual-shock
+ if( ( pad->type == 0x4 ) || ( pad->type == 0x5 ) || ( pad->type == 0x7 ) ) {
+
+ // The button status bits are inverted,
+ // so 0 means pressed in this case
+
+ // Look controls
+ if( !(pad->btn&PAD_UP) ) {
+
+ // Look up
+ cam_rot.vx -= ONE*8;
+
+ } else if( !(pad->btn&PAD_DOWN) ) {
+
+ // Look down
+ cam_rot.vx += ONE*8;
+
+ }
+
+ if( !(pad->btn&PAD_LEFT) ) {
+
+ // Look left
+ cam_rot.vy += ONE*8;
+
+ } else if( !(pad->btn&PAD_RIGHT) ) {
+
+ // Look right
+ cam_rot.vy -= ONE*8;
+
+ }
+
+ // Movement controls
+ if( !(pad->btn&PAD_TRIANGLE) ) {
+
+ // Move forward
+ cam_pos.vx -= (( isin( trot.vy )*icos( trot.vx ) )>>12)<<2;
+ cam_pos.vy += isin( trot.vx )<<2;
+ cam_pos.vz += (( icos( trot.vy )*icos( trot.vx ) )>>12)<<2;
+
+ } else if( !(pad->btn&PAD_CROSS) ) {
+
+ // Move backward
+ cam_pos.vx += (( isin( trot.vy )*icos( trot.vx ) )>>12)<<2;
+ cam_pos.vy -= isin( trot.vx )<<2;
+ cam_pos.vz -= (( icos( trot.vy )*icos( trot.vx ) )>>12)<<2;
+
+ }
+
+ if( !(pad->btn&PAD_SQUARE ) ) {
+
+ // Slide left
+ cam_pos.vx -= icos( trot.vy )<<2;
+ cam_pos.vz -= isin( trot.vy )<<2;
+
+ } else if( !(pad->btn&PAD_CIRCLE ) ) {
+
+ // Slide right
+ cam_pos.vx += icos( trot.vy )<<2;
+ cam_pos.vz += isin( trot.vy )<<2;
+
+ }
+
+ if( !(pad->btn&PAD_R1) ) {
+
+ // Slide up
+ cam_pos.vx -= (( isin( trot.vy )*isin( trot.vx ) )>>12)<<2;
+ cam_pos.vy -= icos( trot.vx )<<2;
+ cam_pos.vz += (( icos( trot.vy )*isin( trot.vx ) )>>12)<<2;
+
+ }
+
+ if( !(pad->btn&PAD_R2) ) {
+
+ // Slide down
+ cam_pos.vx += (( isin( trot.vy )*isin( trot.vx ) )>>12)<<2;
+ cam_pos.vy += icos( trot.vx )<<2;
+ cam_pos.vz -= (( icos( trot.vy )*isin( trot.vx ) )>>12)<<2;
+
+ }
+
+ // Look at cube
+ if( !(pad->btn&PAD_L1) ) {
+
+ cam_mode = 1;
+
+ }
+
+ if( !(pad->btn&PAD_SELECT) ) {
+ _boot();
+ }
+
+ }
+
+ // For dual-analog and dual-shock (analog input)
+ if( ( pad->type == 0x5 ) || ( pad->type == 0x7 ) ) {
+
+ // Moving forwards and backwards
+ if( ( (pad->ls_y-128) < -16 ) || ( (pad->ls_y-128) > 16 ) ) {
+
+ cam_pos.vx += ((( isin( trot.vy )*icos( trot.vx ) )>>12)*(pad->ls_y-128))>>5;
+ cam_pos.vy -= (isin( trot.vx )*(pad->ls_y-128))>>5;
+ cam_pos.vz -= ((( icos( trot.vy )*icos( trot.vx ) )>>12)*(pad->ls_y-128))>>5;
+
+ }
+
+ // Strafing left and right
+ if( ( (pad->ls_x-128) < -16 ) || ( (pad->ls_x-128) > 16 ) ) {
+ cam_pos.vx += (icos( trot.vy )*(pad->ls_x-128))>>5;
+ cam_pos.vz += (isin( trot.vy )*(pad->ls_x-128))>>5;
+ }
+
+ // Look up and down
+ if( ( (pad->rs_y-128) < -16 ) || ( (pad->rs_y-128) > 16 ) ) {
+ cam_rot.vx += (pad->rs_y-128)<<9;
+ }
+
+ // Look left and right
+ if( ( (pad->rs_x-128) < -16 ) || ( (pad->rs_x-128) > 16 ) ) {
+ cam_rot.vy -= (pad->rs_x-128)<<9;
+ }
+
+ }
+
+ }
+
+ // Print out some info
+ FntPrint(-1, "BUTTONS=%04x\n", pad->btn);
+ FntPrint(-1, "X=%d Y=%d Z=%d\n",
+ cam_pos.vx>>12,
+ cam_pos.vy>>12,
+ cam_pos.vz>>12);
+ FntPrint(-1, "RX=%d RY=%d\n",
+ cam_rot.vx>>12,
+ cam_rot.vy>>12);
+
+ // First-person camera mode
+ if( cam_mode == 0 ) {
+
+ // Set rotation to the matrix
+ RotMatrix( &trot, &mtx );
+
+ // Divide out the fractions of camera coordinates and invert
+ // the sign, so camera coordinates will line up to world
+ // (or geometry) coordinates
+ tpos.vx = -cam_pos.vx >> 12;
+ tpos.vy = -cam_pos.vy >> 12;
+ tpos.vz = -cam_pos.vz >> 12;
+
+ // Apply rotation of matrix to translation value to achieve a
+ // first person perspective
+ ApplyMatrixLV( &mtx, &tpos, &tpos );
+
+ // Set translation matrix
+ TransMatrix( &mtx, &tpos );
+
+ // Tracking mode
+ } else {
+
+ // Vector that defines the 'up' direction of the camera
+ SVECTOR up = { 0, -ONE, 0 };
+
+ // Divide out fractions of camera coordinates
+ tpos.vx = cam_pos.vx >> 12;
+ tpos.vy = cam_pos.vy >> 12;
+ tpos.vz = cam_pos.vz >> 12;
+
+ // Look at the cube
+ LookAt(&tpos, &pos, &up, &mtx);
+
+ }
+
+ // Set rotation and translation matrix
+ gte_SetRotMatrix( &mtx );
+ gte_SetTransMatrix( &mtx );
+
+ // Draw the floor
+ pol4 = (POLY_F4*)db_nextpri;
+
+ for( py=0; py<16; py++ ) {
+ for( px=0; px<16; px++ ) {
+
+ // Load first three vertices to GTE
+ gte_ldv3(
+ &verts[py][px],
+ &verts[py][px+1],
+ &verts[py+1][px] );
+
+ gte_rtpt();
+
+ gte_avsz3();
+ gte_stotz( &p );
+
+ if( ( (p>>2) >= OT_LEN ) || ( (p>>2) <= 0 ) )
+ continue;
+
+ setPolyF4( pol4 );
+
+ // Set the projected vertices to the primitive
+ gte_stsxy0( &pol4->x0 );
+ gte_stsxy1( &pol4->x1 );
+ gte_stsxy2( &pol4->x2 );
+
+ // Compute the last vertex and set the result
+ gte_ldv0( &verts[py+1][px+1] );
+ gte_rtps();
+ gte_stsxy( &pol4->x3 );
+
+ // Test if quad is off-screen, discard if so
+ // Clipping is important as it not only prevents primitive
+ // overflows (tends to happen on textured polys) but also
+ // saves packet buffer space and speeds up rendering.
+ if( quad_clip( &screen_clip,
+ (DVECTOR*)&pol4->x0, (DVECTOR*)&pol4->x1,
+ (DVECTOR*)&pol4->x2, (DVECTOR*)&pol4->x3 ) )
+ continue;
+
+ gte_avsz4();
+ gte_stotz( &p );
+
+ if((px+py)&0x1) {
+ setRGB0( pol4, 128, 128, 128 );
+ } else {
+ setRGB0( pol4, 255, 255, 255 );
+ }
+
+ addPrim( db[db_active].ot+(p>>2), pol4 );
+ pol4++;
+
+ }
+ }
+
+ // Update nextpri variable (very important)
+ db_nextpri = (char*)pol4;
+
+
+ // Position the cube going around the floor bouncily
+ setVector( &pos,
+ isin( rot.vy )>>4,
+ -300+(isin( rot.vy<<2 )>>5),
+ icos( rot.vy )>>3 );
+
+ // Sort cube
+ sort_cube( &mtx, &pos, &rot );
+
+ // Make the cube SPEEN
+ rot.vx += 8;
+ rot.vy += 8;
+
+
+ // Flush text to drawing area
+ FntFlush(-1);
+
+ // Swap buffers and draw the primitives
+ display();
+
+ }
+
+ return 0;
+
+}
+
+void sort_cube(MATRIX *mtx, VECTOR *pos, SVECTOR *rot) {
+
+ int i,p;
+ POLY_F4 *pol4;
+
+ // Object and light matrix for object
+ MATRIX omtx,lmtx;
+
+ // Set object rotation and position
+ RotMatrix( rot, &omtx );
+ TransMatrix( &omtx, pos );
+
+ // Multiply light matrix to object matrix
+ MulMatrix0( &light_mtx, &omtx, &lmtx );
+
+ // Set result to GTE light matrix
+ gte_SetLightMatrix( &lmtx );
+
+ // Composite coordinate matrix transform, so object will be rotated and
+ // positioned relative to camera matrix (mtx), so it'll appear as
+ // world-space relative.
+ CompMatrixLV( mtx, &omtx, &omtx );
+
+ // Save matrix
+ PushMatrix();
+
+ // Set matrices
+ gte_SetRotMatrix( &omtx );
+ gte_SetTransMatrix( &omtx );
+
+ // Sort the cube
+ pol4 = (POLY_F4*)db_nextpri;
+
+ for( i=0; i<CUBE_FACES; i++ ) {
+
+ // Load the first 3 vertices of a quad to the GTE
+ gte_ldv3(
+ &cube_verts[cube_indices[i].v0],
+ &cube_verts[cube_indices[i].v1],
+ &cube_verts[cube_indices[i].v2] );
+
+ // Rotation, Translation and Perspective Triple
+ gte_rtpt();
+
+ // Compute normal clip for backface culling
+ gte_nclip();
+
+ // Get result
+ gte_stopz( &p );
+
+ // Skip this face if backfaced
+ if( p < 0 )
+ continue;
+
+ // Calculate average Z for depth sorting
+ gte_avsz3();
+ gte_stotz( &p );
+
+ // Skip if clipping off
+ // (the shift right operator is to scale the depth precision)
+ if( ((p>>2) <= 0) || ((p>>2) >= OT_LEN) )
+ continue;
+
+ // Initialize a quad primitive
+ setPolyF4( pol4 );
+
+ // Set the projected vertices to the primitive
+ gte_stsxy0( &pol4->x0 );
+ gte_stsxy1( &pol4->x1 );
+ gte_stsxy2( &pol4->x2 );
+
+ // Compute the last vertex and set the result
+ gte_ldv0( &cube_verts[cube_indices[i].v3] );
+ gte_rtps();
+ gte_stsxy( &pol4->x3 );
+
+ // Test if quad is off-screen, discard if so
+ if( quad_clip( &screen_clip,
+ (DVECTOR*)&pol4->x0, (DVECTOR*)&pol4->x1,
+ (DVECTOR*)&pol4->x2, (DVECTOR*)&pol4->x3 ) )
+ continue;
+
+ // Load primitive color even though gte_ncs() doesn't use it.
+ // This is so the GTE will output a color result with the
+ // correct primitive code.
+ gte_ldrgb( &pol4->r0 );
+
+ // Load the face normal
+ gte_ldv0( &cube_norms[i] );
+
+ // Normal Color Single
+ gte_ncs();
+
+ // Store result to the primitive
+ gte_strgb( &pol4->r0 );
+
+ gte_avsz4();
+ gte_stotz( &p );
+
+ // Sort primitive to the ordering table
+ addPrim( db[db_active].ot+(p>>2), pol4 );
+
+ // Advance to make another primitive
+ pol4++;
+
+ }
+
+ // Update nextpri
+ db_nextpri = (char*)pol4;
+
+ // Restore matrix
+ PopMatrix();
+
+}
+
+void init() {
+
+ // Reset the GPU, also installs a VSync event handler
+ ResetGraph( 0 );
+
+ // Set display and draw environment areas
+ // (display and draw areas must be separate, otherwise hello flicker)
+ SetDefDispEnv( &db[0].disp, 0, 0, SCREEN_XRES, SCREEN_YRES );
+ SetDefDrawEnv( &db[0].draw, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES );
+
+ // Enable draw area clear and dither processing
+ setRGB0( &db[0].draw, 63, 0, 127 );
+ db[0].draw.isbg = 1;
+ db[0].draw.dtd = 1;
+
+
+ // Define the second set of display/draw environments
+ SetDefDispEnv( &db[1].disp, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES );
+ SetDefDrawEnv( &db[1].draw, 0, 0, SCREEN_XRES, SCREEN_YRES );
+
+ setRGB0( &db[1].draw, 63, 0, 127 );
+ db[1].draw.isbg = 1;
+ db[1].draw.dtd = 1;
+
+ // Apply the drawing environment of the first double buffer
+ PutDrawEnv( &db[0].draw );
+
+ // Clear both ordering tables to make sure they are clean at the start
+ ClearOTagR( db[0].ot, OT_LEN );
+ ClearOTagR( db[1].ot, OT_LEN );
+
+ // Set primitive pointer address
+ db_nextpri = db[0].p;
+
+ // Set clip region
+ setRECT( &screen_clip, 0, 0, SCREEN_XRES, SCREEN_YRES );
+
+
+ // Initialize the GTE
+ InitGeom();
+
+ // Set GTE offset (recommended method of centering)
+ gte_SetGeomOffset( CENTERX, CENTERY );
+
+ // Set screen depth (basically FOV control, W/2 works best)
+ gte_SetGeomScreen( CENTERX );
+
+ // Set light ambient color and light color matrix
+ gte_SetBackColor( 63, 63, 63 );
+ gte_SetColorMatrix( &color_mtx );
+
+
+ // Init BIOS pad driver and set pad buffers (buffers are updated
+ // automatically on every V-Blank)
+ InitPAD(&pad_buff[0][0], 34, &pad_buff[1][0], 34);
+
+ // Start pad
+ StartPAD();
+
+ // Don't make pad driver acknowledge V-Blank IRQ (recommended)
+ ChangeClearPAD(0);
+
+ // Load font and open a text stream
+ FntLoad(960, 0);
+ FntOpen(0, 8, 320, 216, 0, 100);
+
+}
+
+void display() {
+
+ // Wait for GPU to finish drawing and vertical retrace
+ DrawSync( 0 );
+ VSync( 0 );
+
+ // Swap buffers
+ db_active ^= 1;
+ db_nextpri = db[db_active].p;
+
+ // Clear the OT of the next frame
+ ClearOTagR( db[db_active].ot, OT_LEN );
+
+ // Apply display/drawing environments
+ PutDrawEnv( &db[db_active].draw );
+ PutDispEnv( &db[db_active].disp );
+
+ // Enable display
+ SetDispMask( 1 );
+
+ // Start drawing the OT of the last buffer
+ DrawOTag( db[1-db_active].ot+(OT_LEN-1) );
+
+}
+
diff --git a/examples/graphics/fpscam/makefile b/examples/graphics/fpscam/makefile
new file mode 100644
index 0000000..339bb91
--- /dev/null
+++ b/examples/graphics/fpscam/makefile
@@ -0,0 +1,39 @@
+include ../../sdk-common.mk
+
+TARGET = fpscam.elf
+
+CFILES = $(notdir $(wildcard *.c))
+CPPFILES = $(notdir $(wildcard *.cpp))
+AFILES = $(notdir $(wildcard *.s))
+
+OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o))
+
+INCLUDE +=
+LIBDIRS +=
+
+LIBS = -lpsxetc -lpsxgpu -lpsxgte -lpsxspu -lpsxapi -lc
+
+CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections
+CPPFLAGS = $(CFLAGS) -fno-exceptions
+AFLAGS = -g -msoft-float
+LDFLAGS = -g -Ttext=0x80010000 -gc-sections -T $(GCC_BASE)/mipsel-unknown-elf/lib/ldscripts/elf32elmip.x
+
+CC = $(PREFIX)gcc
+CXX = $(PREFIX)g++
+AS = $(PREFIX)as
+LD = $(PREFIX)ld
+
+all: $(OFILES)
+ $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET)
+ elf2x -q $(TARGET)
+
+build/%.o: %.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.s
+ @mkdir -p $(dir $@)
+ $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+
+clean:
+ rm -rf build $(TARGET) $(TARGET:.elf=.exe)
diff --git a/examples/graphics/gte/main.c b/examples/graphics/gte/main.c
new file mode 100644
index 0000000..432ef95
--- /dev/null
+++ b/examples/graphics/gte/main.c
@@ -0,0 +1,313 @@
+/*
+ * LibPSn00b Example Programs
+ *
+ * GTE Graphics Example
+ * 2019 Meido-Tek Productions / PSn00bSDK Project
+ *
+ * Renders a spinning 3D cube with light source calculation
+ * using GTE macros.
+ *
+ *
+ * Example by Lameguy64
+ *
+ * Changelog:
+ *
+ * Jan 26, 2019 - Initial version.
+ *
+ */
+
+#include <stdio.h>
+#include <psxgpu.h>
+#include <psxgte.h>
+#include <inline_c.h>
+
+/* OT and Packet Buffer sizes */
+#define OT_LEN 256
+#define PACKET_LEN 1024
+
+/* Screen resolution */
+#define SCREEN_XRES 320
+#define SCREEN_YRES 240
+
+/* Screen center position */
+#define CENTERX SCREEN_XRES>>1
+#define CENTERY SCREEN_YRES>>1
+
+
+/* Double buffer structure */
+typedef struct {
+ DISPENV disp; /* Display environment */
+ DRAWENV draw; /* Drawing environment */
+ int ot[OT_LEN]; /* Ordering table */
+ char p[PACKET_LEN]; /* Packet buffer */
+} DB;
+
+/* Double buffer variables */
+DB db[2];
+int db_active = 0;
+char *db_nextpri;
+
+
+/* For easier handling of vertex indices */
+typedef struct {
+ short v0,v1,v2,v3;
+} INDEX;
+
+/* Cube vertices */
+SVECTOR cube_verts[] = {
+ { -100, -100, -100, 0 },
+ { 100, -100, -100, 0 },
+ { -100, 100, -100, 0 },
+ { 100, 100, -100, 0 },
+ { 100, -100, 100, 0 },
+ { -100, -100, 100, 0 },
+ { 100, 100, 100, 0 },
+ { -100, 100, 100, 0 }
+};
+
+/* Cube face normals */
+SVECTOR cube_norms[] = {
+ { 0, 0, -ONE, 0 },
+ { 0, 0, ONE, 0 },
+ { 0, -ONE, 0, 0 },
+ { 0, ONE, 0, 0 },
+ { -ONE, 0, 0, 0 },
+ { ONE, 0, 0, 0 }
+};
+
+/* Cube vertex indices */
+INDEX cube_indices[] = {
+ { 0, 1, 2, 3 },
+ { 4, 5, 6, 7 },
+ { 5, 4, 0, 1 },
+ { 6, 7, 3, 2 },
+ { 0, 2, 5, 7 },
+ { 3, 1, 6, 4 }
+};
+
+/* Number of faces of cube */
+#define CUBE_FACES 6
+
+
+/* Light color matrix */
+/* Each column represents the color matrix of each light source and is */
+/* used as material color when using gte_ncs() or multiplied by a */
+/* source color when using gte_nccs(). 4096 is 1.0 in this matrix */
+/* A column of zeroes disables the light source. */
+MATRIX color_mtx = {
+ ONE, 0, 0, /* Red */
+ ONE, 0, 0, /* Green */
+ ONE, 0, 0 /* Blue */
+};
+
+/* Light matrix */
+/* Each row represents a vector direction of each light source. */
+/* An entire row of zeroes disables the light source. */
+MATRIX light_mtx = {
+ /* X, Y, Z */
+ -2048 , -2048 , -2048,
+ 0 , 0 , 0,
+ 0 , 0 , 0
+};
+
+
+/* Function declarations */
+void init();
+void display();
+
+
+/* Main function */
+int main() {
+
+ int i,p,xy_temp;
+
+ SVECTOR rot = { 0 }; /* Rotation vector for Rotmatrix */
+ VECTOR pos = { 0, 0, 400 }; /* Translation vector for TransMatrix */
+ MATRIX mtx,lmtx; /* Rotation matrices for geometry and lighting */
+
+ POLY_F4 *pol4; /* Flat shaded quad primitive pointer */
+
+
+ /* Init graphics and GTE */
+ init();
+
+
+ /* Main loop */
+ while( 1 ) {
+
+ /* Set rotation and translation to the matrix */
+ RotMatrix( &rot, &mtx );
+ TransMatrix( &mtx, &pos );
+
+ /* Multiply light matrix by rotation matrix so light source */
+ /* won't appear relative to the model's rotation */
+ MulMatrix0( &light_mtx, &mtx, &lmtx );
+
+ /* Set rotation and translation matrix */
+ gte_SetRotMatrix( &mtx );
+ gte_SetTransMatrix( &mtx );
+
+ /* Set light matrix */
+ gte_SetLightMatrix( &lmtx );
+
+ /* Make the cube SPEEN */
+ rot.vx += 16;
+ rot.vz += 16;
+
+
+ /* Draw the cube */
+ pol4 = (POLY_F4*)db_nextpri;
+
+ for( i=0; i<CUBE_FACES; i++ ) {
+
+ /* Load the first 3 vertices of a quad to the GTE */
+ gte_ldv3(
+ &cube_verts[cube_indices[i].v0],
+ &cube_verts[cube_indices[i].v1],
+ &cube_verts[cube_indices[i].v2] );
+
+ /* Rotation, Translation and Perspective Triple */
+ gte_rtpt();
+
+ /* Compute normal clip for backface culling */
+ gte_nclip();
+
+ /* Get result*/
+ gte_stopz( &p );
+
+ /* Skip this face if backfaced */
+ if( p < 0 )
+ continue;
+
+ /* Calculate average Z for depth sorting */
+ gte_avsz4();
+ gte_stotz( &p );
+
+ /* Skip if clipping off */
+ /* (the shift right operator is to scale the depth precision) */
+ if( (p>>2) > OT_LEN )
+ continue;
+
+ /* Initialize a quad primitive */
+ setPolyF4( pol4 );
+
+ /* Set the projected vertices to the primitive */
+ gte_stsxy0( &pol4->x0 );
+ gte_stsxy1( &pol4->x1 );
+ gte_stsxy2( &pol4->x2 );
+
+ /* Compute the last vertex and set the result */
+ gte_ldv0( &cube_verts[cube_indices[i].v3] );
+ gte_rtps();
+ gte_stsxy( &pol4->x3 );
+
+ /* Load primitive color even though gte_ncs() doesn't use it. */
+ /* This is so the GTE will output a color result with the */
+ /* correct primitive code. */
+ gte_ldrgb( &pol4->r0 );
+
+ /* Load the face normal */
+ gte_ldv0( &cube_norms[i] );
+
+ /* Normal Color Single */
+ gte_ncs();
+
+ /* Store result to the primitive */
+ gte_strgb( &pol4->r0 );
+
+ /* Sort primitive to the ordering table */
+ addPrim( db[db_active].ot+(p>>2), pol4 );
+
+ /* Advance to make another primitive */
+ pol4++;
+
+ }
+
+ /* Update nextpri variable */
+ /* (IMPORTANT if you plan to sort more primitives after this) */
+ db_nextpri = (char*)pol4;
+
+ /* Swap buffers and draw the primitives */
+ display();
+
+ }
+
+ return 0;
+
+}
+
+void init() {
+
+ /* Reset the GPU, also installs a VSync event handler */
+ ResetGraph( 0 );
+
+ /* Set display and draw environment areas */
+ /* (display and draw areas must be separate, otherwise hello flicker) */
+ SetDefDispEnv( &db[0].disp, 0, 0, SCREEN_XRES, SCREEN_YRES );
+ SetDefDrawEnv( &db[0].draw, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES );
+
+ /* Enable draw area clear and dither processing */
+ setRGB0( &db[0].draw, 63, 0, 127 );
+ db[0].draw.isbg = 1;
+ db[0].draw.dtd = 1;
+
+
+ /* Define the second set of display/draw environments */
+ SetDefDispEnv( &db[1].disp, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES );
+ SetDefDrawEnv( &db[1].draw, 0, 0, SCREEN_XRES, SCREEN_YRES );
+
+ setRGB0( &db[1].draw, 63, 0, 127 );
+ db[1].draw.isbg = 1;
+ db[1].draw.dtd = 1;
+
+
+ /* Apply the drawing environment of the first double buffer */
+ PutDrawEnv( &db[0].draw );
+
+
+ /* Clear both ordering tables to make sure they are clean at the start */
+ ClearOTagR( db[0].ot, OT_LEN );
+ ClearOTagR( db[1].ot, OT_LEN );
+
+ /* Set primitive pointer address */
+ db_nextpri = db[0].p;
+
+ /* Initialize the GTE */
+ InitGeom();
+
+ /* Set GTE offset (recommended method of centering) */
+ gte_SetGeomOffset( CENTERX, CENTERY );
+
+ /* Set screen depth (basically FOV control, W/2 works best) */
+ gte_SetGeomScreen( CENTERX );
+
+ /* Set light ambient color and light color matrix */
+ gte_SetBackColor( 63, 63, 63 );
+ gte_SetColorMatrix( &color_mtx );
+
+}
+
+void display() {
+
+ /* Wait for GPU to finish drawing and vertical retrace */
+ DrawSync( 0 );
+ VSync( 0 );
+
+ /* Swap buffers */
+ db_active ^= 1;
+ db_nextpri = db[db_active].p;
+
+ /* Clear the OT of the next frame */
+ ClearOTagR( db[db_active].ot, OT_LEN );
+
+ /* Apply display/drawing environments */
+ PutDrawEnv( &db[db_active].draw );
+ PutDispEnv( &db[db_active].disp );
+
+ /* Enable display */
+ SetDispMask( 1 );
+
+ /* Start drawing the OT of the last buffer */
+ DrawOTag( db[1-db_active].ot+(OT_LEN-1) );
+
+} \ No newline at end of file
diff --git a/examples/graphics/gte/makefile b/examples/graphics/gte/makefile
new file mode 100644
index 0000000..8b3f81f
--- /dev/null
+++ b/examples/graphics/gte/makefile
@@ -0,0 +1,39 @@
+include ../../sdk-common.mk
+
+TARGET = gte.elf
+
+CFILES = $(notdir $(wildcard *.c))
+CPPFILES = $(notdir $(wildcard *.cpp))
+AFILES = $(notdir $(wildcard *.s))
+
+OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o))
+
+INCLUDE +=
+LIBDIRS +=
+
+LIBS = -lc -lpsxetc -lpsxgpu -lpsxgte -lpsxspu -lpsxapi -lc
+
+CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections
+CPPFLAGS = $(CFLAGS) -fno-exceptions
+AFLAGS = -g -msoft-float
+LDFLAGS = -g -Ttext=0x80010000 -gc-sections -T $(GCC_BASE)/mipsel-unknown-elf/lib/ldscripts/elf32elmip.x
+
+CC = $(PREFIX)gcc
+CXX = $(PREFIX)g++
+AS = $(PREFIX)as
+LD = $(PREFIX)ld
+
+all: $(OFILES)
+ $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET)
+ elf2x -q $(TARGET)
+
+build/%.o: %.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.s
+ @mkdir -p $(dir $@)
+ $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+
+clean:
+ rm -rf build $(TARGET) $(TARGET:.elf=.exe)
diff --git a/examples/graphics/render2tex/blendpattern-16c.png b/examples/graphics/render2tex/blendpattern-16c.png
new file mode 100644
index 0000000..74ac945
--- /dev/null
+++ b/examples/graphics/render2tex/blendpattern-16c.png
Binary files differ
diff --git a/examples/graphics/render2tex/blendpattern-16c.tim b/examples/graphics/render2tex/blendpattern-16c.tim
new file mode 100644
index 0000000..2fff580
--- /dev/null
+++ b/examples/graphics/render2tex/blendpattern-16c.tim
Binary files differ
diff --git a/examples/graphics/render2tex/blendpattern.png b/examples/graphics/render2tex/blendpattern.png
new file mode 100644
index 0000000..49af62c
--- /dev/null
+++ b/examples/graphics/render2tex/blendpattern.png
Binary files differ
diff --git a/examples/graphics/render2tex/main.c b/examples/graphics/render2tex/main.c
new file mode 100644
index 0000000..6ae450a
--- /dev/null
+++ b/examples/graphics/render2tex/main.c
@@ -0,0 +1,649 @@
+/*
+ * LibPSn00b Example Programs
+ *
+ * Off-screen Render to Texture Example
+ * 2019 Meido-Tek Productions / PSn00bSDK Project
+ *
+ * Demonstrates quick render to texture for multi-texture style effects,
+ * view screens and more. This example also shows how to use multiple
+ * ordering tables and chaining them together so it can all be rendered
+ * with a single DrawOTag() call.
+ *
+ * Example by Lameguy64
+ *
+ * Changelog:
+ *
+ * Oct 26, 2019 - Initial version.
+ *
+ */
+
+#include <stdio.h>
+#include <psxgpu.h>
+#include <psxgte.h>
+#include <inline_c.h>
+
+
+/* OT and Packet Buffer sizes */
+#define OT_LEN 256
+#define PACKET_LEN 1024
+
+
+/* Screen resolution */
+/* (note: display/draw code is hardcoded for double buffer) */
+#define SCREEN_XRES 320
+#define SCREEN_YRES 240
+
+
+/* Screen center position */
+#define CENTERX SCREEN_XRES>>1
+#define CENTERY SCREEN_YRES>>1
+
+
+/* Double buffer structure */
+typedef struct DB
+{
+ DISPENV disp; /* Display environment */
+ DRAWENV draw; /* Drawing environment */
+ int ot[OT_LEN]; /* Main ordering table */
+ int sub_ot[2][4]; /* Second ordering table for r2t stuff */
+ char p[PACKET_LEN]; /* Packet buffer */
+} DB;
+
+
+/* Double buffer variables */
+DB db[2];
+int db_active = 0;
+char *db_nextpri;
+
+
+/* For easier handling of vertex indices */
+typedef struct {
+ short v0,v1,v2,v3;
+} INDEX;
+
+/* Cube vertices */
+SVECTOR cube_verts[] = {
+ { -100, -100, -100, 0 },
+ { 100, -100, -100, 0 },
+ { -100, 100, -100, 0 },
+ { 100, 100, -100, 0 },
+ { 100, -100, 100, 0 },
+ { -100, -100, 100, 0 },
+ { 100, 100, 100, 0 },
+ { -100, 100, 100, 0 }
+};
+
+/* Cube face normals */
+SVECTOR cube_norms[] = {
+ { 0, 0, -ONE, 0 },
+ { 0, -ONE, 0, 0 },
+ { 0, 0, ONE, 0 },
+ { 0, ONE, 0, 0 },
+ { ONE, 0, 0, 0 },
+ { -ONE, 0, 0, 0 }
+};
+
+/* Cube vertex indices */
+INDEX cube_indices[] = {
+ { 0, 1, 2, 3 },
+ { 5, 4, 0, 1 },
+ { 4, 5, 6, 7 },
+ { 6, 7, 3, 2 },
+ { 3, 1, 6, 4 },
+ { 0, 2, 5, 7 }
+};
+
+/* Number of cube faces */
+#define CUBE_FACES 6
+
+/* Light color matrix */
+MATRIX color_mtx = {
+ ONE, 0, 0, /* Red */
+ ONE, 0, 0, /* Green */
+ ONE, 0, 0 /* Blue */
+};
+
+/* Light matrix */
+MATRIX light_mtx = {
+ /* X, Y, Z */
+ -2048 , -2048 , -2048,
+ 0 , 0 , 0,
+ 0 , 0 , 0
+};
+
+
+/* Reference texture data */
+extern int tim_blendpattern[];
+
+
+/* TPage and CLUT values */
+unsigned short rendertex_tpage; /* For the render to texture cube */
+unsigned short bpattern_tpage; /* For the scrolling blending pattern */
+unsigned short bpattern_clut;
+
+
+/* Function declarations */
+void init();
+void display();
+
+/* This function sorts a cube that is drawn
+ * to an offscreen area specified by *area */
+void sort_cube(int *ot, RECT *area);
+void sort_multitex(int *ot, RECT *area, int count);
+
+/* Main function */
+int main() {
+
+ int i,p,xy_temp;
+ int count = 0;
+
+ SVECTOR rot = { 0 }; /* Rotation vector for Rotmatrix */
+ VECTOR pos = { 0, 0, 400 }; /* Translation vector for TransMatrix */
+ MATRIX mtx,lmtx; /* Rotation matrices for geometry and lighting */
+
+ VECTOR spos = { 0, 0, 250 };
+ SVECTOR srot = { 0 };
+
+ RECT texarea,area,dtexarea;
+ RECT cubearea;
+
+ POLY_FT4 *pol4; /* Flat shaded quad primitive pointer */
+
+
+ /* Init graphics and GTE */
+ init();
+
+ setRECT(&area, 704, 0, 64, 64);
+ setRECT(&cubearea, 704, 64, 64, 64);
+
+ setRECT(&texarea, 0, 0, 64>>3, 64>>3);
+ setRECT(&dtexarea, 0, 0, 0, 0);
+
+ rendertex_tpage = getTPage(2, 0, area.x, area.y);
+
+ /* Main loop */
+ while( 1 ) {
+
+ /* Sort multi-texture stuff */
+ sort_multitex(db[db_active].ot+(OT_LEN-1), &area, count);
+
+ /* Matrix stuff for render to texture cube */
+ RotMatrix( &srot, &mtx );
+ TransMatrix( &mtx, &spos );
+
+ MulMatrix0( &light_mtx, &mtx, &lmtx );
+
+ gte_SetRotMatrix( &mtx );
+ gte_SetTransMatrix( &mtx );
+ gte_SetLightMatrix( &lmtx );
+
+ /* Reduce FOV to fit in area and sort cube */
+ gte_SetGeomScreen(32);
+
+ sort_cube(db[db_active].ot+(OT_LEN-1), &cubearea);
+
+ gte_SetGeomScreen(CENTERX);
+
+
+ /* Set rotation and translation to the matrix */
+ RotMatrix( &rot, &mtx );
+ TransMatrix( &mtx, &pos );
+
+ /* Multiply light matrix by rotation matrix so light source */
+ /* won't appear relative to the model's rotation */
+ MulMatrix0( &light_mtx, &mtx, &lmtx );
+
+ /* Set rotation and translation matrix */
+ gte_SetRotMatrix( &mtx );
+ gte_SetTransMatrix( &mtx );
+
+ /* Set light matrix */
+ gte_SetLightMatrix( &lmtx );
+
+ /* Make the cube SPEEN */
+ rot.vx += 4;
+ rot.vz += 4;
+
+ srot.vx += 8;
+ srot.vy += 8;
+ srot.vz -= 8;
+
+ /* Draw the cube */
+ pol4 = (POLY_FT4*)db_nextpri;
+
+ for( i=0; i<CUBE_FACES; i++ ) {
+
+ /* Load the first 3 vertices of a quad to the GTE */
+ gte_ldv3(
+ &cube_verts[cube_indices[i].v0],
+ &cube_verts[cube_indices[i].v1],
+ &cube_verts[cube_indices[i].v2] );
+
+ /* Rotation, Translation and Perspective Triple */
+ gte_rtpt();
+
+ /* Compute normal clip for backface culling */
+ gte_nclip();
+
+ /* Get result*/
+ gte_stopz( &p );
+
+ /* Skip this face if backfaced */
+ if( p < 0 )
+ continue;
+
+ /* Calculate average Z for depth sorting */
+ gte_avsz4();
+ gte_stotz( &p );
+
+ /* Skip if clipping off */
+ /* (the shift right operator is to scale the depth precision) */
+ if( (p>>2) > OT_LEN )
+ continue;
+
+ /* Initialize a quad primitive */
+ setPolyFT4( pol4 );
+
+ /* Set the projected vertices to the primitive */
+ gte_stsxy0( &pol4->x0 );
+ gte_stsxy1( &pol4->x1 );
+ gte_stsxy2( &pol4->x2 );
+
+ /* Compute the last vertex and set the result */
+ gte_ldv0( &cube_verts[cube_indices[i].v3] );
+ gte_rtps();
+ gte_stsxy( &pol4->x3 );
+
+ /* Load primitive color even though gte_ncs() doesn't use it. */
+ /* This is so the GTE will output a color result with the */
+ /* correct primitive code. */
+ gte_ldrgb( &pol4->r0 );
+
+ /* Load the face normal */
+ gte_ldv0( &cube_norms[i] );
+
+ /* Normal Color Single */
+ gte_ncs();
+
+ /* Store result to the primitive */
+ gte_strgb( &pol4->r0 );
+
+ // Map to render to texture texture
+ pol4->tpage = rendertex_tpage;
+ if( (i&0x1) == 0 )
+ setUVWH(pol4, 0, 0, 63, 63);
+ else
+ setUVWH(pol4, 0, 64, 63, 63);
+
+
+ /* Sort primitive to the ordering table */
+ addPrim( db[db_active].ot+(p>>2), pol4 );
+
+ /* Advance to make another primitive */
+ pol4++;
+
+ }
+
+ /* Update nextpri variable */
+ /* (IMPORTANT if you plan to sort more primitives after this) */
+ db_nextpri = (char*)pol4;
+
+ /* Swap buffers and draw the primitives */
+ display();
+
+ count++;
+
+ }
+
+ return 0;
+
+}
+
+void init() {
+
+ TIM_IMAGE tim;
+
+ /* Reset the GPU, also installs a VSync event handler */
+ ResetGraph( 0 );
+
+ /* Set display and draw environment areas */
+ /* (display and draw areas must be separate, otherwise hello flicker) */
+ SetDefDispEnv( &db[0].disp, 0, 0, SCREEN_XRES, SCREEN_YRES );
+ SetDefDrawEnv( &db[0].draw, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES );
+
+ /* Enable draw area clear and dither processing */
+ setRGB0( &db[0].draw, 63, 0, 127 );
+ db[0].draw.isbg = 1;
+ db[0].draw.dtd = 1;
+
+
+ /* Define the second set of display/draw environments */
+ SetDefDispEnv( &db[1].disp, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES );
+ SetDefDrawEnv( &db[1].draw, 0, 0, SCREEN_XRES, SCREEN_YRES );
+
+ setRGB0( &db[1].draw, 63, 0, 127 );
+ db[1].draw.isbg = 1;
+ db[1].draw.dtd = 1;
+
+
+ /* Apply the drawing environment of the first double buffer */
+ PutDrawEnv( &db[0].draw );
+
+
+ /* Clear both ordering tables to make sure they are clean at the start */
+ ClearOTagR( db[0].ot, OT_LEN );
+ ClearOTagR( db[1].ot, OT_LEN );
+ ClearOTagR( db[0].sub_ot[0], 4 );
+ ClearOTagR( db[0].sub_ot[1], 4 );
+ ClearOTagR( db[1].sub_ot[0], 4 );
+ ClearOTagR( db[1].sub_ot[1], 4 );
+
+ /* Set primitive pointer address */
+ db_nextpri = db[0].p;
+
+ /* Initialize the GTE */
+ InitGeom();
+
+ /* Set GTE offset (recommended method of centering) */
+ gte_SetGeomOffset( CENTERX, CENTERY );
+
+ /* Set screen depth (basically FOV control, W/2 works best) */
+ gte_SetGeomScreen( CENTERX );
+
+ /* Set light ambient color and light color matrix */
+ gte_SetBackColor( 63, 63, 63 );
+ gte_SetColorMatrix( &color_mtx );
+
+ GetTimInfo(tim_blendpattern, &tim);
+ if( tim.mode & 0x8 )
+ {
+ LoadImage( tim.crect, tim.caddr ); /* Upload CLUT if present */
+ }
+ LoadImage( tim.prect, tim.paddr ); /* Upload texture to VRAM */
+
+ bpattern_tpage = getTPage(0, 1, tim.prect->x, tim.prect->y);
+ bpattern_clut = getClut(tim.crect->x, tim.crect->y);
+
+}
+
+void display() {
+
+ /* Wait for GPU to finish drawing and vertical retrace */
+ DrawSync( 0 );
+ VSync( 0 );
+
+ /* Swap buffers */
+ db_active ^= 1;
+ db_nextpri = db[db_active].p;
+
+ /* Clear the OT of the next frame */
+ ClearOTagR( db[db_active].ot, OT_LEN );
+ ClearOTagR( db[db_active].sub_ot[0], 4 );
+ ClearOTagR( db[db_active].sub_ot[1], 4 );
+
+ /* Apply display/drawing environments */
+ PutDrawEnv( &db[db_active].draw );
+ PutDispEnv( &db[db_active].disp );
+
+ /* Enable display */
+ SetDispMask( 1 );
+
+ /* Start drawing the OT of the last buffer */
+ DrawOTag( db[1-db_active].ot+(OT_LEN-1) );
+
+}
+
+void sort_multitex(int *ot, RECT *area, int count)
+{
+ DR_TPAGE *ptpage;
+ FILL *pfill;
+ DR_AREA *parea;
+ DR_TWIN *ptwin;
+ DR_OFFSET *poffs;
+
+ SPRT *psprt;
+
+ /* Texture window constraint */
+ /* (coordinates specified in units of 8 pixels) */
+ RECT texwindow = { 0, 0, 64>>3, 64>>3 };
+
+ /* Sort the sub OT to the specified OT level */
+ addPrims(
+ ot, /* Target OT */
+ db[db_active].sub_ot[0]+3, /* Start of OT to sort */
+ db[db_active].sub_ot[0]); /* End of OT to sort */
+
+
+ /* Sort a FILL primitive to clear the off-screen area */
+ pfill = (FILL*)db_nextpri;
+ setFill(pfill);
+ setXY0(pfill, area->x, area->y);
+ setWH(pfill, 64, 64);
+ setRGB0(pfill, 0, 0, 0);
+ addPrim(db[db_active].sub_ot[0]+3, pfill);
+ db_nextpri += sizeof(FILL);
+
+
+ /* Sort draw area primitives to set the drawing target */
+ parea = (DR_AREA*)db_nextpri;
+
+ setDrawArea(parea, area); /* Sets to off-screen area */
+ addPrim(db[db_active].sub_ot[0]+3, parea);
+ parea++;
+
+ setDrawArea(parea, /* Reverts to draw area */
+ &db[1-db_active].draw.clip);
+ addPrim(db[db_active].sub_ot[0]+1, parea);
+ parea++;
+ db_nextpri = (char*)parea;
+
+
+ /* Sort offset primitives to set the drawing offset to the target */
+ poffs = (DR_OFFSET*)db_nextpri;
+
+ setDrawOffset(poffs, area->x, area->y); /* Sets to off-screen area */
+ addPrim(db[db_active].sub_ot[0]+3, poffs);
+ poffs++;
+
+ setDrawOffset(poffs, /* Reverts to draw area */
+ db[1-db_active].draw.clip.x,
+ db[1-db_active].draw.clip.y);
+ addPrim(db[db_active].sub_ot[0]+1, poffs);
+ poffs++;
+ db_nextpri = (char*)poffs;
+
+
+ /* This sets the active texture page for the SPRT primitives */
+ ptpage = (DR_TPAGE*)db_nextpri;
+ setDrawTPage(ptpage, 1, 0, bpattern_tpage);
+ addPrim(db[db_active].sub_ot[0]+3, ptpage);
+ ptpage++;
+ db_nextpri = (char*)ptpage;
+
+
+ /* Sort a DR_TWIN primitive to wrap texture coordinates to 64x64 */
+ ptwin = (DR_TWIN*)db_nextpri;
+
+ setTexWindow(ptwin, &texwindow); /* Set window constraint */
+ addPrim(db[db_active].sub_ot[0]+3, ptwin);
+ ptwin++;
+
+ texwindow.w = 0; /* Clear window constraint */
+ texwindow.h = 0;
+ setTexWindow(ptwin, &texwindow);
+ addPrim(db[db_active].sub_ot[0]+1, ptwin);
+ ptwin++;
+ db_nextpri = (char*)ptwin;
+
+
+ /* Sort blending and scrolling sprites layering over one another */
+ psprt = (SPRT*)db_nextpri;
+
+ /* Sort pattern in green scrolling up-left */
+ setSprt(psprt);
+ setSemiTrans(psprt, 1);
+ setXY0(psprt, 0, 0);
+ setWH(psprt, 64, 64);
+ setUV0(psprt, (count>>1)&0x3F, count&0x3F);
+ setRGB0(psprt, 0, 91, 0);
+ psprt->clut = bpattern_clut;
+ addPrim(db[db_active].sub_ot[0]+1, psprt);
+ psprt++;
+
+ /* Sort pattern in blue scrolling up-right*/
+ setSprt(psprt);
+ setSemiTrans(psprt, 1);
+ setXY0(psprt, 0, 0);
+ setWH(psprt, 64, 64);
+ setUV0(psprt, (-count>>1)&0x3F, (count>>1)&0x3F);
+ setRGB0(psprt, 0, 0, 91);
+ psprt->clut = bpattern_clut;
+ addPrim(db[db_active].sub_ot[0]+1, psprt);
+ psprt++;
+
+ /* Sort pattern in red scrolling down-right */
+ setSprt(psprt);
+ setSemiTrans(psprt, 1);
+ setXY0(psprt, 0, 0);
+ setWH(psprt, 64, 64);
+ setUV0(psprt, (-count>>1)&0x3F, (-count>>1)&0x3F);
+ setRGB0(psprt, 91, 0, 0);
+ psprt->clut = bpattern_clut;
+ addPrim(db[db_active].sub_ot[0]+1, psprt);
+ psprt++;
+
+ /* Sort pattern in grey scrolling up-left */
+ setSprt(psprt);
+ setXY0(psprt, 0, 0);
+ setWH(psprt, 64, 64);
+ setUV0(psprt, count&0x3F, (count>>1)&0x3F);
+ setRGB0(psprt, 64, 64, 64);
+ psprt->clut = bpattern_clut;
+ addPrim(db[db_active].sub_ot[0]+1, psprt);
+ psprt++;
+
+ db_nextpri = (char*)psprt;
+
+}
+
+void sort_cube(int *ot, RECT *area)
+{
+ int i,p;
+ POLY_FT4* pol4;
+ FILL* pfill;
+ DR_AREA* parea;
+ DR_OFFSET* poffs;
+
+ addPrims(
+ ot,
+ db[db_active].sub_ot[1]+3,
+ db[db_active].sub_ot[1]);
+
+ pfill = (FILL*)db_nextpri;
+ setFill(pfill);
+ setXY0(pfill, area->x, area->y);
+ setWH(pfill, 64, 64);
+ setRGB0(pfill, 128, 91, 0);
+ addPrim(db[db_active].sub_ot[1]+3, pfill);
+ db_nextpri += sizeof(FILL);
+
+ parea = (DR_AREA*)db_nextpri;
+ setDrawArea(parea, area);
+ addPrim(db[db_active].sub_ot[1]+3, parea);
+ parea++;
+ setDrawArea(parea, &db[1-db_active].draw.clip);
+ addPrim(db[db_active].sub_ot[1]+1, parea);
+ parea++;
+ db_nextpri = (char*)parea;
+
+ poffs = (DR_OFFSET*)db_nextpri;
+ setDrawOffset(poffs, area->x, area->y);
+ addPrim(db[db_active].sub_ot[1]+3, poffs);
+ poffs++;
+ setDrawOffset(poffs, db[1-db_active].draw.clip.x, db[1-db_active].draw.clip.y);
+ addPrim(db[db_active].sub_ot[1]+1, poffs);
+ poffs++;
+ db_nextpri = (char*)poffs;
+
+
+ gte_SetGeomOffset(32, 32);
+
+ // Sort the cube
+ pol4 = (POLY_FT4*)db_nextpri;
+
+ for( i=0; i<CUBE_FACES; i++ ) {
+
+ // Load the first 3 vertices of a quad to the GTE
+ gte_ldv3(
+ &cube_verts[cube_indices[i].v0],
+ &cube_verts[cube_indices[i].v1],
+ &cube_verts[cube_indices[i].v2] );
+
+ // Rotation, Translation and Perspective Triple
+ gte_rtpt();
+
+ // Compute normal clip for backface culling
+ gte_nclip();
+
+ // Get result
+ gte_stopz( &p );
+
+ // Skip this face if backfaced
+ if( p < 0 )
+ continue;
+
+ // Calculate average Z for depth sorting
+ gte_avsz3();
+ gte_stotz( &p );
+
+ // Skip if clipping off
+ // (the shift right operator is to scale the depth precision)
+ /*if( ((p>>6) <= 0) || ((p>>6) >= 4) )
+ continue;*/
+
+ // Initialize a quad primitive
+ setPolyFT4( pol4 );
+
+ // Set the projected vertices to the primitive
+ gte_stsxy0( &pol4->x0 );
+ gte_stsxy1( &pol4->x1 );
+ gte_stsxy2( &pol4->x2 );
+
+ // Compute the last vertex and set the result
+ gte_ldv0( &cube_verts[cube_indices[i].v3] );
+ gte_rtps();
+ gte_stsxy( &pol4->x3 );
+
+ // Load primitive color even though gte_ncs() doesn't use it.
+ // This is so the GTE will output a color result with the
+ // correct primitive code.
+ gte_ldrgb( &pol4->r0 );
+
+ // Load the face normal
+ gte_ldv0( &cube_norms[i] );
+
+ // Normal Color Single
+ gte_ncs();
+
+ // Store result to the primitive
+ gte_strgb( &pol4->r0 );
+
+ gte_avsz4();
+ gte_stotz( &p );
+
+ pol4->tpage = rendertex_tpage;
+ setUVWH(pol4, 0, 0, 63, 63);
+
+ // Sort primitive to the ordering table
+ addPrim( db[db_active].sub_ot[1]+1, pol4 );
+
+ // Advance to make another primitive
+ pol4++;
+
+ }
+
+ // Update nextpri
+ db_nextpri = (char*)pol4;
+
+ gte_SetGeomOffset(CENTERX, CENTERY);
+} \ No newline at end of file
diff --git a/examples/graphics/render2tex/makefile b/examples/graphics/render2tex/makefile
new file mode 100644
index 0000000..aaa3b1d
--- /dev/null
+++ b/examples/graphics/render2tex/makefile
@@ -0,0 +1,39 @@
+include ../../sdk-common.mk
+
+TARGET = render2tex.elf
+
+CFILES = $(notdir $(wildcard *.c))
+CPPFILES = $(notdir $(wildcard *.cpp))
+AFILES = $(notdir $(wildcard *.s))
+
+OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o))
+
+INCLUDE +=
+LIBDIRS +=
+
+LIBS = -lc -lpsxetc -lpsxgpu -lpsxgte -lpsxspu -lpsxapi -lgcc
+
+CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections
+CPPFLAGS = $(CFLAGS) -fno-exceptions
+AFLAGS = -g -msoft-float
+LDFLAGS = -g -Ttext=0x80010000 -gc-sections -T $(GCC_BASE)/mipsel-unknown-elf/lib/ldscripts/elf32elmip.x
+
+CC = $(PREFIX)gcc
+CXX = $(PREFIX)g++
+AS = $(PREFIX)as
+LD = $(PREFIX)ld
+
+all: $(OFILES)
+ $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET)
+ elf2x -q $(TARGET)
+
+build/%.o: %.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.s
+ @mkdir -p $(dir $@)
+ $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+
+clean:
+ rm -rf build $(TARGET) $(TARGET:.elf=.exe)
diff --git a/examples/graphics/render2tex/texture.s b/examples/graphics/render2tex/texture.s
new file mode 100644
index 0000000..e786dce
--- /dev/null
+++ b/examples/graphics/render2tex/texture.s
@@ -0,0 +1,9 @@
+# Assembler file for including the texture file in a more elegant manner
+
+.section .data
+
+.global tim_blendpattern
+.type tim_blendpattern, @object
+tim_blendpattern:
+ .incbin "blendpattern-16c.tim"
+ \ No newline at end of file
diff --git a/examples/graphics/rgb24/bunpattern.tim b/examples/graphics/rgb24/bunpattern.tim
new file mode 100644
index 0000000..f233453
--- /dev/null
+++ b/examples/graphics/rgb24/bunpattern.tim
Binary files differ
diff --git a/examples/graphics/rgb24/main.c b/examples/graphics/rgb24/main.c
new file mode 100644
index 0000000..9f1a647
--- /dev/null
+++ b/examples/graphics/rgb24/main.c
@@ -0,0 +1,52 @@
+/* LibPSn00b Example Programs
+ * Part of the PSn00bSDK Project
+ *
+ * RGB24 Example by Lameguy64
+ *
+ *
+ * This example demonstrates the 24-bit color mode of the PS1. This mode is
+ * not practical for gameplay as the GPU can only draw graphics primitives
+ * in 16-bit color depth so this feature would normally be used only for
+ * fullscreen graphic illustrations or FMV sequences.
+ *
+ *
+ * Changelog:
+ *
+ * 05-03-2019 - Initial version.
+ *
+ */
+
+#include <stdio.h>
+#include <psxgte.h>
+#include <psxgpu.h>
+
+// So data from tim.s can be accessed
+extern unsigned int tim_image[];
+
+int main() {
+
+ DISPENV disp;
+ TIM_IMAGE tim;
+
+ // Reset GPU
+ ResetGraph(0);
+
+ // Setup 640x480 24-bit video mode
+ SetDefDispEnv(&disp, 0, 0, 640, 480);
+ disp.isrgb24 = 1;
+ disp.isinter = 1;
+
+ // Apply and enable display
+ PutDispEnv(&disp);
+ SetDispMask(1);
+
+ // Upload image to VRAM
+ GetTimInfo(tim_image, &tim);
+ LoadImage(tim.prect, tim.paddr);
+ DrawSync(0);
+
+ while(1) {
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/examples/graphics/rgb24/makefile b/examples/graphics/rgb24/makefile
new file mode 100644
index 0000000..61ef24f
--- /dev/null
+++ b/examples/graphics/rgb24/makefile
@@ -0,0 +1,41 @@
+include ../../sdk-common.mk
+
+TARGET = rgb24.elf
+
+CFILES = $(notdir $(wildcard *.c))
+CPPFILES = $(notdir $(wildcard *.cpp))
+AFILES = $(notdir $(wildcard *.s))
+
+OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o))
+
+INCLUDE +=
+LIBDIRS +=
+
+LIBS = -lpsxgpu -lpsxapi -lc
+
+CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections
+CPPFLAGS = $(CFLAGS) -fno-exceptions
+AFLAGS = -g -msoft-float
+LDFLAGS = -g -Ttext=0x80010000 -gc-sections -T $(GCC_BASE)/mipsel-unknown-elf/lib/ldscripts/elf32elmip.x
+
+CC = $(PREFIX)gcc
+CXX = $(PREFIX)g++
+AS = $(PREFIX)as
+LD = $(PREFIX)ld
+
+all: $(OFILES)
+ $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET)
+ elf2x -q $(TARGET)
+
+build/%.o: %.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.s
+ @mkdir -p $(dir $@)
+ $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.tim
+
+clean:
+ rm -rf build $(TARGET) $(TARGET:.elf=.exe)
diff --git a/examples/graphics/rgb24/tim.s b/examples/graphics/rgb24/tim.s
new file mode 100644
index 0000000..a4432d9
--- /dev/null
+++ b/examples/graphics/rgb24/tim.s
@@ -0,0 +1,7 @@
+.section .data
+
+.global tim_image
+.type tim_image, @object
+tim_image:
+ .incbin "bunpattern.tim"
+ \ No newline at end of file