Finish Freecam (#186)

* Fix freecam rotation smoothing

* Configure freecam controller

* Fix cont z button and impl freecam on/off events
This commit is contained in:
MegaMech
2025-02-07 19:27:44 -07:00
committed by GitHub
parent fc1e1a9782
commit 7fe7052270
8 changed files with 134 additions and 87 deletions
+2
View File
@@ -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
+1 -5
View File
@@ -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);
+94 -68
View File
@@ -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 (controller->buttonPressed & L_TRIG) {
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
}
// Freecam mode is enabled
if (enabled) {
freecam_loop(camera, player, index);
} else {
func_8001E45C(camera, player, index);
}
}
void on_freecam(void) {
gIsHUDVisible = false;
gPlayerOne->type |= PLAYER_KART_AI;
}
void off_freecam(void) {
gIsHUDVisible = true;
gPlayerOne->type &= ~PLAYER_KART_AI;
}
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));
// 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);
}
// 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;
// Sensitivity multipliers
float controllerSensitivityX = 5.0f; // Adjust as needed
float controllerSensitivityY = 3.0f;
if (wnd->GetMouseState(Ship::LUS_MOUSE_BTN_RIGHT)) {
// 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;
// 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
f32 yawChange = mouse.x * MOUSE_SENSITIVITY_X;
f32 pitchChange = mouse.y * 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 (wnd->GetMouseState(Ship::LUS_MOUSE_BTN_RIGHT)) {
yawChange = mouse.x * MOUSE_SENSITIVITY_X;
pitchChange = mouse.y * MOUSE_SENSITIVITY_Y;
}
if (camera->rot[2] < -15999) {
camera->rot[2] = -15999;
}
// 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 (controller->buttonPressed & L_CBUTTONS) {
if (fController.buttonDepressed & L_TRIG) {
fTargetPlayer = false;
}
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) {
if (fController.rawStickX > 10) {
PanRight = true;
}
if (fController.rawStickX < -10) {
PanLeft = true;
}
if (controller->button & R_JPAD) {
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;
+5 -2
View File
@@ -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
}
+16 -2
View File
@@ -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];
+2 -2
View File
@@ -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
+6 -1
View File
@@ -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;
}
+8 -7
View File
@@ -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)