summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/common_structs.h2
-rw-r--r--src/camera.c6
-rw-r--r--src/enhancements/freecam/freecam.cpp160
-rw-r--r--src/enhancements/freecam/freecam.h7
-rw-r--r--src/enhancements/freecam/freecam_engine.c18
-rw-r--r--src/enhancements/freecam/freecam_engine.h4
-rw-r--r--src/main.c7
-rw-r--r--src/port/ui/FreecamWindow.cpp15
8 files changed, 133 insertions, 86 deletions
diff --git a/include/common_structs.h b/include/common_structs.h
index 8ae06ea1e..b5efa387a 100644
--- a/include/common_structs.h
+++ b/include/common_structs.h
@@ -66,6 +66,8 @@ struct D_80150158 {
struct Controller {
s16 rawStickX;
s16 rawStickY;
+ s16 rightRawStickX;
+ s16 rightRawStickY;
u16 button; // HeldButton
u16 buttonPressed; // OnTriggered
u16 buttonDepressed; // OffTriggered
diff --git a/src/camera.c b/src/camera.c
index 5410b33fc..953f1f96b 100644
--- a/src/camera.c
+++ b/src/camera.c
@@ -992,11 +992,7 @@ void func_8001EE98(Player* player, Camera* camera, s8 index) {
func_8001E8E8(camera, player, index);
break;
}
- if (CVarGetInteger("gFreecam", 0) == 1) {
- freecam(camera, player, index);
- } else {
- func_8001E45C(camera, player, index);
- }
+ freecam(camera, player, index); // Runs func_8001E45C when freecam is disabled
break;
case 8:
func_8001E0C4(camera, player, index);
diff --git a/src/enhancements/freecam/freecam.cpp b/src/enhancements/freecam/freecam.cpp
index 00d8ee84a..dd92d9c2a 100644
--- a/src/enhancements/freecam/freecam.cpp
+++ b/src/enhancements/freecam/freecam.cpp
@@ -33,6 +33,8 @@ typedef struct {
freecamSaveState fState;
+Controller fController;
+
u32 fRankIndex = 0;
u32 fTargetPlayer = false;
u32 fMode; // freecam mode should probably be an enum
@@ -40,7 +42,7 @@ u32 fModeInit = false;
int rightMouseButtonDown = 0; // Track if right mouse button is held down
-u32 gFreecamControllerType = 0;
+u32 bFreecamUseController = false;
/**
* Controls
@@ -66,25 +68,43 @@ u32 gFreecamControllerType = 0;
*
*/
void freecam(Camera* camera, Player* player, s8 index) {
- struct Controller* controller = &gControllers[0];
f32 dirX;
f32 dirY;
f32 dirZ;
f32 length;
+ static bool enabled = false; // Tracks previous activation state
+ bool freecamEnabled = CVarGetInteger("gFreecam", 0);
+
+ if (freecamEnabled && !enabled) {
+ enabled = true; // Mark that freecam was activated
+ on_freecam();
+ } else if (!freecamEnabled && enabled) {
+ off_freecam();
+ enabled = false; // Reset when freecam is turned off
+ }
- if (controller->buttonPressed & L_TRIG) {
- // Toggle freecam
- CVarSetInteger("gFreecam", !CVarGetInteger("gFreecam", 0));
+ // Freecam mode is enabled
+ if (enabled) {
+ freecam_loop(camera, player, index);
+ } else {
+ func_8001E45C(camera, player, index);
+ }
+}
- // 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;
- }
+void on_freecam(void) {
+ gIsHUDVisible = false;
+ gPlayerOne->type |= PLAYER_KART_AI;
+}
+
+void off_freecam(void) {
+ gIsHUDVisible = true;
+ gPlayerOne->type &= ~PLAYER_KART_AI;
+}
- gIsHUDVisible = !CVarGetInteger("gFreecam", 0);
+void freecam_loop(Camera* camera, Player* player, s8 index) {
+ if ((fController.buttonPressed & L_TRIG) && (fController.buttonPressed & R_TRIG)) {
+ // Toggle freecam
+ CVarSetInteger("gFreecam", !CVarGetInteger("gFreecam", 0));
}
// Calculate forward direction
@@ -104,47 +124,39 @@ void freecam(Camera* camera, Player* player, s8 index) {
freecam_tick(camera, freeCam.forwardVector);
}
-f32 gFreecamRotateSmoothingFactor = 0.85f;
-
void freecam_mouse_manager(Camera* camera, Vec3f forwardVector) {
- static int prevMouseX = 0, prevMouseY = 0;
auto wnd = GameEngine::Instance->context->GetWindow();
Ship::Coords mouse = wnd->GetMouseDelta();
- // Uint32 mouseState = SDL_GetRelativeMouseState(&mouse.x, &mouse.y);
+ f32 yawChange = 0.0f;
+ f32 pitchChange = 0.0f;
- // printf("MOUSE %d %d\n", mouse.x, mouse.y);
+ if (bFreecamUseController) {
+ // Controller controls
+ f32 stickX = ((f32)fController.rightRawStickX);
+ f32 stickY = ((f32)fController.rightRawStickY);
- mouse.x = (mouse.x + prevMouseX) / 2;
- mouse.y = (mouse.y + prevMouseY) / 2;
- prevMouseX = mouse.x;
- prevMouseY = mouse.y;
-
- if (wnd->GetMouseState(Ship::LUS_MOUSE_BTN_RIGHT)) {
- // Calculate yaw (left/right) and pitch (up/down) changes
- f32 yawChange = mouse.x * MOUSE_SENSITIVITY_X;
- f32 pitchChange = mouse.y * MOUSE_SENSITIVITY_Y;
+ // Sensitivity multipliers
+ float controllerSensitivityX = 5.0f; // Adjust as needed
+ float controllerSensitivityY = 3.0f;
- // 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)
+ // Deadzone handling (ignore tiny stick movements)
+ const float deadzone = 0.1f;
+ if (fabs(stickX) < deadzone) stickX = 0.0f;
+ if (fabs(stickY) < deadzone) stickY = 0.0f;
- // Clamp pitch to avoid extreme values
- if (camera->rot[2] > 15999) {
- camera->rot[2] = 15999;
- }
- if (camera->rot[2] < -15999) {
- camera->rot[2] = -15999;
+ // Instead of adding, directly set rotation velocity (so holding gives a steady rotation)
+ freeCam.rotVelocity[1] += stickX * controllerSensitivityX; // Yaw (left/right)
+ freeCam.rotVelocity[2] += stickY * controllerSensitivityY; // Pitch (up/down)
+ } else { // Mouse controls
+ // Calculate yaw (left/right) and pitch (up/down) changes
+ if (wnd->GetMouseState(Ship::LUS_MOUSE_BTN_RIGHT)) {
+ yawChange = mouse.x * MOUSE_SENSITIVITY_X;
+ pitchChange = mouse.y * MOUSE_SENSITIVITY_Y;
}
-
- // 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;
+ // Update rotational velocity based on mouse movement
+ freeCam.rotVelocity[1] += yawChange * 65535.0f / (2 * M_PI); // Yaw (left/right)
+ freeCam.rotVelocity[2] += pitchChange * 65535.0f / (2 * M_PI); // Pitch (up/down)
}
}
@@ -186,7 +198,6 @@ bool FreecamKeyDown(int virtualKey) {
void freecam_keyboard_manager(Camera* camera, Vec3f forwardVector) {
auto wnd = GameEngine::Instance->context->GetWindow();
float moveSpeed = gFreecamSpeed;
- Controller* controller = &gControllers[0];
// Determine movement direction based on keys pressed
Vec3f totalMove = { 0.0f, 0.0f, 0.0f };
@@ -207,44 +218,42 @@ void freecam_keyboard_manager(Camera* camera, Vec3f forwardVector) {
// Use n64 controls for use with a controller
//! @todo configure this properly
- if (gFreecamControllerType == 1) {
- // Targeting /fMode is broken
- // if (controller->buttonPressed & U_JPAD) {
- // fMode = !fMode;
- // }
- // Target a player
- if (controller->buttonPressed & R_TRIG) {
- fTargetPlayer = !fTargetPlayer;
+ if (bFreecamUseController) {
+ if (fController.buttonDepressed & R_TRIG) {
+ fTargetPlayer = true;
+ }
+ if (fController.buttonDepressed & L_TRIG) {
+ fTargetPlayer = false;
}
- if (controller->buttonPressed & L_CBUTTONS) {
+ if (fController.buttonPressed & L_JPAD) {
TargetPreviousPlayer = true;
}
- if (controller->buttonPressed & R_CBUTTONS) {
+ if (fController.buttonPressed & R_JPAD) {
TargetNextPlayer = true;
}
- if (controller->button & A_BUTTON) {
+ if (fController.rawStickY > 10) {
Forward = true;
}
- if (controller->button & B_BUTTON) {
+ if (fController.rawStickY < -10) {
Backward = true;
}
- if (controller->button & L_JPAD) {
- PanLeft = true;
+ if (fController.rawStickX > 10) {
+ PanRight = true;
}
- if (controller->button & R_JPAD) {
+ if (fController.rawStickX < -10) {
PanLeft = true;
}
- if (controller->button & U_CBUTTONS) {
+ if (fController.button & B_BUTTON) {
Down = true;
}
- if (controller->button & U_CBUTTONS) {
+ if (fController.button & A_BUTTON) {
Up = true;
}
- // if (controller->button ??) {
- // FastMove = true;
- // }
- // Keyboard and mouse DX
+ if (fController.button & Z_TRIG) {
+ FastMove = true;
+ }
}
+ // Keyboard and mouse DX
#ifdef _WIN32
else if (wnd->GetWindowBackend() == Ship::WindowBackend::FAST3D_DXGI_DX11) {
if (FreecamKeyDown('F')) {
@@ -364,6 +373,23 @@ void freecam_keyboard_manager(Camera* camera, Vec3f forwardVector) {
freeCam.velocity[2] += totalMove[2];
}
+void freecam_update_controller(void) {
+ fController.rawStickX = gControllerPads[0].stick_x;
+ fController.rawStickY = gControllerPads[0].stick_y;
+
+ fController.rightRawStickX = gControllerPads[0].right_stick_x;
+ fController.rightRawStickY = gControllerPads[0].right_stick_y;
+
+ if ((gControllerPads[0].button & 4) != 0) {
+ gControllerPads[0].button |= Z_TRIG;
+ }
+ fController.buttonPressed = gControllerPads[0].button & (gControllerPads[0].button ^ fController.button);
+ fController.buttonDepressed = fController.button & (gControllerPads[0].button ^ fController.button);
+ fController.button = gControllerPads[0].button;
+
+ // Note that D Pad as stick code has been removed. So if it's needed, it needs to be put back in.
+}
+
void freecam_render_setup(void) {
u16 perspNorm;
Mat4 matrix;
diff --git a/src/enhancements/freecam/freecam.h b/src/enhancements/freecam/freecam.h
index e7a1a1ece..db55866ed 100644
--- a/src/enhancements/freecam/freecam.h
+++ b/src/enhancements/freecam/freecam.h
@@ -9,16 +9,19 @@ extern "C" {
#include "camera.h"
void freecam(Camera*, Player*, s8);
+void on_freecam(void);
+void off_freecam(void);
+void freecam_loop(Camera*, Player*, s8);
+void freecam_update_controller(void);
void freecam_render_setup(void);
void freecam_mouse_manager(Camera*, Vec3f);
void freecam_keyboard_manager(Camera*, Vec3f);
extern f32 gFreecamSpeed;
extern f32 gFreecamSpeedMultiplier;
-extern f32 gFreecamRotateSmoothingFactor;
extern f32 gFreecamRotateFollowFactor;
extern u32 fRankIndex;
-extern u32 gFreecamControllerType;
+extern u32 bFreecamUseController;
#ifdef __cplusplus
}
diff --git a/src/enhancements/freecam/freecam_engine.c b/src/enhancements/freecam/freecam_engine.c
index 02501ba1f..350ab192b 100644
--- a/src/enhancements/freecam/freecam_engine.c
+++ b/src/enhancements/freecam/freecam_engine.c
@@ -16,18 +16,32 @@ FreeCam freeCam;
#include <math.h>
f32 gDampValue = 0.99f;
+f32 gRotDampValue = 0.96f;
+// Update FreeCam state
void freecam_tick(Camera* camera, Vec3f forwardVector) {
- // Update FreeCam state
+
+ // Apply camera movement
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
+ // Damp the velocity back to zero over time (camera slowly comes to a stop)
freeCam.velocity[0] *= gDampValue;
freeCam.velocity[1] *= gDampValue;
freeCam.velocity[2] *= gDampValue;
+ // Apply camera rotation
+ camera->rot[0] += freeCam.rotVelocity[0] * gDeltaTime;
+ camera->rot[1] += freeCam.rotVelocity[1] * gDeltaTime;
+ camera->rot[2] += freeCam.rotVelocity[2] * gDeltaTime;
+
+ // Damp the velocity back to zero over time (camera rot slowly comes to a stop)
+ freeCam.rotVelocity[0] *= gRotDampValue;
+ freeCam.rotVelocity[1] *= gRotDampValue;
+ freeCam.rotVelocity[2] *= gRotDampValue;
+
+ // Update lookAt
camera->lookAt[0] = camera->pos[0] + forwardVector[0];
camera->lookAt[1] = camera->pos[1] + forwardVector[1];
camera->lookAt[2] = camera->pos[2] + forwardVector[2];
diff --git a/src/enhancements/freecam/freecam_engine.h b/src/enhancements/freecam/freecam_engine.h
index 7c7ecc754..c817d8986 100644
--- a/src/enhancements/freecam/freecam_engine.h
+++ b/src/enhancements/freecam/freecam_engine.h
@@ -14,11 +14,13 @@ extern "C" {
typedef struct {
Vec3f velocity;
+ Vec3f rotVelocity;
Vec3f forwardVector;
} FreeCam;
extern FreeCam freeCam;
extern f32 gDampValue;
+extern f32 gRotDampValue;
extern u32 fTargetPlayer;
extern u32 fRankIndex;
@@ -26,8 +28,6 @@ void freecam_calculate_forward_vector_allow_rotation(Camera* camera, Vec3f forwa
void freecam_target_player(Camera* camera, Vec3f forwardVector);
void freecam_tick(Camera* camera, Vec3f forwardVector);
-extern f32 gDampValue;
-
#ifdef __cplusplus
}
#endif
diff --git a/src/main.c b/src/main.c
index e4e49d155..48fcec7ac 100644
--- a/src/main.c
+++ b/src/main.c
@@ -367,13 +367,18 @@ void update_controller(s32 index) {
}
// Prevents pause menu intereference while controlling flycam
- if ((CVarGetInteger("gFreecam", 0) == 1) && (gFreecamControllerType == 0) && (gGamestate == RACING)) {
+ // Freecam only works with controller 1
+ if ((CVarGetInteger("gFreecam", 0) == 1) && (gGamestate == RACING) && (index == 0)) {
+ freecam_update_controller();
return;
}
controller->rawStickX = gControllerPads[index].stick_x;
controller->rawStickY = gControllerPads[index].stick_y;
+ controller->rightRawStickX = gControllerPads[index].right_stick_x;
+ controller->rightRawStickY = gControllerPads[index].right_stick_y;
+
if ((gControllerPads[index].button & 4) != 0) {
gControllerPads[index].button |= Z_TRIG;
}
diff --git a/src/port/ui/FreecamWindow.cpp b/src/port/ui/FreecamWindow.cpp
index e5cf5eea1..b5a4a0ba4 100644
--- a/src/port/ui/FreecamWindow.cpp
+++ b/src/port/ui/FreecamWindow.cpp
@@ -22,14 +22,14 @@ extern "C" {
// } FreeCam;
extern f32 gDampValue;
+extern f32 gRotDampValue;
extern f32 gFreecamSpeed;
extern f32 gFreecamSpeedMultiplier;
-extern f32 gFreecamRotateSmoothingFactor;
extern f32 gFreecamFollowFactor;
extern char* D_800E76A8[];
extern u32 fRankIndex;
extern u32 fTargetPlayer;
-extern u32 gFreecamControllerType;
+extern u32 bFreecamUseController;
void freecam_get_player_from_character(s32 characterId);
}
@@ -58,12 +58,13 @@ void RegisterFreecamWidgets() {
//mPortMenu->AddWidget(path, "Controller mode is not configured yet.", WIDGET_TEXT);
//static int current_item = 0;
- mPortMenu->AddWidget(path, "Control Type", WIDGET_COMBOBOX)
+ mPortMenu->AddWidget(path, "Human Interface Device", WIDGET_COMBOBOX)
.ValuePointer(&controllerType)
- .Callback([](WidgetInfo& info) { gFreecamControllerType = (uint32_t)*std::get<int32_t*>(info.valuePointer); })
+ .Callback([](WidgetInfo& info) { bFreecamUseController = (uint32_t)*std::get<int32_t*>(info.valuePointer); })
.Options(UIWidgets::ComboboxOptions().ComboMap(controlType));
- mPortMenu->AddWidget(path, "Move: W,A,S,D\nUp: Space, Down: Shift\nFaster: Ctrl\nLook: Right-mouse button\nTarget Player Mode: F, Next: M, Previous: N", WIDGET_TEXT);
+ mPortMenu->AddWidget(path, "Keyboard: Move: W,A,S,D, Up: Space, Down: Shift\n Faster: Ctrl, Look: Right-mouse button\n Target Player Mode: F, Next: M, Previous: N", WIDGET_TEXT);
+ mPortMenu->AddWidget(path, "Controller: Up: A, Down: B, Faster: Z\n Target Player Mode: R, Next: Right DPad, Previous: Left DPad\n Driving Mode: L and R Buttons", WIDGET_TEXT);
mPortMenu->AddWidget(path, "Enable Flycam", WIDGET_CVAR_CHECKBOX)
.CVar("gFreecam")
.Options(UIWidgets::CheckboxOptions({{ .tooltip = "Allows you to fly around the course"}}));
@@ -77,8 +78,8 @@ void RegisterFreecamWidgets() {
mPortMenu->AddWidget(path, "Camera Speed Multiplier", WIDGET_SLIDER_FLOAT)
.ValuePointer(&gFreecamSpeedMultiplier)
.Options(UIWidgets::FloatSliderOptions().Min(1.5f).Max(15.0f).Step(0.1f).Format("%.1f"));
- mPortMenu->AddWidget(path, "Camera Rotation Smoothing", WIDGET_SLIDER_FLOAT)
- .ValuePointer(&gFreecamRotateSmoothingFactor)
+ mPortMenu->AddWidget(path, "Camera Rotator Damping", WIDGET_SLIDER_FLOAT)
+ .ValuePointer(&gRotDampValue)
.Options(UIWidgets::FloatSliderOptions().Min(0.0f).Max(1.0f).Step(0.01f).Format("%.2f"));
mPortMenu->AddWidget(path, "Follow Factor", WIDGET_SLIDER_FLOAT)
.ValuePointer(&gFreecamFollowFactor)