[enhancement] Freecam (#56)

* Create noclip.c

* Create noclip.h

* Update camera.c

* Update render_courses.c

* Update ImguiUI.cpp

* Freecam Done

* Add smoothing to window

* Update freecam

* focus  player

* fix

* Update freecam.c

* Update freecam.h

* Update render_courses.c

* Update FreecamWindow.cpp

* Update Engine.cpp

* Move vars

---------

Co-authored-by: MegaMech <7255464+MegaMech@users.noreply.github.com>
This commit is contained in:
MegaMech
2024-09-10 16:19:19 -06:00
committed by GitHub
parent cf05266bf9
commit 47e95c0e18
15 changed files with 890 additions and 4 deletions
+3
View File
@@ -94,6 +94,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/src/audio
${CMAKE_CURRENT_SOURCE_DIR}/src/debug
${CMAKE_CURRENT_SOURCE_DIR}/src/networking
${CMAKE_CURRENT_SOURCE_DIR}/src/enhancements/freecam
)
# Collect source files to build the executable
@@ -133,6 +134,8 @@ file(GLOB_RECURSE ALL_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
"courses/*.h"
"src/networking/*.c"
"src/networking/*.h"
"src/enhancements/freecam/*.c"
"src/enhancements/freecam/*.h"
)
list(APPEND ALL_FILES ${ALL_FILES_ROOT})
+2 -1
View File
@@ -130,7 +130,7 @@ Gfx* koopa_troopa_beach_dls2[] = {
// 0x06014828
// animations? lights?
u32 d_course_koopa_troopa_beach_unk_data1[] = {
uintptr_t d_course_koopa_troopa_beach_unk_data1[] = {
0x00000000,
0x00000001,
0x00000000,
@@ -319,3 +319,4 @@ Animation* d_course_koopa_troopa_beach_unk_data5[] = {
u32 d_course_koopa_troopa_beach_unk_data6[] = {
0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
};
+6 -1
View File
@@ -16,6 +16,7 @@
#include "code_80005FD0.h"
#include "main.h"
#include "spawn_players.h"
#include "enhancements/freecam/freecam_engine.h"
f32 D_800DDB30[] = { 0.4f, 0.6f, 0.275f, 0.3f };
@@ -984,7 +985,11 @@ void func_8001EE98(Player* player, Camera* camera, s8 index) {
func_8001E8E8(camera, player, index);
break;
}
func_8001E45C(camera, player, index);
if (CVarGetInteger("gFreecam", 0) == 1) {
freecam(camera, player, index);
} else {
func_8001E45C(camera, player, index);
}
break;
case 8:
func_8001E0C4(camera, player, index);
+374
View File
@@ -0,0 +1,374 @@
#include <libultraship.h>
#include <macros.h>
#include <defines.h>
#include <camera.h>
#include "main.h"
#include <libc/math.h>
#include <common_structs.h>
#include "racing/collision.h"
#include "player_controller.h"
#include "code_80057C60.h"
#include "code_800029B0.h"
#include "code_80005FD0.h"
#include <SDL2/SDL.h>
#include "freecam_engine.h"
#include "n64_freecam.h"
typedef struct {
Vec3f pos;
Vec3f lookAt;
Vec3s rot;
} freecamSaveState;
freecamSaveState fState;
u32 fRankIndex = -1;
u32 fTargetPlayer = false;
u32 fMode; // freecam mode should probably be an enum
u32 fModeInit = false;
int rightMouseButtonDown = 0; // Track if right mouse button is held down
u32 gFreecamControllerType = 0;
// void freecam_n64_calculate_forward_vector(Camera* camera, Vec3f forwardVector);
// void freecam_n64_move_camera_forward(Camera* camera, struct Controller *controller, f32 distance);
// void freecam_calculate_forward_vector(Camera* camera, Vec3f forwardVector);
// void freecam_move_camera_forward(Camera* camera, f32 distance);
// void freecam_update(Camera* camera, struct Controller *controller);
// void freecam_n64_controller_manager(Camera *camera, struct Controller *controller, Player *player);
// void freecam_target_player(Camera *camera, u32 playerIndex);
// void freecam_move_camera_up(Camera* camera, struct Controller *controller, f32 distance);
void freecam_save_state(Camera *camera);
void freecam_load_state(Camera *camera);
void freecam_mouse_manager(Camera *camera);
void freecam_keyboard_manager(Camera* camera, Vec3f forwardVector);
/**
* 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 freecam at the player's position
* Camera mode 2: Enter freecam at previous freecam spot
*
*/
void freecam(Camera *camera, Player *player, s8 index) {
struct Controller *controller = &gControllers[0];
f32 dirX;
f32 dirY;
f32 dirZ;
f32 length;
if (controller->buttonPressed & L_TRIG) {
// Toggle freecam
CVarSetInteger("gFreecam", !CVarGetInteger("gFreecam", 0));
// 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 (CVarGetInteger("gFreecam", 0) == 1) {
player->type |= PLAYER_KART_AI;
} else {
player->type &= PLAYER_KART_AI;
}
gIsHUDVisible = !CVarGetInteger("gFreecam", 0);
if (CVarGetInteger("gFreecam", 0) == 1) {
if (fMode && fModeInit) {
freecam_load_state(camera);
} else {
// !fMode or fMode not initialized
freecam_target_player(camera, get_player_index_for_player(player));
}
return;
} else {
if(fMode) {
freecam_save_state(camera);
}
}
}
// Driving mode
if (CVarGetInteger("gFreecam", 0) == 0) {
// 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; }
// Mouse/Keyboard
if (gFreecamControllerType == 0) {
freecam_calculate_forward_vector_allow_rotation(camera, freeCam.forwardVector);
freecam_mouse_manager(camera, freeCam.forwardVector);
freecam_keyboard_manager(camera, freeCam.forwardVector);
} else { // Stock N64 controller
freecam_n64_controller_manager(camera, controller, player);
}
if (!fTargetPlayer) {
freecam_update(camera, freeCam.forwardVector);
}
}
void freecam_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 freecam_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];
}
f32 gFreecamRotateSmoothingFactor = 0.85f;
void freecam_mouse_manager(Camera* camera, Vec3f forwardVector) {
int mouseX, mouseY;
static int prevMouseX = 0, prevMouseY = 0;
Uint32 mouseState = SDL_GetRelativeMouseState(&mouseX, &mouseY);
mouseX = (mouseX + prevMouseX) / 2;
mouseY = (mouseY + prevMouseY) / 2;
prevMouseX = mouseX;
prevMouseY = mouseY;
if (mouseState & SDL_BUTTON(SDL_BUTTON_RIGHT)) {
// Calculate yaw (left/right) and pitch (up/down) changes
f32 yawChange = mouseX * MOUSE_SENSITIVITY_X;
f32 pitchChange = mouseY * MOUSE_SENSITIVITY_Y;
// Smoothly update yaw and pitch
camera->rot[1] += (short)(yawChange * 65535.0f / (2 * M_PI)); // Yaw (left/right)
camera->rot[2] += (short)(pitchChange * 65535.0f / (2 * M_PI)); // Pitch (up/down)
// Clamp pitch to avoid extreme values
if (camera->rot[2] > 15999) camera->rot[2] = 15999;
if (camera->rot[2] < -15999) camera->rot[2] = -15999;
// Calculate the forward vector based on the new yaw and pitch
//freecam_calculate_forward_vector_allow_rotation(camera, forwardVector);
// Smoothly interpolate the lookAt position
Vec3f targetLookAt = {
camera->pos[0] + forwardVector[0],
camera->pos[1] + forwardVector[1],
camera->pos[2] + forwardVector[2]
};
// Smoothing
camera->lookAt[0] += (targetLookAt[0] - camera->lookAt[0]) * gFreecamRotateSmoothingFactor;
camera->lookAt[1] += (targetLookAt[1] - camera->lookAt[1]) * gFreecamRotateSmoothingFactor;
camera->lookAt[2] += (targetLookAt[2] - camera->lookAt[2]) * gFreecamRotateSmoothingFactor;
}
}
f32 gFreecamSpeed = 3.0f;
f32 gFreecamSpeedMultiplier = 2.0f;
void freecam_keyboard_manager(Camera* camera, Vec3f forwardVector) {
const uint8_t* keystate = SDL_GetKeyboardState(NULL);
float moveSpeed = gFreecamSpeed;
// Fast movement with Ctrl
if (keystate[SDL_SCANCODE_LCTRL] || keystate[SDL_SCANCODE_RCTRL]) {
moveSpeed *= gFreecamSpeedMultiplier;
}
// Determine movement direction based on keys pressed
Vec3f totalMove = {0.0f, 0.0f, 0.0f};
// if (keystate[SDL_SCANCODE_F]) {
// fMode = true;
// }
// // Target a player
// if (keystate[SDL_SCANCODE_G]) {
// fTargetPlayer = false;
// }
// Target next player
if (keystate[SDL_SCANCODE_N]) {
if (fRankIndex > 0) {
fRankIndex--;
camera->playerId = fRankIndex;
D_800DC5EC->player = &gPlayers[fRankIndex];
}
}
// Target previous player
if (keystate[SDL_SCANCODE_M]) {
if (fRankIndex < 7) {
fRankIndex++;
camera->playerId = fRankIndex;
D_800DC5EC->player = &gPlayers[fRankIndex];
}
}
// Target camera at chosen player
if (fRankIndex != -1) {
freecam_target_player(camera, gGPCurrentRacePlayerIdByRank[fRankIndex]);
// Don't run the other camera code.
return;
}
if (keystate[SDL_SCANCODE_W]) {
totalMove[0] += forwardVector[0] * moveSpeed;
totalMove[2] += forwardVector[2] * moveSpeed;
}
if (keystate[SDL_SCANCODE_S]) {
totalMove[0] -= forwardVector[0] * moveSpeed;
totalMove[2] -= forwardVector[2] * moveSpeed;
}
if (keystate[SDL_SCANCODE_D]) {
totalMove[0] -= forwardVector[2] * moveSpeed; // Pan right
totalMove[2] += forwardVector[0] * moveSpeed;
}
if (keystate[SDL_SCANCODE_A]) {
totalMove[0] += forwardVector[2] * moveSpeed; // Pan left
totalMove[2] -= forwardVector[0] * moveSpeed;
}
if (keystate[SDL_SCANCODE_SPACE]) {
totalMove[1] += moveSpeed; // Move up
}
if (keystate[SDL_SCANCODE_LSHIFT] || keystate[SDL_SCANCODE_RSHIFT]) {
totalMove[1] -= moveSpeed; // Move down
}
freeCam.velocity[0] += totalMove[0];
freeCam.velocity[1] += totalMove[1];
freeCam.velocity[2] += totalMove[2];
// Update camera's lookAt position
camera->lookAt[0] = camera->pos[0] + forwardVector[0];
camera->lookAt[2] = camera->pos[2] + forwardVector[2];
}
// Control the camera using the n64 controller
void freecam_n64_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) {
freecam_target_player(camera, gGPCurrentRacePlayerIdByRank[fRankIndex]);
// Don't run the other camera code.
return;
}
// Rotation
if (!fTargetPlayer) {
if (controller->stickDirection != 0) {
freecam_n64_update(camera, controller);
}
}
// Forward
if (controller->button & A_BUTTON) {
freecam_n64_move_camera_forward(camera, controller, 3.0f);
}
// Backward B button but not A button.
if (controller->button & B_BUTTON && !(controller->button & A_BUTTON)) {
freecam_n64_move_camera_forward(camera, controller, -3.0f);
}
// Up
if (controller->button & U_CBUTTONS) {
freecam_n64_move_camera_up(camera, controller, 2.0f);
}
// Up
if (controller->button & D_CBUTTONS) {
freecam_n64_move_camera_up(camera, controller, -2.0f);
}
}
void freecam_render_setup(void) {
u16 perspNorm;
Mat4 matrix;
init_rdp();
func_802A53A4();
init_rdp();
func_80057FC4(0);
gSPSetGeometryMode(gDisplayListHead++, G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH);
gSPClearGeometryMode(gDisplayListHead++, G_CULL_BACK | G_CULL_BOTH | G_CULL_FRONT);
guPerspective(&gGfxPool->mtxPersp[0], &perspNorm, gCameraZoom[0], gScreenAspect, D_80150150, D_8015014C, 1.0f);
gSPPerspNormalize(gDisplayListHead++, perspNorm);
gSPMatrix(gDisplayListHead++, (&gGfxPool->mtxPersp[0]), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION);
guLookAt(&gGfxPool->mtxLookAt[0], camera1->pos[0], camera1->pos[1], camera1->pos[2], camera1->lookAt[0], camera1->lookAt[1], camera1->lookAt[2], camera1->up[0], camera1->up[1], camera1->up[2]);
gSPMatrix(gDisplayListHead++, (&gGfxPool->mtxLookAt[0]), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION);
mtxf_identity(matrix);
gSPSetGeometryMode(gDisplayListHead++, G_CULL_BACK);
render_set_position(matrix, 0);
init_rdp();
}
+24
View File
@@ -0,0 +1,24 @@
#ifndef FREECAM_H
#define FREECAM_H
#ifdef __cplusplus
extern "C" {
#endif
#include <libultraship.h>
#include "camera.h"
void freecam(Camera*, Player*, s8);
void freecam_render_setup(void);
extern f32 gFreecamSpeed;
extern f32 gFreecamSpeedMultiplier;
extern f32 gFreecamRotateSmoothingFactor;
extern u32 fRankIndex;
extern u32 gFreecamControllerType;
#ifdef __cplusplus
}
#endif
#endif // FREECAM_H
+149
View File
@@ -0,0 +1,149 @@
#include <libultraship.h>
#include <macros.h>
#include <defines.h>
#include <common_structs.h>
#include "player_controller.h"
#include "code_80057C60.h"
#include "code_800029B0.h"
#include "code_80005FD0.h"
#include "racing/collision.h"
#include "main.h"
#include <camera.h>
#include "freecam_engine.h"
FreeCam freeCam;
#include <math.h>
f32 gDampValue = 0.99f;
void freecam_update(Camera *camera, Vec3f forwardVector) {
// Update FreeCam state
camera->pos[0] += freeCam.velocity[0] * gDeltaTime;
camera->pos[1] += freeCam.velocity[1] * gDeltaTime;
camera->pos[2] += freeCam.velocity[2] * gDeltaTime;
// Apply damping to velocity
freeCam.velocity[0] *= gDampValue;
freeCam.velocity[1] *= gDampValue;
freeCam.velocity[2] *= gDampValue;
camera->lookAt[0] = camera->pos[0] + forwardVector[0];
camera->lookAt[1] = camera->pos[1] + forwardVector[1];
camera->lookAt[2] = camera->pos[2] + forwardVector[2];
}
// Function to move the camera forward or backward without changing its height
void freecam_move_camera_forward(Camera *camera, f32 moveSpeed, Vec3f forwardVector) {
// Calculate the forward vector
forwardVector[0] = camera->lookAt[0] - camera->pos[0];
//forwardVector[1] = 0; // Don't change the height
forwardVector[2] = camera->lookAt[2] - camera->pos[2];
// Normalize the forward vector
f32 length = sqrtf(forwardVector[0] * forwardVector[0] + forwardVector[2] * forwardVector[2]);
if (length != 0) {
forwardVector[0] /= length;
forwardVector[2] /= length;
}
// Update FreeCam velocity
freeCam.velocity[0] = forwardVector[0] * moveSpeed;
freeCam.velocity[2] = forwardVector[2] * moveSpeed;
camera->lookAt[0] = camera->pos[0] + forwardVector[0];
camera->lookAt[2] = camera->pos[0] + forwardVector[2];
}
void freecam_pan_camera(Camera *camera, f32 moveSpeed) {
Vec3f rightVector;
f32 length;
// Calculate the right vector for pan movement
rightVector[0] = camera->lookAt[2] - camera->pos[2];
// rightVector[1] = 0; // Maintain the same height
rightVector[2] = -(camera->lookAt[0] - camera->pos[0]);
// Normalize the right vector
length = sqrtf(rightVector[0] * rightVector[0] + rightVector[2] * rightVector[2]);
if (length != 0) {
rightVector[0] /= length;
rightVector[2] /= length;
}
// Update camera's position
freeCam.velocity[0] = rightVector[0] * moveSpeed;
freeCam.velocity[2] = rightVector[2] * moveSpeed;
}
void freecam_calculate_forward_vector_allow_rotation(Camera* camera, Vec3f forwardVector) {
// Convert yaw from 0-65535 to degrees
f32 pitch = (camera->rot[2] / 65535.0f) * 360.0f;
f32 yaw = (camera->rot[1] / 65535.0f) * 360.0f;
// Convert degrees to radians
pitch = pitch * M_PI / 180.0f;
yaw = yaw * M_PI / 180.0f;
// Calculate the forward vector based on yaw, ignoring pitch to keep height unchanged
forwardVector[0] = -sinf(yaw);
forwardVector[1] = -sinf(pitch); // Keep the height unchanged
forwardVector[2] = cosf(yaw);
}
// Calculates the forward direction vector based on camera orientation
void freecam_calculate_forward_vector(Camera *camera, Vec3f forwardVector) {
// Convert yaw from 0-65535 to degrees
f32 yaw = (camera->rot[0] / 65535.0f) * 360.0f;
f32 pitch = (camera->rot[2] / 65535.0f) * 360.0f;
// Convert degrees to radians
yaw = yaw * M_PI / 180.0f;
pitch = pitch * M_PI / 180.0f;
// Calculate the forward vector based on yaw and pitch
forwardVector[0] = -sinf(yaw) * cosf(pitch); // x-component
forwardVector[1] = sinf(pitch); // y-component (vertical)
forwardVector[2] = cosf(yaw) * cosf(pitch); // z-component
}
// Function to move the camera forward
void freecam_move_camera_up(Camera* camera, f32 distance) {
// Update the velocity of the camera in the Y direction
freeCam.velocity[1] += distance;
camera->lookAt[1] += distance;
}
void freecam_move_camera_down(Camera* camera, f32 distance) {
// Update the velocity of the camera in the Y direction
freeCam.velocity[1] += distance;
camera->lookAt[1] += distance;
}
void freecam_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;
}
+39
View File
@@ -0,0 +1,39 @@
#ifndef FREE_CAM_ENGINE_H
#define FREE_CAM_ENGINE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <libultraship.h>
#include "camera.h"
// Yaw/pitch rotation sensitivity
#define MOUSE_SENSITIVITY_X 0.006f
#define MOUSE_SENSITIVITY_Y 0.006f
typedef struct {
Vec3f velocity;
Vec3f forwardVector;
} FreeCam;
extern FreeCam freeCam;
extern f32 gDampValue;
//void update_camera(Camera *camera, f32 deltaTime);
void freecam_pan_camera(Camera *camera, f32 moveSpeed);
void freecam_calculate_forward_vector_allow_rotation(Camera* camera, Vec3f forwardVector);
void freecam_calculate_forward_vector(Camera* camera, Vec3f forwardVector);
void freecam_move_camera_forward(Camera* camera, f32 distance, Vec3f forwardVector);
void freecam_target_player(Camera *camera, u32 playerIndex);
void freecam_update(Camera* camera, Vec3f forwardVector);
void freecam_move_camera_up(Camera* camera, f32);
void freecam_move_camera_down(Camera* camera, f32);
extern f32 gDampValue;
#ifdef __cplusplus
}
#endif
#endif // FREE_CAM_ENGINE_H
+99
View File
@@ -0,0 +1,99 @@
#include <libultraship.h>
#include <defines.h>
#include <macros.h>
#include "code_80057C60.h"
#include "code_800029B0.h"
#include "code_80005FD0.h"
#include "camera.h"
#include <common_structs.h>
#include "player_controller.h"
#define N64_SENSITIVITY_X 0.0003f
#define N64_SENSITIVITY_Y 0.0003f
// Calculates the forward direction vector based on camera orientation
void freecam_n64_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 freecam_n64_move_camera_forward(Camera* camera, struct Controller *controller, f32 distance) {
Vec3f forwardVector;
Vec3f rightVector;
f32 length;
freecam_n64_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 freecam_n64_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 freecam_n64_update(Camera* camera, struct Controller *controller) {
// Calculate yaw (horizontal movement)
f32 yawChange = controller->rawStickX * N64_SENSITIVITY_X;
f32 pitchChange = controller->rawStickY * N64_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
freecam_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];
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef N64_FREECAM_H
#define N64_FREECAM_H
#include <libultraship.h>
void freecam_n64_move_camera_up(Camera* camera, struct Controller *controller, f32 distance);
void freecam_n64_controller_manager(Camera *camera, struct Controller *controller, Player *player);
void freecam_n64_calculate_forward_vector(Camera* camera, Vec3f forwardVector);
void freecam_n64_move_camera_forward(Camera* camera, struct Controller *controller, f32 distance);
void freecam_n64_update(Camera* camera, struct Controller *controller);
#endif // N64_FREECAM_H
+31
View File
@@ -43,6 +43,7 @@
#include "crash_screen.h"
#include "buffers/gfx_output_buffer.h"
#include <bridge/gfxdebuggerbridge.h>
#include "enhancements/freecam/freecam.h"
// Declarations (not in this file)
void func_80091B78(void);
@@ -317,6 +318,29 @@ void create_gfx_task_structure(void) {
#endif
}
f32 gDeltaTime = 0.0f;
f32 calculate_delta_time(void) {
static u32 prevtime = 0;
u32 now = osGetCount();
f32 deltaTime;
if (now > prevtime) {
deltaTime = (f32)(now - prevtime) / OS_CPU_COUNTER;
} else {
// Handle counter reset
deltaTime = (f32)((0xffffffff - prevtime) + 1 + now) / OS_CPU_COUNTER;
}
prevtime = now;
// Cap deltaTime to avoid large jumps (e.g., on pause/resume)
if (deltaTime > 1.0f / 15.0f) { // Assume 15 FPS is the lowest acceptable rate
deltaTime = 1.0f / 15.0f;
}
gDeltaTime = deltaTime;
}
void init_controllers(void) {
osCreateMesgQueue(&gSIEventMesgQueue, &gSIEventMesgBuf[0], ARRAY_COUNT(gSIEventMesgBuf));
osSetEventMesg(OS_EVENT_SI, &gSIEventMesgQueue, OS_MESG_32(0x33333333));
@@ -336,6 +360,11 @@ void update_controller(s32 index) {
return;
}
// Prevents pause menu intereference while controlling flycam
if ((CVarGetInteger("gFreecam", 0) == 1) && (gFreecamControllerType == 0) && (gGamestate == RACING)) {
return;
}
controller->rawStickX = gControllerPads[index].stick_x;
controller->rawStickY = gControllerPads[index].stick_y;
@@ -1226,6 +1255,8 @@ void thread5_game_loop(void) {
}
void thread5_iteration(void) {
// func_800CB2C4();
calculate_delta_time();
#ifdef TARGET_N64
while (true) {
func_800CB2C4();
+1
View File
@@ -104,6 +104,7 @@ void update_gamestate(void);
void thread5_game_loop(void);
void thread5_iteration(void);
void thread4_audio(void*);
extern f32 gDeltaTime;
extern struct VblankHandler* gVblankHandler1;
extern struct VblankHandler* gVblankHandler2;
+109
View File
@@ -0,0 +1,109 @@
#include "FreecamWindow.h"
#include "UIWidgets.h"
#include "libultraship/src/Context.h"
#include <imgui.h>
#include <libultraship/libultraship.h>
#include "spdlog/spdlog.h"
#include <common_structs.h>
#include <defines.h>
#include "enhancements/freecam/freecam_engine.h"
#include "enhancements/freecam/freecam.h"
extern "C" {
// typedef struct {
// Vec3f pos;
// Vec3f velocity;
// Vec3f lookAt;
// Vec3f lookAtVelocity;
// } FreeCam;
extern f32 gDampValue;
extern f32 gFreecamSpeed;
extern f32 gFreecamSpeedMultiplier;
extern f32 gFreecamRotateSmoothingFactor;
extern char* D_800E76A8[];
extern u32 fRankIndex;
extern u32 gFreecamControllerType;
}
namespace Freecam {
FreecamWindow::~FreecamWindow() {
SPDLOG_TRACE("destruct freecam window");
}
void FreecamWindow::InitElement() {
}
static s32 sReadyUpBool = false;
float dampMin = 0.995;
float dampMax = 1.0f;
float minSpeed = 1.0;
float maxSpeed = 30.0f;
float minSpeedMultiplier = 1.5f;
float maxSpeedMultiplier = 15.0f;
float minFreecamRotateFactor = 0.0f;
float maxFreecamRotateFactor = 1.0f;
uint32_t focusPlayer;
void FreecamWindow::DrawElement() {
const float framerate = ImGui::GetIO().Framerate;
const float deltatime = ImGui::GetIO().DeltaTime;
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
ImGui::Text("Freecam: Mouse/Keyboard Requires OpenGL. DX is not supported");
const char* items[] = { "Mouse/Keyboard", "N64 Controls" };
static int current_item = 0;
if (ImGui::Combo("Dropdown", &current_item, items, IM_ARRAYSIZE(items))) {
gFreecamControllerType = current_item;
}
ImGui::Text("Move: W,A,S,D\nUp: Space, Down: Shift\nFaster: Ctrl\nLook: Right-mouse button\nTarget previous player: N, Target next player: M");
ImGui::Spacing();
UIWidgets::CVarCheckbox("Enable Flycam", "gFreecam", {
.tooltip = "Allows you to fly around the course"
});
ImGui::Spacing();
if (ImGui::SliderScalar("Camera Damping", ImGuiDataType_Float, &gDampValue, &dampMin, &dampMax, "%f")) {
};
if (ImGui::SliderScalar("Camera Speed", ImGuiDataType_Float, &gFreecamSpeed, &minSpeed, &maxSpeed, "%f")) {
};
if (ImGui::SliderScalar("Camera Speed Multiplier", ImGuiDataType_Float, &gFreecamSpeedMultiplier, &minSpeedMultiplier, &maxSpeedMultiplier, "%f")) {
};
if (ImGui::SliderScalar("Camera Rotation Smoothing", ImGuiDataType_Float, &gFreecamRotateSmoothingFactor, &minFreecamRotateFactor, &maxFreecamRotateFactor, "%f")) {
};
ImGui::Spacing();
ImGui::Text("Target Player");
if (ImGui::Button("None")) {
fRankIndex = -1;
}
for (size_t i = 0; i < NUM_PLAYERS; i++) {
if (D_800E76A8[i] == NULL || D_800E76A8[i] == "ーーーー") {
break;
}
ImGui::SameLine();
if (ImGui::Button(D_800E76A8[i])) {
fRankIndex = i;
}
}
ImGui::PopStyleColor();
}
void FreecamWindow::UpdateElement() {
}
} // namespace Freecam
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <libultraship/libultraship.h>
namespace Freecam {
class FreecamWindow : public Ship::GuiWindow {
public:
using Ship::GuiWindow::GuiWindow;
~FreecamWindow();
private:
void InitElement() override;
void DrawElement() override;
void UpdateElement() override;
};
} // namespace GameInfo
+15
View File
@@ -2,6 +2,7 @@
#include "UIWidgets.h"
#include "ResolutionEditor.h"
#include "GameInfoWindow.h"
#include "FreecamWindow.h"
#include <spdlog/spdlog.h>
#include <imgui.h>
@@ -22,6 +23,7 @@ std::shared_ptr<Ship::GuiWindow> mStatsWindow;
std::shared_ptr<Ship::GuiWindow> mInputEditorWindow;
std::shared_ptr<Ship::GuiWindow> mGfxDebuggerWindow;
std::shared_ptr<Ship::GuiWindow> mGameInfoWindow;
std::shared_ptr<Ship::GuiWindow> mFreecamWindow;
std::shared_ptr<AdvancedResolutionSettings::AdvancedResolutionSettingsWindow> mAdvancedResolutionSettingsWindow;
void SetupGuiElements() {
@@ -61,6 +63,14 @@ void SetupGuiElements() {
SPDLOG_ERROR("Could not find input GfxDebuggerWindow");
}
mFreecamWindow = gui->GetGuiWindow("FreecamWindow");
if (mFreecamWindow == nullptr) {
SPDLOG_ERROR("Could not find input FreecamWindow");
}
mFreecamWindow = std::make_shared<Freecam::FreecamWindow>("gFreecamEnabled", "Freecam");
gui->AddGuiWindow(mFreecamWindow);
mGameInfoWindow = std::make_shared<GameInfo::GameInfoWindow>("gGameInfoEnabled", "Game info");
gui->AddGuiWindow(mGameInfoWindow);
@@ -441,6 +451,11 @@ void DrawEnhancementsMenu() {
if (UIWidgets::BeginMenu("Enhancements")) {
if (UIWidgets::BeginMenu("Gameplay")) {
UIWidgets::WindowButton("Freecam", "gFreecam", GameUI::mFreecamWindow, {
.tooltip = "Allows you to fly around the course"
});
UIWidgets::CVarCheckbox(
"No Level of Detail (LOD)", "gDisableLod",
{ .tooltip = "Disable Level of Detail (LOD) to avoid models using lower poly versions at a distance" });
+9 -2
View File
@@ -1282,7 +1282,7 @@ void render_big_donut(struct UnkStruct_800DC5EC* arg0) {
gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH);
gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING);
func_802B5D64((uintptr_t) D_800DC610, D_802B87D4, 0, 1);
func_802B5D64(D_800DC610, D_802B87D4, 0, 1);
gSPTexture(gDisplayListHead++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON);
gSPSetGeometryMode(gDisplayListHead++, G_SHADING_SMOOTH);
@@ -1367,9 +1367,16 @@ void func_8029569C(void) {
}
}
void render_course(struct UnkStruct_800DC5EC* arg0) {
void render_course(struct UnkStruct_800DC5EC *arg0) {
func_802B5D64(D_800DC610, D_802B87D4, 0, 1);
if ((CVarGetInteger("gFreecam", 0) == 1) ) {
// Render credits courses
func_8029569C();
return;
}
if (creditsRenderMode) {
func_8029569C();
return;