From ba58d2486e5dbae1c98ee363cbac0ea1f1511923 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Sat, 25 Apr 2026 23:35:43 -0400 Subject: [PATCH] Free Camera (#542) * freecam wip * added manual mode * fix freecam collision * made freecam into its own function * Added settings & Radius modification when camer is higher up --------- Co-authored-by: MelonSpeedruns --- include/d/d_cam_param.h | 6 +++ include/d/d_camera.h | 5 ++ include/dusk/settings.h | 5 +- src/d/d_camera.cpp | 66 ++++++++++++++++++++++++++ src/dusk/imgui/ImGuiFirstRunPreset.cpp | 1 + src/dusk/imgui/ImGuiMenuGame.cpp | 10 +++- src/dusk/settings.cpp | 8 +++- 7 files changed, 98 insertions(+), 3 deletions(-) diff --git a/include/d/d_cam_param.h b/include/d/d_cam_param.h index d6ff7b46d7..867c66e38d 100644 --- a/include/d/d_cam_param.h +++ b/include/d/d_cam_param.h @@ -143,6 +143,12 @@ public: /* 0x20 */ JORFile mFile; #endif +#if TARGET_PC + /* 0x24 */ u8 mManualMode; + /* 0x25 */ f32 freeXAngle; + /* 0x29 */ f32 freeYAngle; +#endif + u32 Id(s32 i_style) { return mCamStyleData[i_style].field_0x0; } int Algorythmn(s32 i_style) { return mCamStyleData[i_style].field_0x4; } int Algorythmn() { return mCurrentStyle->field_0x4; } diff --git a/include/d/d_camera.h b/include/d/d_camera.h index 85e930c395..105c9cae9f 100644 --- a/include/d/d_camera.h +++ b/include/d/d_camera.h @@ -273,6 +273,8 @@ public: /* 0xA4 */ f32 field_0xa4; /* 0xA8 */ int field_0xa8; /* 0xAC */ f32 field_0xac; + f32 xAngle; + f32 yAngle; }; struct LockOnData { @@ -1024,6 +1026,9 @@ public: bool colosseumCamera(s32); bool test1Camera(s32); bool test2Camera(s32); + #if TARGET_PC + bool freeCamera(); + #endif bool towerCamera(s32); bool hookshotCamera(s32); bool railCamera(s32); diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 8a54197317..a7815569d0 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -82,7 +82,6 @@ struct UserSettings { // Preferences ConfigVar enableMirrorMode; - ConfigVar invertCameraXAxis; ConfigVar disableMainHUD; ConfigVar pauseOnFocusLost; ConfigVar enableLinkDollRotation; @@ -112,6 +111,10 @@ struct UserSettings { ConfigVar gyroDeadband; ConfigVar gyroInvertPitch; ConfigVar gyroInvertYaw; + ConfigVar freeCamera; + ConfigVar invertCameraXAxis; + ConfigVar invertCameraYAxis; + ConfigVar freeCameraSensitivity; // Cheats ConfigVar infiniteHearts; diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index f805a6a08b..917b30709a 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -800,6 +800,10 @@ void dCamera_c::updatePad() { } mLockLActive = 1; + + #if TARGET_PC + mCamParam.mManualMode = 0; + #endif } else { mLockLJustActivated = 0; mLockLActive = 0; @@ -833,6 +837,12 @@ void dCamera_c::updatePad() { mHoldB = mDoCPd_c::getHoldB(mPadID) ? true : false; mTrigB = mDoCPd_c::getTrigB(mPadID) ? true : false; + #if TARGET_PC + if (mCamParam.mManualMode) { + return; + } + #endif + bool sp6B = true; bool sp6C = true; int temp1; @@ -1167,6 +1177,13 @@ bool dCamera_c::Run() { } } else { sp0F = (this->*engine_tbl[mCamParam.Algorythmn(mCamStyle)])(mCamStyle); + + #if TARGET_PC + if (mCamParam.Algorythmn(mCamStyle) != 1) { + mCamParam.mManualMode = 0; + } + #endif + field_0x170++; field_0x160++; mCurCamStyleTimer++; @@ -3078,6 +3095,11 @@ bool dCamera_c::bumpCheck(u32 i_flags) { } else { field_0x968 *= mMonitor.field_0xc / 5.0f; } + + #if TARGET_PC + if (!dusk::getSettings().game.freeCamera || !mCamParam.mManualMode) { + #endif + f32 tmp = field_0x96c * (mIsWolf == 1 ? 30.0f : 30.0f); center += vec3.norm() * (tmp * globe.V().Sin()); cSGlobe globe2(vec2 - center); @@ -3091,6 +3113,10 @@ bool dCamera_c::bumpCheck(u32 i_flags) { vec = lin_chk1.GetCross(); } + #if TARGET_PC + } + #endif + #if DEBUG if (mCamSetup.CheckFlag(0x8000)) { dDbVw_Report(20, 235, " U"); @@ -4604,6 +4630,11 @@ bool dCamera_c::chaseCamera(s32 param_0) { sp110 = mViewCache.mDirection.R(); mViewCache.mDirection.R(mViewCache.mDirection.R() + (fVar55 - mViewCache.mDirection.R()) * chase->field_0x74); + + #if TARGET_PC + freeCamera(); + #endif + chase->field_0x64 = mViewCache.mCenter + mViewCache.mDirection.Xyz(); mViewCache.mEye = chase->field_0x64; @@ -7444,6 +7475,41 @@ bool dCamera_c::test2Camera(s32 param_0) { return false; } +#if TARGET_PC +bool dCamera_c::freeCamera() { + if (!dusk::getSettings().game.freeCamera) { + mCamParam.mManualMode = 0; + return false; + } + + cXyz camMovement = {mPadInfo.mCStick.mLastPosX, mPadInfo.mCStick.mLastPosY, 0.0f}; + f32 magnitude = sqrt(mPadInfo.mCStick.mLastPosX * mPadInfo.mCStick.mLastPosX + mPadInfo.mCStick.mLastPosY * mPadInfo.mCStick.mLastPosY); + + if (mPadInfo.mCStick.mLastPosX != 0 || mPadInfo.mCStick.mLastPosY != 0) { + if (!mCamParam.mManualMode) { + mCamParam.mManualMode = 1; + mCamParam.freeXAngle = mViewCache.mDirection.mAzimuth.Degree(); + mCamParam.freeYAngle = mViewCache.mDirection.mInclination.Degree(); + } + + camMovement = camMovement.normalize(); + camMovement.x *= (dusk::getSettings().game.invertCameraXAxis ? 1.0f : -1.0f) * dusk::getSettings().game.freeCameraSensitivity * 4.0f; + camMovement.y *= (dusk::getSettings().game.invertCameraYAxis ? 1.0f : -1.0f) * dusk::getSettings().game.freeCameraSensitivity * 4.0f; + mCamParam.freeXAngle += camMovement.x * magnitude * dusk::getSettings().game.freeCameraSensitivity; + mCamParam.freeYAngle += camMovement.y * magnitude * dusk::getSettings().game.freeCameraSensitivity; + } + + if (mCamParam.mManualMode) { + mCamParam.freeYAngle = std::clamp(mCamParam.freeYAngle, -35.0f, 60.0f); + mViewCache.mDirection.mAzimuth = cSAngle(mCamParam.freeXAngle); + mViewCache.mDirection.mInclination = cSAngle(mCamParam.freeYAngle); + mViewCache.mDirection.mRadius = std::clamp(mCamParam.freeYAngle * 15.0f, 300.0f, 10000.0f); + } + + return mCamParam.mManualMode; +} +#endif + bool dCamera_c::towerCamera(s32 param_0) { cSAngle stack_444 = cSAngle(mCamSetup.ChargeLatitude()); f32 sp224 = mCamSetup.ChargeBRatio(); diff --git a/src/dusk/imgui/ImGuiFirstRunPreset.cpp b/src/dusk/imgui/ImGuiFirstRunPreset.cpp index 8009305870..2a9bcc5c6e 100644 --- a/src/dusk/imgui/ImGuiFirstRunPreset.cpp +++ b/src/dusk/imgui/ImGuiFirstRunPreset.cpp @@ -29,6 +29,7 @@ static void ApplyPresetHD() { s.game.fastTears.setValue(true); s.game.biggerWallets.setValue(true); s.game.invertCameraXAxis.setValue(true); + s.game.freeCamera.setValue(true); } static void ApplyPresetDusk() { diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 8cb50e143d..0a687233aa 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -383,7 +383,15 @@ namespace dusk { ImGui::SeparatorText("Camera"); - config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis); + config::ImGuiCheckbox("Free Camera", getSettings().game.freeCamera); + + if (getSettings().game.freeCamera) { + config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis); + config::ImGuiCheckbox("Invert Camera Y Axis", getSettings().game.invertCameraYAxis); + config::ImGuiSliderFloat("Free Camera Sensitivity", getSettings().game.freeCameraSensitivity, 0.5f, 2.0f, "%.1f"); + } else { + config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis); + } ImGui::SeparatorText("Gyro"); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index e10e6d9ee9..eb1d513ba9 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -42,7 +42,6 @@ UserSettings g_userSettings = { // Preferences .enableMirrorMode {"game.enableMirrorMode", false}, - .invertCameraXAxis {"game.invertCameraXAxis", false}, .disableMainHUD {"game.disableMainHUD", false}, .pauseOnFocusLost {"game.pauseOnFocusLost", false}, .enableLinkDollRotation = {"game.enableLinkDollRotation", false }, @@ -71,6 +70,10 @@ UserSettings g_userSettings = { .gyroDeadband {"game.gyroDeadband", 0.04f}, .gyroInvertPitch {"game.gyroInvertPitch", false}, .gyroInvertYaw {"game.gyroInvertYaw", false}, + .freeCamera {"game.freeCamera", false}, + .invertCameraXAxis {"game.invertCameraXAxis", false}, + .invertCameraYAxis {"game.invertCameraYAxis", false}, + .freeCameraSensitivity {"game.freeCameraSensitivity", 1.0f}, // Cheats .infiniteHearts {"game.infiniteHearts", false}, @@ -144,6 +147,8 @@ void registerSettings() { Register(g_userSettings.game.sunsSong); Register(g_userSettings.game.enableMirrorMode); Register(g_userSettings.game.invertCameraXAxis); + Register(g_userSettings.game.invertCameraYAxis); + Register(g_userSettings.game.freeCameraSensitivity); Register(g_userSettings.game.disableMainHUD); Register(g_userSettings.game.pauseOnFocusLost); Register(g_userSettings.game.bloomMode); @@ -183,6 +188,7 @@ void registerSettings() { Register(g_userSettings.game.gyroSmoothing); Register(g_userSettings.game.gyroInvertPitch); Register(g_userSettings.game.gyroInvertYaw); + Register(g_userSettings.game.freeCamera); Register(g_userSettings.backend.isoPath); Register(g_userSettings.backend.graphicsBackend);