mirror of
https://github.com/HarbourMasters/SpaghettiKart
synced 2026-07-06 05:54:59 -04:00
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:
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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];
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user