commit ea44f4e99cacd87bdd9d3e48c3be7ff47ec5ffc5 Author: Nico de Poel Date: Thu Jan 12 12:26:48 2023 +0100 First import, with bits of code taken from the MDL viewer prototype, split up into organized units. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e524d79 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..84c4fc9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,25 @@ +# PSn00bSDK example CMake script +# (C) 2021 spicyjpeg - MPL licensed + +cmake_minimum_required(VERSION 3.20) + +project( + PSn00bSDK-ps1bsp + LANGUAGES C CXX ASM + VERSION 1.0.0 + DESCRIPTION "PS1 BSP Viewer" + HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" +) + +file(GLOB _sources *.c) +psn00bsdk_add_executable(ps1bsp STATIC ${_sources}) + +psn00bsdk_target_incbin(ps1bsp PRIVATE tim_e1m1 atlas-e1m1.tim) +psn00bsdk_target_incbin(ps1bsp PRIVATE tim_e2m2 atlas-e2m2.tim) + +psn00bsdk_add_cd_image( + iso # Target name + ps1bsp # Output file name (= ps1bsp.bin + ps1bsp.cue) + iso.xml # Path to config file + DEPENDS ps1bsp +) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..d08b334 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,23 @@ +{ + "version": 2, + "cmakeMinimumRequired": { + "major": 3, + "minor": 20, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "displayName": "Default configuration", + "description": "Use this preset to build the project using PSn00bSDK.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_TOOLCHAIN_FILE": "$env{PSN00BSDK_LIBS}/cmake/sdk.cmake", + "PSN00BSDK_TC": "", + "PSN00BSDK_TARGET": "mipsel-none-elf" + } + } + ] +} diff --git a/asset.c b/asset.c new file mode 100644 index 0000000..9d745e5 --- /dev/null +++ b/asset.c @@ -0,0 +1,29 @@ +#include "common.h" +#include "asset.h" + +void asset_loadTexture(const u_long* tim, ps1texture_t *texture) +{ + TIM_IMAGE* tparam; + GetTimInfo(tim, tparam); + + // This freezes up if the TIM image has weird dimensions + LoadImage(tparam->prect, tparam->paddr); + + // Upload CLUT for palettized images + if (tparam->mode & 0x8) + { + LoadImage(tparam->crect, tparam->caddr); + } + + DrawSync(0); + + if (texture != NULL) + { + texture->prect = *tparam->prect; + texture->crect = *tparam->crect; + texture->mode = tparam->mode; + + texture->uoffs = (texture->prect.x % 64) << (2 - (texture->mode & 0x3)); + texture->voffs = (texture->prect.y & 0xFF); + } +} diff --git a/asset.h b/asset.h new file mode 100644 index 0000000..fb2e8a8 --- /dev/null +++ b/asset.h @@ -0,0 +1,13 @@ +#ifndef __ASSET_H__ +#define __ASSET_H__ + +typedef struct +{ + RECT prect, crect; + int mode; + int uoffs, voffs; +} ps1texture_t; + +void asset_loadTexture(const u_long* tim, ps1texture_t *texture); + +#endif // __ASSET_H__ diff --git a/atlas-e1m1.tim b/atlas-e1m1.tim new file mode 100644 index 0000000..9497325 Binary files /dev/null and b/atlas-e1m1.tim differ diff --git a/atlas-e2m2.tim b/atlas-e2m2.tim new file mode 100644 index 0000000..e3be25b Binary files /dev/null and b/atlas-e2m2.tim differ diff --git a/common.h b/common.h new file mode 100644 index 0000000..5603df1 --- /dev/null +++ b/common.h @@ -0,0 +1,16 @@ +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include +#include +#include +#include +#include +#include + +#define RotMatrixQ RotMatrix // TODO: temporary hack to allow Quake-specific code without implementation + +extern VECTOR cam_pos; +extern SVECTOR cam_rot; + +#endif // __COMMON_H__ diff --git a/display.c b/display.c new file mode 100644 index 0000000..b690d49 --- /dev/null +++ b/display.c @@ -0,0 +1,131 @@ +#include "common.h" +#include "display.h" + +#include + +// Define display/draw environments for double buffering +static DISPENV disp[2]; +static DRAWENV draw[2]; +static int db; + +u_long ot[2][OTLEN]; // Ordering tables, two arrays for double buffering. These are basically the buckets for bucket sorting of polygons. + // You can also see them as "layers" in the modern 3D graphics sense, but they serve a more immediate role for polygon ordering. + // Layer 0 is free for overlays, as polygons at depth 0 will be clipped. +char primbuff[2][65536]; // Primitive buffer, just a raw buffer of bytes to use as a pool for primitives +char *nextpri; + +const MATRIX identity = { + ONE, 0, 0, + 0, ONE, 0, + 0, 0, ONE, + 0, 0, 0, +}; + +// Transform to change the coordinate system from PS1 Default (Y down, Z forward) to Quake (Z up, Y forward) +const MATRIX quake_swizzle = { + ONE, 0, 0, + 0, 0, -ONE, + 0, ONE, 0, + 0, 0, 0, +}; + +MATRIX light_cols = { + ONE, 0, 0, + ONE, 0, 0, + ONE, 0, 0 +}; + +MATRIX light_dirs = { + ONE, 0, 0, + 0, 0, 0, + 0, 0, 0 +}; + +// Scale X coordinates to correct the aspect ratio for the chosen resolution +VECTOR aspect_scale = { SCREENWIDTH * ONE / 320 , ONE, ONE }; + +void display_init() +{ + // This not only resets the GPU but it also installs the library's + // ISR subsystem to the kernel + ResetGraph(0); + + SetVideoMode(MODE_NTSC); + + // Define display environments, first on top and second on bottom + SetDefDispEnv(&disp[0], 0, 0, SCREENWIDTH, SCREENHEIGHT); + SetDefDispEnv(&disp[1], 0, SCREENHEIGHT, SCREENWIDTH, SCREENHEIGHT); + + // Define drawing environments, first on bottom and second on top + SetDefDrawEnv(&draw[0], 0, SCREENHEIGHT, SCREENWIDTH, SCREENHEIGHT); + SetDefDrawEnv(&draw[1], 0, 0, SCREENWIDTH, SCREENHEIGHT); + + // Set and enable clear color + setRGB0(&draw[0], 49, 77, 121); + setRGB0(&draw[1], 49, 77, 121); + draw[0].isbg = 1; + draw[0].dtd = 1; + draw[1].isbg = 1; + draw[1].dtd = 1; + + // Clear double buffer counter + db = 0; + + // Apply the GPU environments + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + // Load test font + FntLoad(960, 448); + + // Open up a test font text stream + FntOpen(0, 8, SCREENWIDTH, SCREENHEIGHT, 0, 200); + + // TODO: OT initialization + + // Initialize GTE + InitGeom(); + gte_SetGeomOffset(SCREENWIDTH >> 1, SCREENHEIGHT >> 1); + gte_SetGeomScreen(180); // Screen depth for FOV control. Determines the distance of the camera to the near plane. +} + +void display_start() +{ + ClearOTagR(ot[db], OTLEN); + + gte_SetBackColor(48, 48, 48); // Ambient light color + gte_SetColorMatrix(&light_cols); // Light color (up to three different lights) + + MATRIX proj_matrix = quake_swizzle; // Swizzle coordinates so that everything is in Quake coordinate system (Z up) + ScaleMatrixL(&proj_matrix, &aspect_scale); // Apply aspect ratio correction for the current resolution + + MATRIX view_matrix; + SVECTOR trot = { -cam_rot.vx, -cam_rot.vy, -cam_rot.vz, 0 }; // Inverse camera rotation (in Quake coordinates) + VECTOR tpos = { -cam_pos.vx, -cam_pos.vy, -cam_pos.vz }; // Inverse camera position (in Quake coordinates) + RotMatrixQ(&trot, &view_matrix); // Set camera rotation part of the view matrix + ApplyMatrixLV(&view_matrix, &tpos, &tpos); // Apply camera rotation to camera position + TransMatrix(&view_matrix, &tpos); // Apply transformed position to the translation part of the view matrix + + // Compose view and projection matrices to obtain a combined view-projection matrix + CompMatrixLV(&proj_matrix, &view_matrix, &view_matrix); +} + +void display_finish() +{ + // Flip buffer index + db = !db; + + // Wait for all drawing to complete + DrawSync(0); + + // Wait for vertical sync to cap the logic to 60fps (or 50 in PAL mode) + // and prevent screen tearing + VSync(0); + + // Switch pages + PutDispEnv(&disp[db]); + PutDrawEnv(&draw[db]); + + // Enable display output, ResetGraph() disables it by default + SetDispMask(1); +} diff --git a/display.h b/display.h new file mode 100644 index 0000000..15d7f21 --- /dev/null +++ b/display.h @@ -0,0 +1,13 @@ +#ifndef __DISPLAY_H__ +#define __DISPLAY_H__ + +#define SCREENWIDTH 512 +#define SCREENHEIGHT 240 + +#define OTLEN 1024 + +void display_init(); +void display_start(); +void display_finish(); + +#endif // __DISPLAY_H__ diff --git a/input.c b/input.c new file mode 100644 index 0000000..903b01d --- /dev/null +++ b/input.c @@ -0,0 +1,124 @@ +#include "common.h" +#include "input.h" + +#include + +static u_char padBuffer[2][34]; + +static const int moveSpeed = 32; +static const short rotSpeed = 32; +static const char deadZone = 0x30; + +void input_init() +{ + InitPAD(padBuffer[0], 34, padBuffer[1], 34); + padBuffer[0][0] = padBuffer[0][1] = 0xFF; + padBuffer[1][0] = padBuffer[1][1] = 0xFF; + StartPAD(); + ChangeClearPAD(1); // Avoid issues with VSync timeouts caused by StartPAD +} + +void input_process() +{ + // Check if we have a connected controller on port 1 + if (padBuffer[0][0] != 0) + return; + + // Determine direction vectors of the camera + MATRIX cam_mtx; + RotMatrixQ(&cam_rot, &cam_mtx); + + u_short buttons = *((u_short*)(padBuffer[0]+2)); + if (!(buttons & PAD_UP)) + { + // Move forward (+Y) + cam_pos.vx += (cam_mtx.m[0][1] * moveSpeed) >> 12; + cam_pos.vy += (cam_mtx.m[1][1] * moveSpeed) >> 12; + cam_pos.vz += (cam_mtx.m[2][1] * moveSpeed) >> 12; + } + if (!(buttons & PAD_DOWN)) + { + // Move backward (-Y) + cam_pos.vx -= (cam_mtx.m[0][1] * moveSpeed) >> 12; + cam_pos.vy -= (cam_mtx.m[1][1] * moveSpeed) >> 12; + cam_pos.vz -= (cam_mtx.m[2][1] * moveSpeed) >> 12; + } + if (!(buttons & PAD_LEFT)) + { + // Rotate left + cam_rot.vz += rotSpeed; + } + if (!(buttons & PAD_RIGHT)) + { + // Rotate right + cam_rot.vz -= rotSpeed; + } + if (!(buttons & PAD_TRIANGLE)) + { + // Move up (+Z) + cam_pos.vx += (cam_mtx.m[0][2] * moveSpeed) >> 12; + cam_pos.vy += (cam_mtx.m[1][2] * moveSpeed) >> 12; + cam_pos.vz += (cam_mtx.m[2][2] * moveSpeed) >> 12; + } + if (!(buttons & PAD_CROSS)) + { + // Move down (-Z) + cam_pos.vx -= (cam_mtx.m[0][2] * moveSpeed) >> 12; + cam_pos.vy -= (cam_mtx.m[1][2] * moveSpeed) >> 12; + cam_pos.vz -= (cam_mtx.m[2][2] * moveSpeed) >> 12; + } + if (!(buttons & PAD_L1)) + { + // Strafe left (-X) + cam_pos.vx -= (cam_mtx.m[0][0] * moveSpeed) >> 12; + cam_pos.vy -= (cam_mtx.m[1][0] * moveSpeed) >> 12; + cam_pos.vz -= (cam_mtx.m[2][0] * moveSpeed) >> 12; + } + if (!(buttons & PAD_R1)) + { + // Strafe right (+X) + cam_pos.vx += (cam_mtx.m[0][0] * moveSpeed) >> 12; + cam_pos.vy += (cam_mtx.m[1][0] * moveSpeed) >> 12; + cam_pos.vz += (cam_mtx.m[2][0] * moveSpeed) >> 12; + } + if (!(buttons & PAD_L2)) + { + // Look down + cam_rot.vx -= rotSpeed; + } + if (!(buttons & PAD_R2)) + { + // Look up + cam_rot.vx += rotSpeed; + } + + // Check for analog controller + u_char padType = padBuffer[0][1] >> 4; + if (padType == 0x5 || padType == 0x7) + { + short rightJoyX = (short)(padBuffer[0][4]) - 0x80; + if (rightJoyX > -deadZone && rightJoyX < deadZone) rightJoyX = 0; + short rightJoyY = (short)(padBuffer[0][5]) - 0x80; + if (rightJoyY > -deadZone && rightJoyY < deadZone) rightJoyY = 0; + short leftJoyX = (short)(padBuffer[0][6]) - 0x80; + if (leftJoyX > -deadZone && leftJoyX < deadZone) leftJoyX = 0; + short leftJoyY = (short)(padBuffer[0][7]) - 0x80; + if (leftJoyY > -deadZone && leftJoyY < deadZone) leftJoyY = 0; + + // Move forward/backward + cam_pos.vx -= (cam_mtx.m[0][1] * leftJoyY * moveSpeed) >> 19; + cam_pos.vy -= (cam_mtx.m[1][1] * leftJoyY * moveSpeed) >> 19; + cam_pos.vz -= (cam_mtx.m[2][1] * leftJoyY * moveSpeed) >> 19; + + // Strafe left/right + cam_pos.vx += (cam_mtx.m[0][0] * leftJoyX * moveSpeed) >> 19; + cam_pos.vy += (cam_mtx.m[1][0] * leftJoyX * moveSpeed) >> 19; + cam_pos.vz += (cam_mtx.m[2][0] * leftJoyX * moveSpeed) >> 19; + + // Rotate left/right + cam_rot.vz -= (rightJoyX * rotSpeed) >> 7; + + // Look up/down + cam_rot.vx -= (rightJoyY * rotSpeed) >> 7; + } +} diff --git a/input.h b/input.h new file mode 100644 index 0000000..b64c2eb --- /dev/null +++ b/input.h @@ -0,0 +1,7 @@ +#ifndef __INPUT_H__ +#define __INPUT_H__ + +void input_init(); +void input_process(); + +#endif // __INPUT_H__ diff --git a/iso.xml b/iso.xml new file mode 100644 index 0000000..74b3b61 --- /dev/null +++ b/iso.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + diff --git a/main.c b/main.c new file mode 100644 index 0000000..f15b675 --- /dev/null +++ b/main.c @@ -0,0 +1,54 @@ +#include "common.h" +#include "input.h" +#include "display.h" +#include "time.h" +#include "asset.h" + +extern u_long tim_e1m1[]; +extern u_long tim_e2m2[]; + +VECTOR cam_pos = { 0, -400, 100 }; +SVECTOR cam_rot = { 0 }; + +// BSP face rendering: +// - Gather vertex data from face start index + length +// - Store vertex data in scratchpad memory (should be plenty of space for one face) +// - Perform camera offset and re-scaling operations to fit vertex data in signed 16-bit fixed point vectors +// - Note: may be possible to (ab)use 32-bit GTE registers & ops to do this calculation and downcast +// - Store rescaled vertex data in scratchpad memory + +// Init function +void init(void) +{ + time_init(); + input_init(); + display_init(); + + asset_loadTexture(tim_e1m1, NULL); + asset_loadTexture(tim_e2m2, NULL); +} + +// Main function, program entrypoint +int main(int argc, const char *argv[]) +{ + // Init stuff + init(); + + // Main loop + while(1) + { + input_process(); + + display_start(); + + // Draw stuff + FntPrint(-1, "Camera pos = (%d, %d, %d) rot = (%d, %d, %d)\n", cam_pos.vx, cam_pos.vy, cam_pos.vz, cam_rot.vx, cam_rot.vy, cam_rot.vz); + FntFlush(-1); + + display_finish(); + + time_tick(); + } + + return 0; +} diff --git a/system.cnf b/system.cnf new file mode 100644 index 0000000..7013dba --- /dev/null +++ b/system.cnf @@ -0,0 +1,4 @@ +BOOT=cdrom:\ps1bsp.exe;1 +TCB=4 +EVENT=10 +STACK=801FFFF0 diff --git a/time.c b/time.c new file mode 100644 index 0000000..a3aebcb --- /dev/null +++ b/time.c @@ -0,0 +1,19 @@ +#include "common.h" +#include "time.h" + +static int frameNumber; + +void time_init() +{ + frameNumber = 0; +} + +void time_tick() +{ + ++frameNumber; +} + +int time_getFrameNumber() +{ + return frameNumber; +} diff --git a/time.h b/time.h new file mode 100644 index 0000000..98a15b1 --- /dev/null +++ b/time.h @@ -0,0 +1,9 @@ +#ifndef __TIME_H__ +#define __TIME_H__ + +void time_init(); +void time_tick(); + +int time_getFrameNumber(); + +#endif // __TIME_H__