From 66ec623542c16e92d69f9150fcd2883458a9cb7b Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Tue, 12 Jul 2022 18:40:18 -0400 Subject: [PATCH] Free Camera (#337) * wip free cam * Almost done, needs collision still * Added free cam behind cvar * added WIP collision * Fixed & implemented "Manual mode" from WW & TP * Fixed camera not rotating when Link is moving * fixed initialized camera rotation * Fixed camera getting stuck + made it smoother * reduced deadzone * fixed epona camera height + added WW z-target free camera * Adjusted player camera height & fixed fov * Fixed camera roll * fixed fov when moving the camera while in z-target * Camera resets to Auto when going through doors or changing maps * Fixed building * touch * more touch work * Added WIP mouse support to the free cam * gui stuff * fixed building * fixed building error * ok fixed building for real this time * oops * Fix compilation issues * removed mouse stuff that magically appeared in this branch * smoothed out stick values & removed remains of mouse support * re-added manual camera when pressing Z * reduced minimum Y position of camera * Addressed dcsv's nitpicks * part 2 * oops Co-authored-by: David Chavez --- libultraship/libultraship/Controller.cpp | 17 +- libultraship/libultraship/Controller.h | 5 +- libultraship/libultraship/ImGuiImpl.cpp | 2 + .../Lib/Fast3D/U64/PR/ultra64/controller.h | 8 +- libultraship/libultraship/SDLController.cpp | 28 +++- libultraship/libultraship/SDLController.h | 2 +- libultraship/libultraship/UltraController.h | 4 +- soh/include/z64.h | 3 + soh/src/code/z_camera.c | 155 ++++++++++++++++++ soh/src/code/z_play.c | 4 + .../gamestates/ovl_file_choose/file_choose.h | 5 + .../ovl_file_choose/z_file_choose.c | 8 +- 12 files changed, 221 insertions(+), 20 deletions(-) diff --git a/libultraship/libultraship/Controller.cpp b/libultraship/libultraship/Controller.cpp index e400f6e18..7cad7f178 100644 --- a/libultraship/libultraship/Controller.cpp +++ b/libultraship/libultraship/Controller.cpp @@ -2,6 +2,12 @@ #include "GlobalCtx2.h" #include "stox.h" #include +#include +#if __APPLE__ +#include +#else +#include +#endif namespace Ship { Controller::Controller(int32_t dwControllerNumber) : dwControllerNumber(dwControllerNumber) { @@ -16,8 +22,12 @@ namespace Ship { void Controller::Read(OSContPad* pad) { ReadFromSource(); + SDL_PumpEvents(); + + // Button Inputs pad->button |= dwPressedButtons & 0xFFFF; + // Stick Inputs if (pad->stick_x == 0) { if (dwPressedButtons & BTN_STICKLEFT) { pad->stick_x = -128; @@ -42,8 +52,13 @@ namespace Ship { } } + // Gyro pad->gyro_x = wGyroX; pad->gyro_y = wGyroY; + + // Right Stick + pad->cam_x = wCamX; + pad->cam_y = wCamY; } void Controller::SetButtonMapping(const std::string& szButtonName, int32_t dwScancode) { @@ -93,4 +108,4 @@ namespace Ship { std::string Controller::GetBindingConfSection() { return GetControllerType() + " CONTROLLER BINDING " + std::to_string(GetControllerNumber() + 1); } -} \ No newline at end of file +} diff --git a/libultraship/libultraship/Controller.h b/libultraship/libultraship/Controller.h index 256b31fee..d03d70e8a 100644 --- a/libultraship/libultraship/Controller.h +++ b/libultraship/libultraship/Controller.h @@ -13,7 +13,6 @@ namespace Ship { class Controller { - public: Controller(int32_t dwControllerNumber); @@ -38,7 +37,9 @@ namespace Ship { int8_t wStickY; float wGyroX; float wGyroY; - + float wCamX; + float wCamY; + virtual std::string GetControllerType() = 0; virtual std::string GetConfSection() = 0; virtual std::string GetBindingConfSection() = 0; diff --git a/libultraship/libultraship/ImGuiImpl.cpp b/libultraship/libultraship/ImGuiImpl.cpp index bade9f04f..ba51c1e99 100644 --- a/libultraship/libultraship/ImGuiImpl.cpp +++ b/libultraship/libultraship/ImGuiImpl.cpp @@ -1143,6 +1143,8 @@ namespace SohImGui { EnhancementCheckbox("Skip Text", "gSkipText"); Tooltip("Holding down B skips text\nKnown to cause a cutscene softlock in Water Temple\nSoftlock can be fixed by pressing D-Right in Debug mode"); + EnhancementCheckbox("Free Camera", "gFreeCamera"); + ImGui::EndMenu(); } diff --git a/libultraship/libultraship/Lib/Fast3D/U64/PR/ultra64/controller.h b/libultraship/libultraship/Lib/Fast3D/U64/PR/ultra64/controller.h index c2aafa919..af95c4212 100644 --- a/libultraship/libultraship/Lib/Fast3D/U64/PR/ultra64/controller.h +++ b/libultraship/libultraship/Lib/Fast3D/U64/PR/ultra64/controller.h @@ -112,9 +112,11 @@ typedef struct { /* 0x02 */ s8 stick_x; /* 0x03 */ s8 stick_y; /* 0x04 */ u8 err_no; - /* 0x05 */ f32 gyro_x; - /* 0x09 */ f32 gyro_y; -} OSContPad; // size = 0x0D + /* 0x05 */ f32 gyro_x; + /* 0x09 */ f32 gyro_y; + /* 0x1C */ f32 cam_x; + /* 0x20 */ f32 cam_y; +} OSContPad; // size = 0x24 typedef struct { /* 0x00 */ u8 rumble; diff --git a/libultraship/libultraship/SDLController.cpp b/libultraship/libultraship/SDLController.cpp index bc89cc78a..d0550e730 100644 --- a/libultraship/libultraship/SDLController.cpp +++ b/libultraship/libultraship/SDLController.cpp @@ -132,7 +132,7 @@ namespace Ship { } - void SDLController::NormalizeStickAxis(int16_t wAxisValueX, int16_t wAxisValueY, int16_t wAxisThreshold) { + void SDLController::NormalizeStickAxis(int16_t wAxisValueX, int16_t wAxisValueY, int16_t wAxisThreshold, bool isRightStick) { //scale {-32768 ... +32767} to {-84 ... +84} auto ax = wAxisValueX * 85.0 / 32767.0; auto ay = wAxisValueY * 85.0 / 32767.0; @@ -163,8 +163,15 @@ namespace Ship { ay *= scale; } - wStickX = +ax; - wStickY = -ay; + if (!isRightStick) { + wStickX = +ax; + wStickY = -ay; + } + else { + //SOHTODO KIRITO: Camera Sensitivity + wCamX = +ax * 15.0f; + wCamY = -ay * 15.0f; + } } void SDLController::ReadFromSource() { @@ -187,6 +194,10 @@ namespace Ship { } } + auto cameraX = SDL_GameControllerGetAxis(Cont, SDL_CONTROLLER_AXIS_RIGHTX); + auto cameraY = SDL_GameControllerGetAxis(Cont, SDL_CONTROLLER_AXIS_RIGHTY); + NormalizeStickAxis(cameraX, cameraY, ThresholdMapping[SDL_CONTROLLER_AXIS_LEFTX], true); + if (SDL_GameControllerHasSensor(Cont, SDL_SENSOR_GYRO)) { size_t contNumber = GetControllerNumber(); @@ -328,7 +339,7 @@ namespace Ship { if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != SDL_CONTROLLER_AXIS_INVALID) { auto AxisValueX = SDL_GameControllerGetAxis(Cont, StickAxisX); auto AxisValueY = SDL_GameControllerGetAxis(Cont, StickAxisY); - NormalizeStickAxis(AxisValueX, AxisValueY, StickDeadzone); + NormalizeStickAxis(AxisValueX, AxisValueY, StickDeadzone, false); } } } @@ -365,12 +376,13 @@ namespace Ship { void SDLController::CreateDefaultBinding() { std::string ConfSection = GetBindingConfSection(); std::shared_ptr pConf = GlobalCtx2::GetInstance()->GetConfig(); + ConfigFile& Conf = *pConf.get(); - Conf[ConfSection][STR(BTN_CRIGHT)] = std::to_string((SDL_CONTROLLER_AXIS_RIGHTX + AXIS_SCANCODE_BIT)); - Conf[ConfSection][STR(BTN_CLEFT)] = std::to_string(-(SDL_CONTROLLER_AXIS_RIGHTX + AXIS_SCANCODE_BIT)); - Conf[ConfSection][STR(BTN_CDOWN)] = std::to_string((SDL_CONTROLLER_AXIS_RIGHTY + AXIS_SCANCODE_BIT)); - Conf[ConfSection][STR(BTN_CUP)] = std::to_string(-(SDL_CONTROLLER_AXIS_RIGHTY + AXIS_SCANCODE_BIT)); + Conf[ConfSection][STR(BTN_CRIGHT)] = std::to_string(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); + Conf[ConfSection][STR(BTN_CLEFT)] = std::to_string(SDL_CONTROLLER_BUTTON_Y); + Conf[ConfSection][STR(BTN_CDOWN)] = std::to_string(SDL_CONTROLLER_BUTTON_X); + Conf[ConfSection][STR(BTN_CUP)] = std::to_string(SDL_CONTROLLER_BUTTON_RIGHTSTICK); //Conf[ConfSection][STR(BTN_CRIGHT + "_2")] = std::to_string(SDL_CONTROLLER_BUTTON_X); //Conf[ConfSection][STR(BTN_CLEFT + "_2")] = std::to_string(SDL_CONTROLLER_BUTTON_Y); //Conf[ConfSection][STR(BTN_CDOWN + "_2")] = std::to_string(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); diff --git a/libultraship/libultraship/SDLController.h b/libultraship/libultraship/SDLController.h index 2493efbbc..d1d671bb9 100644 --- a/libultraship/libultraship/SDLController.h +++ b/libultraship/libultraship/SDLController.h @@ -44,7 +44,7 @@ namespace Ship { std::map ThresholdMapping; void LoadAxisThresholds(); - void NormalizeStickAxis(int16_t wAxisValueX, int16_t wAxisValueY, int16_t wAxisThreshold); + void NormalizeStickAxis(int16_t wAxisValueX, int16_t wAxisValueY, int16_t wAxisThreshold, bool isRightStick); bool Open(); bool Close(); }; diff --git a/libultraship/libultraship/UltraController.h b/libultraship/libultraship/UltraController.h index 1c18dcd9b..e08212b97 100644 --- a/libultraship/libultraship/UltraController.h +++ b/libultraship/libultraship/UltraController.h @@ -120,7 +120,9 @@ typedef struct { /* 0x04 */ uint8_t err_no; /* 0x05 */ float gyro_x; /* 0x09 */ float gyro_y; -} OSContPad; // size = 0x0D + /* 0x1C */ float cam_x; + /* 0x20 */ float cam_y; +} OSContPad; // size = 0x24 typedef struct { /* 0x00 */ uint8_t rumble; diff --git a/soh/include/z64.h b/soh/include/z64.h index 2d8a6f4a8..fdc634e58 100644 --- a/soh/include/z64.h +++ b/soh/include/z64.h @@ -1203,6 +1203,9 @@ typedef struct GlobalContext { /* 0x00790 */ Camera* cameraPtrs[NUM_CAMS]; /* 0x007A0 */ s16 activeCamera; /* 0x007A2 */ s16 nextCamera; + /* 0x007A2 */ bool manualCamera; + /* 0x007A2 */ f32 camX; + /* 0x007A2 */ f32 camY; /* 0x007A4 */ SequenceContext sequenceCtx; /* 0x007A8 */ LightContext lightCtx; /* 0x007B8 */ FrameAdvanceContext frameAdvCtx; diff --git a/soh/src/code/z_camera.c b/soh/src/code/z_camera.c index 7f4a5d704..704062b1b 100644 --- a/soh/src/code/z_camera.c +++ b/soh/src/code/z_camera.c @@ -1409,7 +1409,125 @@ s32 Camera_Noop(Camera* camera) { return true; } +s32 SetCameraManual(Camera* camera) { + f32 newCamX = -D_8015BD7C->state.input[0].cur.cam_x; + f32 newCamY = D_8015BD7C->state.input[0].cur.cam_y; + + if ((fabsf(newCamX) >= 15.0f || fabsf(newCamY) >= 15.0f) && camera->globalCtx->manualCamera == false) { + camera->globalCtx->manualCamera = true; + + VecSph eyeAdjustment; + OLib_Vec3fDiffToVecSphGeo(&eyeAdjustment, &camera->at, &camera->eye); + + camera->globalCtx->camX = eyeAdjustment.yaw; + camera->globalCtx->camY = eyeAdjustment.pitch; + } + + if (camera->globalCtx->manualCamera) { + return 1; + } + + return 0; +} + +s32 Camera_Free(Camera* camera) { + Normal1* norm1 = (Normal1*)camera->paramData; + + f32 playerHeight = Player_GetHeight(camera->player); + f32 sp94; + CamColChk camBgChk; + PosRot* playerPosRot = &camera->playerPosRot; + Vec3f at; + + sCameraInterfaceFlags = norm1->interfaceFlags; + + if (RELOAD_PARAMS) { + VecSph eyeAdjustment1; + OLib_Vec3fDiffToVecSphGeo(&eyeAdjustment1, &camera->at, &camera->eye); + + camera->globalCtx->camX = eyeAdjustment1.yaw; + camera->globalCtx->camY = eyeAdjustment1.pitch; + + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(R_CAM_YOFFSET_NORM) - PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerHeight)); + sp94 = yNormal * PCT(playerHeight); + + norm1->yOffset = NEXTSETTING * sp94; + norm1->distMin = NEXTSETTING * sp94; + norm1->distMax = NEXTSETTING * sp94; + norm1->pitchTarget = DEGF_TO_BINANG(NEXTSETTING); + norm1->unk_0C = NEXTSETTING; + norm1->unk_10 = NEXTSETTING; + norm1->unk_14 = NEXTPCT; + norm1->fovTarget = NEXTSETTING; + norm1->atLERPScaleMax = NEXTPCT; + norm1->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + VecSph eyeAdjustment; + const f32 camSpeed = 0.5f; + + camera->animState = 0; + + at.x = Camera_LERPCeilF(camera->player->actor.world.pos.x, camera->at.x, camSpeed, 1.0f); + at.y = Camera_LERPCeilF(camera->player->actor.world.pos.y + (camera->player->rideActor != NULL ? Player_GetHeight(camera->player) / 2 : Player_GetHeight(camera->player)) / 1.2f, camera->at.y, camSpeed, 1.0f); + at.z = Camera_LERPCeilF(camera->player->actor.world.pos.z, camera->at.z, camSpeed, 1.0f); + + OLib_Vec3fDiffToVecSphGeo(&eyeAdjustment, &at, &camera->eye); + + camBgChk.pos = camera->eye; + + float maxRadius = 160.0f; + if (Camera_BGCheckInfo(camera, &at, &camBgChk)) { + VecSph collSphere; + OLib_Vec3fDiffToVecSphGeo(&collSphere, &at, &camBgChk.pos); + float rad = collSphere.r; + + if (rad >= maxRadius) { + camera->dist = eyeAdjustment.r = Camera_LERPCeilF(maxRadius, camera->dist, camSpeed / 4, 1.0f); + } else { + camera->dist = eyeAdjustment.r = rad; + } + } else { + camera->dist = eyeAdjustment.r = Camera_LERPCeilF(maxRadius, camera->dist, camSpeed / 4, 1.0f); + } + + f32 newCamX = -D_8015BD7C->state.input[0].cur.cam_x; + f32 newCamY = D_8015BD7C->state.input[0].cur.cam_y; + + camera->globalCtx->camX += newCamX; + camera->globalCtx->camY += newCamY; + + if (camera->globalCtx->camY > 0x32A4) { + camera->globalCtx->camY = 0x32A4; + } + if (camera->globalCtx->camY < -0x228C) { + camera->globalCtx->camY = -0x228C; + } + + eyeAdjustment.yaw = camera->globalCtx->camX; + eyeAdjustment.pitch = camera->globalCtx->camY; + + Camera_Vec3fVecSphGeoAdd(&camera->eye, &at, &eyeAdjustment); + + camera->at = at; + camera->fov = Camera_LERPCeilF(60.0f, camera->fov, camSpeed / 2, 1.0f); + camera->roll = 0; + camera->eyeNext = camera->eye; + + return 1; +} + s32 Camera_Normal1(Camera* camera) { + if (CVar_GetS32("gFreeCamera", 0) && SetCameraManual(camera) == 1) { + Camera_Free(camera); + return 1; + } + Vec3f* eye = &camera->eye; Vec3f* at = &camera->at; Vec3f* eyeNext = &camera->eyeNext; @@ -1637,6 +1755,11 @@ s32 Camera_Normal1(Camera* camera) { } s32 Camera_Normal2(Camera* camera) { + if (CVar_GetS32("gFreeCamera", 0) && SetCameraManual(camera) == 1) { + Camera_Free(camera); + return 1; + } + Vec3f* eye = &camera->eye; Vec3f* at = &camera->at; Vec3f* eyeNext = &camera->eyeNext; @@ -1803,6 +1926,11 @@ s32 Camera_Normal2(Camera* camera) { // riding epona s32 Camera_Normal3(Camera* camera) { + if (CVar_GetS32("gFreeCamera", 0) && SetCameraManual(camera) == 1) { + Camera_Free(camera); + return 1; + } + Vec3f* eye = &camera->eye; Vec3f* at = &camera->at; Vec3f* eyeNext = &camera->eyeNext; @@ -1997,6 +2125,8 @@ s32 Camera_Parallel1(Camera* camera) { OLib_Vec3fDiffToVecSphGeo(&atToEyeDir, at, eye); OLib_Vec3fDiffToVecSphGeo(&atToEyeNextDir, at, eyeNext); + camera->globalCtx->manualCamera = false; + switch (camera->animState) { case 0: case 0xA: @@ -2162,6 +2292,11 @@ s32 Camera_Parallel0(Camera* camera) { * Generic jump, jumping off ledges */ s32 Camera_Jump1(Camera* camera) { + if (CVar_GetS32("gFreeCamera", 0) && SetCameraManual(camera) == 1) { + Camera_Free(camera); + return 1; + } + Vec3f* eye = &camera->eye; Vec3f* at = &camera->at; Vec3f* eyeNext = &camera->eyeNext; @@ -2307,6 +2442,11 @@ s32 Camera_Jump1(Camera* camera) { // Climbing ladders/vines s32 Camera_Jump2(Camera* camera) { + if (CVar_GetS32("gFreeCamera", 0) && SetCameraManual(camera) == 1) { + Camera_Free(camera); + return 1; + } + Vec3f* eye = &camera->eye; Vec3f* at = &camera->at; Vec3f* eyeNext = &camera->eyeNext; @@ -2489,6 +2629,11 @@ s32 Camera_Jump2(Camera* camera) { // swimming s32 Camera_Jump3(Camera* camera) { + if (CVar_GetS32("gFreeCamera", 0) && SetCameraManual(camera) == 1) { + Camera_Free(camera); + return 1; + } + Vec3f* eye = &camera->eye; Vec3f* at = &camera->at; Vec3f* eyeNext = &camera->eyeNext; @@ -2946,6 +3091,11 @@ s32 Camera_Battle3(Camera* camera) { * setting value. */ s32 Camera_Battle4(Camera* camera) { + if (CVar_GetS32("gFreeCamera", 0) && SetCameraManual(camera) == 1) { + Camera_Free(camera); + return 1; + } + Vec3f* eye = &camera->eye; Vec3f* at = &camera->at; Vec3f* eyeNext = &camera->eyeNext; @@ -4476,6 +4626,11 @@ s32 Camera_Data4(Camera* camera) { * Hanging off of a ledge */ s32 Camera_Unique1(Camera* camera) { + if (CVar_GetS32("gFreeCamera", 0) && SetCameraManual(camera) == 1) { + Camera_Free(camera); + return 1; + } + Vec3f* eye = &camera->eye; Vec3f* at = &camera->at; Vec3f* eyeNext = &camera->eyeNext; diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index d06f9ce4c..c6938718f 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -530,6 +530,10 @@ void Gameplay_Update(GlobalContext* globalCtx) { ActorOverlayTable_LogPrint(); } + if (CVar_GetS32("gFreeCamera", 0) && Player_InCsMode(globalCtx)) { + globalCtx->manualCamera = false; + } + gSegments[4] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[globalCtx->objectCtx.mainKeepIndex].segment); gSegments[5] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[globalCtx->objectCtx.subKeepIndex].segment); gSegments[2] = VIRTUAL_TO_PHYSICAL(globalCtx->sceneSegment); diff --git a/soh/src/overlays/gamestates/ovl_file_choose/file_choose.h b/soh/src/overlays/gamestates/ovl_file_choose/file_choose.h index 0a510eba2..93f4efb76 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/file_choose.h +++ b/soh/src/overlays/gamestates/ovl_file_choose/file_choose.h @@ -203,6 +203,11 @@ void FileChoose_DrawOptions(GameState* thisx); void FileChoose_DrawNameEntry(GameState* thisx); void FileChoose_DrawCharacter(GraphicsContext* gfxCtx, void* texture, s16 vtx); +void HandleMouseInput(Input* input); +u8 HandleMouseCursor(FileChooseContext* this, Input* input, int minx, int miny, int maxx, int maxy); +Vec2f HandleMouseCursorSplit(FileChooseContext* this, Input* input, int minx, int miny, int maxx, int maxy, int countx, + int county); + extern s16 D_808123F0[]; #endif diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 08d0c855b..d07222768 100644 --- a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -415,15 +415,15 @@ void FileChoose_UpdateMainMenu(GameState* thisx) { } if ((CVar_GetS32("gNewFileDropped", 0) != 0) || - (CVar_GetS32("gNewSeedGenerated", 0) != 0) || + (CVar_GetS32("gNewSeedGenerated", 0) != 0) || (!fileSelectSpoilerFileLoaded && - SpoilerFileExists(CVar_GetString("gSpoilerLog", "")))) { + SpoilerFileExists(CVar_GetString("gSpoilerLog", "")))) { if (CVar_GetS32("gNewFileDropped", 0) != 0) { CVar_SetString("gSpoilerLog", CVar_GetString("gDroppedFile", "")); } bool silent = true; - if((CVar_GetS32("gNewFileDropped", 0) != 0) || - (CVar_GetS32("gNewSeedGenerated", 0) != 0)) { + if ((CVar_GetS32("gNewFileDropped", 0) != 0) || + (CVar_GetS32("gNewSeedGenerated", 0) != 0)) { silent = false; } CVar_SetS32("gNewSeedGenerated", 0);