diff --git a/mk64.ld b/mk64.ld index 70f4820..f36f11c 100644 --- a/mk64.ld +++ b/mk64.ld @@ -75,6 +75,7 @@ SECTIONS BUILD_DIR/src/audio/seqplayer.o(.text); BUILD_DIR/src/audio/external.o(.text); BUILD_DIR/src/audio/port_eu.o(.text); + BUILD_DIR/src/flycam.o(.text); #if DEBUG BUILD_DIR/src/os/osCartRomInit.o(.text); BUILD_DIR/src/debug/crash_screen_enhancement.o(.text); @@ -229,6 +230,7 @@ SECTIONS BUILD_DIR/src/audio/load.o(.data); BUILD_DIR/src/audio/playback.o(.data); BUILD_DIR/src/audio/effects.o(.data); + BUILD_DIR/src/flycam.o(.data); #if DEBUG BUILD_DIR/src/debug/crash_screen_enhancement.o(.data); BUILD_DIR/src/debug/debug.o(.data); @@ -277,6 +279,7 @@ SECTIONS BUILD_DIR/src/audio/seqplayer.o(.rodata); BUILD_DIR/src/audio/external.o(.rodata); BUILD_DIR/src/audio/port_eu.o(.rodata); + BUILD_DIR/src/flycam.o(.rodata); #if DEBUG BUILD_DIR/src/debug/crash_screen_enhancement.o(.rodata); BUILD_DIR/src/debug/debug.o(.rodata); @@ -311,6 +314,7 @@ SECTIONS BUILD_DIR/src/menu_items.jp.o(.bss); BUILD_DIR/src/code_800AF9B0.o(.bss); BUILD_DIR/src/menus.o(.bss); + BUILD_DIR/src/flycam.o(.bss); #if DEBUG BUILD_DIR/src/os/osCartRomInit.o(.bss); BUILD_DIR/src/debug/crash_screen_enhancement.o(.bss); diff --git a/src/camera.c b/src/camera.c index 02275b1..406739d 100644 --- a/src/camera.c +++ b/src/camera.c @@ -969,7 +969,9 @@ void func_8001EE98(Player *player, Camera *camera, s8 index) { func_8001E8E8(camera, player, index); break; } - func_8001E45C(camera, player, index); + + flycam(camera, player, index); + break; case 8: func_8001E0C4(camera, player, index); diff --git a/src/render_player.c b/src/render_player.c index 6b14e27..d10796b 100644 --- a/src/render_player.c +++ b/src/render_player.c @@ -65,6 +65,8 @@ void func_8001F9E4(Player *player, Camera *camera, s8 arg2) { } } +extern u32 isFlycam; + u16 check_player_camera_collision(Player *player, Camera *camera, f32 arg2, f32 arg3) { UNUSED f32 pad[6]; f32 sp64; @@ -79,6 +81,10 @@ u16 check_player_camera_collision(Player *player, Camera *camera, f32 arg2, f32 s16 var_v0; u16 ret; + if (isFlycam) { + return 1; + } + ret = 0; switch (gActiveScreenMode) { /* irregular */ case SCREEN_MODE_1P: diff --git a/src/flycam.c b/src/flycam.c new file mode 100644 index 0000000..5569838 --- /dev/null +++ b/src/flycam.c @@ -0,0 +1,328 @@ +#include +#include +#include ++#include +#include +#include "main.h" +#include +#include +#include "racing/collision.h" +#include +#include "player_controller.h" +#include "code_80057C60.h" + +// Yaw/pitch rotation sensitivity +#define SENSITIVITY_X 0.0003f +#define SENSITIVITY_Y 0.0003f + +u32 isFlycam = false; +u32 fRankIndex = 0; +u32 fTargetPlayer = false; +u32 fMode; // flycam mode should probably be an enum +u32 fModeInit = false; + +typedef struct { + Vec3f pos; + Vec3f lookAt; + Vec3s rot; +} FlycamSaveState; + +FlycamSaveState fState; + +void flycam_calculate_forward_vector(Camera* camera, Vec3f forwardVector); +void flycam_move_camera_forward(Camera* camera, struct Controller *controller, f32 distance); +void flycam_update(Camera* camera, struct Controller *controller); +void flycam_controller_manager(Camera *camera, struct Controller *controller, Player *player); +void flycam_target_player(Camera *camera, u32 playerIndex); +void flycam_move_camera_up(Camera* camera, struct Controller *controller, f32 distance); +void flycam_save_state(Camera *camera); +void flycam_load_state(Camera *camera); + + +/** + * Controls + * + * Forward: A + * Backward: B + * + * Go faster: Z + * + * Up: C-up + * Down: C-down + * + * Targets players based on rank position + * + * Target player: R-trig + * Target next player: C-right + * Target previous player: C-left + * + * Switch camera modes: D-pad left + * + * Camera mode 1: Enter flycam at the player's position + * Camera mode 2: Enter flycam at previous flycam spot + * +*/ + +void flycam(Camera *camera, Player *player, s8 index) { + struct Controller *controller = &gControllers[0]; + Vec3f forwardVector; + f32 dirX; + f32 dirY; + f32 dirZ; + f32 length; + + if (controller->buttonPressed & L_TRIG) { + isFlycam = !isFlycam; + + // Don't use `bool = !bool` here as the game code can swap these on you. + // Which will confuse the code. This forces it to always be correct + if (isFlycam) { + player->type |= PLAYER_CPU; + } else { + player->type &= PLAYER_CPU; + } + + gIsHUDVisible = !isFlycam; + + if (isFlycam) { + + if (fMode && fModeInit) { + flycam_load_state(camera); + } else { + // !fMode or fMode not initialized + flycam_target_player(camera, get_player_index_for_player(player)); + } + + return; + } else { + if(fMode) { + flycam_save_state(camera); + } + } + } + + // Driving mode + if (!isFlycam) { + // Use normal camera code + func_8001E45C(camera, &gPlayers[fRankIndex], index); + return; + } + + + //if (player == gPlayerOne) { return; } + + //player->type &= ~PLAYER_HUMAN; + //player->type |= PLAYER_HUMAN; + + if ((player->type & PLAYER_START_SEQUENCE)) { return; } + + + flycam_controller_manager(camera, controller, player); + + + + +} + +void flycam_save_state(Camera *camera) { + fState.pos[0] = camera->pos[0]; + fState.pos[1] = camera->pos[1]; + fState.pos[2] = camera->pos[2]; + + fState.lookAt[0] = camera->lookAt[0]; + fState.lookAt[1] = camera->lookAt[1]; + fState.lookAt[2] = camera->lookAt[2]; + + fState.rot[0] = camera->rot[0]; + fState.rot[1] = camera->rot[1]; + fState.rot[2] = camera->rot[2]; + fModeInit = true; +} + +void flycam_load_state(Camera *camera) { + camera->pos[0] = fState.pos[0]; + camera->pos[1] = fState.pos[1]; + camera->pos[2] = fState.pos[2]; + + camera->lookAt[0] = fState.lookAt[0]; + camera->lookAt[1] = fState.lookAt[1]; + camera->lookAt[2] = fState.lookAt[2]; + + camera->rot[0] = fState.rot[0]; + camera->rot[1] = fState.rot[1]; + camera->rot[2] = fState.rot[2]; +} + +void flycam_controller_manager(Camera *camera, struct Controller *controller, Player *player) { + + if (controller->buttonPressed & U_JPAD) { + fMode = !fMode; + } + + // Target a player + if (controller->buttonPressed & R_TRIG) { + fTargetPlayer = !fTargetPlayer; + } + + // Target next player + if (controller->buttonPressed & L_CBUTTONS) { + if (fRankIndex > 0) { + fRankIndex--; + camera->playerId = fRankIndex; + D_800DC5EC->player = &gPlayers[fRankIndex]; + } + } + + // Target previous player + if (controller->buttonPressed & R_CBUTTONS) { + if (fRankIndex < 7) { + fRankIndex++; + camera->playerId = fRankIndex; + D_800DC5EC->player = &gPlayers[fRankIndex]; + } + } + + // Target camera at chosen player + if (fTargetPlayer) { + flycam_target_player(camera, gGPCurrentRacePlayerIdByRank[fRankIndex]); + // Don't run the other camera code. + return; + } + + // Rotation + if (!fTargetPlayer) { + if (controller->stickDirection != 0) { + flycam_update(camera, controller); + } + } + + // Forward + if (controller->button & A_BUTTON) { + flycam_move_camera_forward(camera, controller, 3.0f); + } + + // Backward B button but not A button. + if (controller->button & B_BUTTON && !(controller->button & A_BUTTON)) { + flycam_move_camera_forward(camera, controller, -3.0f); + } + + // Up + if (controller->button & U_CBUTTONS) { + flycam_move_camera_up(camera, controller, 2.0f); + } + // Up + if (controller->button & D_CBUTTONS) { + flycam_move_camera_up(camera, controller, -2.0f); + } +} + +// Calculates the forward direction vector based on camera orientation +void flycam_calculate_forward_vector(Camera* camera, Vec3f forwardVector) { + f32 pitch = (camera->rot[2] / 65535.0f) * 360.0f; // Convert pitch from 0-65535 to degrees + f32 yaw = (camera->rot[1] / 65535.0f) * 360.0f; // Convert yaw from 0-65535 to degrees + + // Convert degrees to radians + pitch = pitch * M_PI / 180.0f; + yaw = yaw * M_PI / 180.0f; + + forwardVector[0] = -sinf(yaw) * cosf(pitch); + forwardVector[1] = -sinf(pitch); + forwardVector[2] = cosf(yaw) * cosf(pitch); +} + +// Function to move the camera forward +void flycam_move_camera_forward(Camera* camera, struct Controller *controller, f32 distance) { + Vec3f forwardVector; + Vec3f rightVector; + f32 length; + flycam_calculate_forward_vector(camera, forwardVector); + + if (controller->button & Z_TRIG) { + distance *= 3; + } + + // Normalize the forward vector + length = sqrtf(forwardVector[0] * forwardVector[0] + forwardVector[1] * forwardVector[1] + forwardVector[2] * forwardVector[2]); + forwardVector[0] /= length; + forwardVector[1] /= length; + forwardVector[2] /= length; + + // Calculate the right vector by taking the cross product of forward and up + rightVector[0] = forwardVector[1] * camera->up[2] - forwardVector[2] * camera->up[1]; + rightVector[1] = forwardVector[2] * camera->up[0] - forwardVector[0] * camera->up[2]; + rightVector[2] = forwardVector[0] * camera->up[1] - forwardVector[1] * camera->up[0]; + + // Move the camera's position along the forward vector while considering its up vector + camera->pos[0] += forwardVector[0] * distance; + camera->pos[1] += forwardVector[1] * distance; + camera->pos[2] += forwardVector[2] * distance; + + // Optionally, you can also adjust the lookAt point to maintain the same relative position + camera->lookAt[0] += forwardVector[0] * distance; + camera->lookAt[1] += forwardVector[1] * distance; + camera->lookAt[2] += forwardVector[2] * distance; +} + +// Function to move the camera forward +void flycam_move_camera_up(Camera* camera, struct Controller *controller, f32 distance) { + // Check if the Z button is pressed (for faster movement) + if (controller->button & Z_TRIG) { + distance *= 3; + } + + // Move the camera's position along its up vector (Y-axis) + camera->pos[1] += distance; + + // Optionally, adjust the lookAt point to maintain the same relative position + camera->lookAt[1] += distance; +} + +// Update camera rotation and lookAt point based on input +void flycam_update(Camera* camera, struct Controller *controller) { + // Calculate yaw (horizontal movement) + f32 yawChange = controller->rawStickX * SENSITIVITY_X; + f32 pitchChange = controller->rawStickY * SENSITIVITY_Y; + Vec3f forwardVector; + + check_bounding_collision(&camera->collision, 50, camera->pos[0], camera->pos[1], camera->pos[2]); + + camera->rot[1] += (short)(yawChange * 65535.0f / (2 * M_PI)); // Convert radians to 0-65535 range + + camera->rot[2] += (short)(-pitchChange * 65535.0f / (2 * M_PI)); // Convert radians to 0-65535 range + + if (camera->rot[2] > 15999) { + camera->rot[2] = 15999; + } else if (camera->rot[2] < -15999) { + camera->rot[2] = -15999; + } + + // Update the lookAt point based on the new orientation + flycam_calculate_forward_vector(camera, forwardVector); + camera->lookAt[0] = camera->pos[0] + forwardVector[0]; + camera->lookAt[1] = camera->pos[1] + forwardVector[1]; + camera->lookAt[2] = camera->pos[2] + forwardVector[2]; +} + +void flycam_target_player(Camera *camera, u32 playerIndex) { + Vec3f forwardVector;// = 2.0f; + Player *player = &gPlayers[playerIndex]; + + // Calculate the direction from the player to the camera + f32 dirX = player->pos[0] - camera->pos[0]; + f32 dirY = player->pos[1] - camera->pos[1]; + f32 dirZ = player->pos[2] - camera->pos[2]; + + // Normalize the direction vector (if needed) + f32 length = sqrtf(dirX * dirX + dirY * dirY + dirZ * dirZ); + if (length > 0) { + dirX /= length; + dirY /= length; + dirZ /= length; + } + + // Update the camera's look-at direction + camera->lookAt[0] = camera->pos[0] + dirX; + camera->lookAt[1] = camera->pos[1] + dirY; + camera->lookAt[2] = camera->pos[2] + dirZ; +} diff --git a/src/main.c b/src/main.c index 7c90951..135e98c 100644 --- a/src/main.c +++ b/src/main.c @@ -578,6 +578,7 @@ void race_logic_loop(void) { gTickSpeed = 2; staff_ghosts_loop(); if (gIsGamePaused == 0) { + func_8001EE98(gPlayerOneCopy, camera1, 0); for (i = 0; i < gTickSpeed; i++) { if (D_8015011E) { gCourseTimer += 0.01666666; // 1 / 60 @@ -585,7 +586,6 @@ void race_logic_loop(void) { func_802909F0(); evaluate_collision_for_players_and_actors(); func_800382DC(); - func_8001EE98(gPlayerOneCopy, camera1, 0); func_80028F70(); func_8028F474(); func_80059AC8(); diff --git a/src/racing/render_courses.c b/src/racing/render_courses.c index c2a84aa..338e2bd 100644 --- a/src/racing/render_courses.c +++ b/src/racing/render_courses.c @@ -180,6 +180,11 @@ void render_course_segments(uintptr_t addr, struct UnkStruct_800DC5EC *arg1) { arg1->pathCounter = temp_v1; temp_v1 = ((temp_v1 - 1) * 4) + var_a3; + if (isFlycam) { + render_course_credits(); + return; + } + gSPDisplayList(gDisplayListHead++, gfx[temp_v1]); }