diff --git a/Runtime/Input/CControllerAxis.hpp b/Runtime/Input/CControllerAxis.hpp new file mode 100644 index 000000000..c74e5d246 --- /dev/null +++ b/Runtime/Input/CControllerAxis.hpp @@ -0,0 +1,22 @@ +#pragma once + +namespace metaforce { +enum class EJoyAxis { + LeftX, + LeftY, + RightX, + RightY, + MAX +}; + +class CControllerAxis { + float x0_absolute = 0.f; + float x4_relative = 0.f; + +public: + void SetRelativeValue(float val) { x0_absolute = val; } + float GetRelativeValue() const { return x0_absolute; } + void SetAbsoluteValue(float val) { x4_relative = val; } + float GetAbsoluteValue() const { return x4_relative; } +}; +} // namespace metaforce \ No newline at end of file diff --git a/Runtime/Input/CControllerButton.hpp b/Runtime/Input/CControllerButton.hpp new file mode 100644 index 000000000..362f4a64a --- /dev/null +++ b/Runtime/Input/CControllerButton.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "Runtime/GCNTypes.hpp" + +namespace metaforce { +enum class EButton { + A, + B, + X, + Y, + Start, + Z, + Up, + Right, + Down, + Left, + L, + R, + MAX, +}; + +enum class EAnalogButton { + Left, + Right +}; + +class CControllerButton { + bool x0_; + bool x1_pressed; + bool x2_; + +public: + void SetIsPressed(bool pressed) { x1_pressed = pressed; } + [[nodiscard]] bool GetIsPressed() const { return x1_pressed; } + void SetPressEvent(bool press); + [[nodiscard]] bool GetPressEvent() const; + void SetReleaseEvent(bool release); +}; +} // namespace metaforce \ No newline at end of file diff --git a/Runtime/Input/CControllerGamepadData.cpp b/Runtime/Input/CControllerGamepadData.cpp new file mode 100644 index 000000000..4757a8714 --- /dev/null +++ b/Runtime/Input/CControllerGamepadData.cpp @@ -0,0 +1,4 @@ +#include "Runtime/Input/CControllerGamepadData.hpp" + +namespace metaforce { +} diff --git a/Runtime/Input/CControllerGamepadData.hpp b/Runtime/Input/CControllerGamepadData.hpp new file mode 100644 index 000000000..2bfee98d7 --- /dev/null +++ b/Runtime/Input/CControllerGamepadData.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "Runtime/Input/CControllerAxis.hpp" +#include "Runtime/Input/CControllerButton.hpp" + +#include + +namespace metaforce { +class CControllerGamepadData { + bool x0_present; + std::array x4_axes; + std::array x24_triggers; + std::array x34_buttons; + +public: + void SetDeviceIsPresent(bool present) { x0_present = present; } + [[nodiscard]] bool DeviceIsPresent() const { return x0_present; } + + [[nodiscard]] const CControllerAxis& GetAxis(EJoyAxis axis) const { return x4_axes[u32(axis)]; } + [[nodiscard]] CControllerAxis& GetAxis(EJoyAxis axis) { return x4_axes[u32(axis)]; } + + [[nodiscard]] const CControllerButton& GetButton(EButton button) const { return x34_buttons[u32(button)]; } + [[nodiscard]] CControllerButton& GetButton(EButton button) { return x34_buttons[u32(button)]; } + + [[nodiscard]] const CControllerAxis& GetAnalogButton(EAnalogButton button) const { return x24_triggers[u32(button)]; } + [[nodiscard]] CControllerAxis& GetAnalogButton(EAnalogButton button) { return x24_triggers[u32(button)]; } +}; +} // namespace metaforce \ No newline at end of file diff --git a/Runtime/Input/CDolphinController.cpp b/Runtime/Input/CDolphinController.cpp new file mode 100644 index 000000000..a0f2b4203 --- /dev/null +++ b/Runtime/Input/CDolphinController.cpp @@ -0,0 +1,98 @@ +#include "Runtime/Input/CDolphinController.hpp" + +namespace PAD { +// clang-format off +enum BUTTON : u16 { + LEFT = 0x0001, + RIGHT = 0x0002, + DOWN = 0x0004, + UP = 0x0008, + TRIGGER_Z = 0x0010, + TRIGGER_R = 0x0020, + TRIGGER_L = 0x0040, + BUTTON_A = 0x0100, + BUTTON_B = 0x0200, + BUTTON_X = 0x0400, + BUTTON_Y = 0x0800, + BUTTON_START = 0x1000, +}; +// clang-format on +} // namespace PAD + +namespace metaforce { +CDolphinController::CDolphinController() { + static bool sIsInitialized = false; + if (!sIsInitialized) { + // PADSetSpec(5); + // PADInit(); + sIsInitialized = true; + } +} +void CDolphinController::SetMotorState(EIOPort port, EMotorState state) { x194_motorStates[u32(port)] = state; } + +float CDolphinController::GetAnalogStickMaxValue(EJoyAxis axis) { + if (axis >= EJoyAxis::LeftX && axis <= EJoyAxis::LeftY) { + return 72.f; + } + + if (axis >= EJoyAxis::RightX && axis <= EJoyAxis::RightY) { + return 59.f; + } + + return 0.f; +} + +void CDolphinController::ProcessAxis(u32 controller, EJoyAxis axis) { + const auto maxAxisValue = GetAnalogStickMaxValue(axis); + auto& data = x34_gamepadStates[controller].GetAxis(axis); + + float axisValue = 0.f; + if (axis == EJoyAxis::LeftX) { + axisValue = x4_status[controller].x2_stickX; + } else if (axis == EJoyAxis::LeftY) { + axisValue = x4_status[controller].x3_stickY; + } else if (axis == EJoyAxis::RightX) { + axisValue = x4_status[controller].x4_substickX; + } else if (axis == EJoyAxis::RightY) { + axisValue = x4_status[controller].x5_substickY; + } + axisValue *= 1.f / maxAxisValue; + float absolute = kAbsoluteMinimum; + if (axisValue < kAbsoluteMinimum) { + absolute = kAbsoluteMinimum; + } else if (axisValue > kAbsoluteMaximum) { + absolute = kAbsoluteMaximum; + } + + axisValue = absolute - data.GetAbsoluteValue(); + float relativeValue = kRelativeMinimum; + if (axisValue < kRelativeMinimum) { + relativeValue = kRelativeMinimum; + } else if (axisValue > kRelativeMaximum) { + relativeValue = kRelativeMaximum; + } + + data.SetAbsoluteValue(absolute); + data.SetRelativeValue(relativeValue); +} + +static constexpr std::array mButtonMapping{ + PAD::BUTTON_A, PAD::BUTTON_B, PAD::BUTTON_X, PAD::BUTTON_Y, PAD::BUTTON_START, PAD::TRIGGER_Z, + PAD::UP, PAD::RIGHT, PAD::DOWN, PAD::LEFT, PAD::TRIGGER_L, PAD::TRIGGER_R, +}; + +void CDolphinController::ProcessButtons(u32 controller) { + for (u32 i = 0; i < u32(EButton::MAX); ++i) { + ProcessDigitalButton(controller, x34_gamepadStates[controller].GetButton(EButton(i)), mButtonMapping[i]); + } + + ProcessAnalogButton(x4_status[controller].x6_triggerL, + x34_gamepadStates[controller].GetAnalogButton(EAnalogButton::Left)); + ProcessAnalogButton(x4_status[controller].x7_triggerR, + x34_gamepadStates[controller].GetAnalogButton(EAnalogButton::Right)); +} +void CDolphinController::ProcessDigitalButton(u32 controller, CControllerButton& button, u16 mapping) {} +void CDolphinController::ProcessAnalogButton(float value, CControllerAxis& axis) {} + +void CDolphinController::Initialize() {} +} // namespace metaforce \ No newline at end of file diff --git a/Runtime/Input/CDolphinController.hpp b/Runtime/Input/CDolphinController.hpp new file mode 100644 index 000000000..05f68bcf7 --- /dev/null +++ b/Runtime/Input/CDolphinController.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "Runtime/Input/IController.hpp" + +namespace metaforce { +struct PADStatus { + u16 x0_buttons; + s8 x2_stickX; + s8 x3_stickY; + s8 x4_substickX; + s8 x5_substickY; + u8 x6_triggerL; + u8 x7_triggerR; + u8 x8_analogA; + u8 x9_analogB; + s8 xa_err; +}; + +class CDolphinController : public IController { + + std::array x4_status; + std::array x34_gamepadStates{}; + std::array x194_motorStates; + std::array x1a4_controllerTypes{}; + std::array x1b4_{}; + u32 x1c4_ = 0xf0000000; + u32 x1c8_ = 0; + u32 x1cc_ = 0; + +public: + CDolphinController(); + void Poll() override{}; + [[nodiscard]] u32 GetDeviceCount() const override { return 4; }; + [[nodiscard]] CControllerGamepadData& GetGamepadData(u32 controller) override { + return x34_gamepadStates[controller]; + }; + [[nodiscard]] u32 GetControllerType(u32 controller) const override { return x1a4_controllerTypes[controller]; } + void SetMotorState(EIOPort port, EMotorState state) override; + + float GetAnalogStickMaxValue(EJoyAxis axis); + void ProcessAxis(u32 controller, EJoyAxis axis); + void ProcessButtons(u32 controller); + void ProcessDigitalButton(u32 controller, CControllerButton& button, u16 mapping); + void ProcessAnalogButton(float value, CControllerAxis& axis); + + void Initialize(); +}; +} // namespace metaforce \ No newline at end of file diff --git a/Runtime/Input/CInputGenerator.hpp b/Runtime/Input/CInputGenerator.hpp index 0e9438f38..be1328dc8 100644 --- a/Runtime/Input/CInputGenerator.hpp +++ b/Runtime/Input/CInputGenerator.hpp @@ -4,16 +4,13 @@ #include #include +#include "Runtime/Input/InputTypes.hpp" #include "Runtime/Input/CFinalInput.hpp" #include "Runtime/Input/CKeyboardMouseController.hpp" namespace metaforce { class CArchitectureQueue; -enum class EIOPort { Zero, One, Two, Three }; - -enum class EMotorState { Stop, Rumble, StopHard }; - class CInputGenerator /*: public boo::DeviceFinder*/ { enum class EStatusChange { NoChange = 0, Connected = 1, Disconnected = 2 }; @@ -152,7 +149,7 @@ public: // } void SetMotorState(EIOPort port, EMotorState state); void ControlAllMotors(const std::array& states) { - for (u32 i = 0; i <= size_t(EIOPort::Three); ++i ) { + for (u32 i = 0; i <= size_t(EIOPort::Player4); ++i ) { SetMotorState(EIOPort(i), states[i]); } } diff --git a/Runtime/Input/CMakeLists.txt b/Runtime/Input/CMakeLists.txt index 809f890f1..d5c325173 100644 --- a/Runtime/Input/CMakeLists.txt +++ b/Runtime/Input/CMakeLists.txt @@ -1,5 +1,9 @@ set(INPUT_SOURCES - IController.hpp + IController.hpp DolphinIController.cpp + CControllerAxis.hpp + CControllerButton.hpp + CControllerGamepadData.hpp CControllerGamepadData.cpp + CDolphinController.hpp CDolphinController.cpp CKeyboardMouseController.hpp ControlMapper.hpp ControlMapper.cpp CInputGenerator.hpp CInputGenerator.cpp diff --git a/Runtime/Input/CRumbleManager.cpp b/Runtime/Input/CRumbleManager.cpp index c00db013f..b19f33845 100644 --- a/Runtime/Input/CRumbleManager.cpp +++ b/Runtime/Input/CRumbleManager.cpp @@ -20,7 +20,7 @@ s16 CRumbleManager::Rumble(CStateManager& mgr, const zeus::CVector3f& pos, ERumb s16 CRumbleManager::Rumble(CStateManager& mgr, ERumbleFxId fx, float gain, ERumblePriority priority) { if (g_GameState->GameOptions().GetIsRumbleEnabled()) - return x0_rumbleGenerator.Rumble(RumbleFxTable[size_t(fx)], gain, priority, EIOPort::Zero); + return x0_rumbleGenerator.Rumble(RumbleFxTable[size_t(fx)], gain, priority, EIOPort::Player1); return -1; } diff --git a/Runtime/Input/CRumbleManager.hpp b/Runtime/Input/CRumbleManager.hpp index 9d642196c..d1298eb78 100644 --- a/Runtime/Input/CRumbleManager.hpp +++ b/Runtime/Input/CRumbleManager.hpp @@ -17,7 +17,7 @@ public: void StopRumble(s16 id) { if (id == -1) return; - x0_rumbleGenerator.Stop(id, EIOPort::Zero); + x0_rumbleGenerator.Stop(id, EIOPort::Player1); } void HardStopAll() { x0_rumbleGenerator.HardStopAll(); } s16 Rumble(CStateManager& mgr, const zeus::CVector3f& pos, ERumbleFxId fx, float dist, ERumblePriority priority); diff --git a/Runtime/Input/DolphinIController.cpp b/Runtime/Input/DolphinIController.cpp new file mode 100644 index 000000000..0f828b6b9 --- /dev/null +++ b/Runtime/Input/DolphinIController.cpp @@ -0,0 +1,10 @@ +#include "Runtime/Input/IController.hpp" +#include "Runtime/Input/CDolphinController.hpp" + +namespace metaforce { +IController* IController::Create() { + CDolphinController* cont = new CDolphinController(); + cont->Initialize(); + return cont; +} +} \ No newline at end of file diff --git a/Runtime/Input/IController.hpp b/Runtime/Input/IController.hpp index d6069d40a..a182b2bb2 100644 --- a/Runtime/Input/IController.hpp +++ b/Runtime/Input/IController.hpp @@ -1,12 +1,26 @@ #pragma once +#include "Runtime/Input/InputTypes.hpp" +#include "Runtime/Input/CControllerGamepadData.hpp" + +#include "Runtime/GCNTypes.hpp" + namespace metaforce { class IController { +protected: + static constexpr float kAbsoluteMinimum = -1.f; + static constexpr float kAbsoluteMaximum = 1.f; + static constexpr float kRelativeMinimum = -1.f; + static constexpr float kRelativeMaximum = 1.f; public: - enum class EMotorState { Stop = 0, Rumble = 1, StopHard = 2 }; virtual void Poll() = 0; - virtual void SetMotorState(EMotorState state) = 0; + virtual u32 GetDeviceCount() const = 0; + virtual CControllerGamepadData& GetGamepadData(u32 controller) = 0; + virtual u32 GetControllerType(u32 controller) const = 0; + virtual void SetMotorState(EIOPort port, EMotorState state) = 0; + + static IController* Create(); }; } // namespace metaforce diff --git a/Runtime/Input/InputTypes.hpp b/Runtime/Input/InputTypes.hpp new file mode 100644 index 000000000..37f3ae9a0 --- /dev/null +++ b/Runtime/Input/InputTypes.hpp @@ -0,0 +1,16 @@ +#pragma once + +namespace metaforce { +enum class EIOPort { + Player1, + Player2, + Player3, + Player4, +}; + +enum class EMotorState { + Stop = 0, + Rumble = 1, + StopHard = 2, +}; +} // namespace metaforce \ No newline at end of file diff --git a/Runtime/MP1/CFrontEndUI.cpp b/Runtime/MP1/CFrontEndUI.cpp index f509e930d..a1ce3a8f4 100644 --- a/Runtime/MP1/CFrontEndUI.cpp +++ b/Runtime/MP1/CFrontEndUI.cpp @@ -1395,7 +1395,7 @@ void CFrontEndUI::SOptionsFrontEndFrame::DoMenuSelectionChange(CGuiTableGroup* c if (option.option == EGameOption::Rumble && caller->GetUserSelection() > 0) { x40_rumbleGen.HardStopAll(); - x40_rumbleGen.Rumble(RumbleFxTable[size_t(ERumbleFxId::PlayerBump)], 1.f, ERumblePriority::One, EIOPort::Zero); + x40_rumbleGen.Rumble(RumbleFxTable[size_t(ERumbleFxId::PlayerBump)], 1.f, ERumblePriority::One, EIOPort::Player1); } } } diff --git a/Runtime/MP1/CInGameGuiManager.cpp b/Runtime/MP1/CInGameGuiManager.cpp index da78c5a01..3e0ec29c5 100644 --- a/Runtime/MP1/CInGameGuiManager.cpp +++ b/Runtime/MP1/CInGameGuiManager.cpp @@ -629,7 +629,7 @@ void CInGameGuiManager::ShowPauseGameHudMessage(CStateManager& stateMgr, CAssetI } void CInGameGuiManager::PauseGame(CStateManager& stateMgr, EInGameGuiState state) { - g_InputGenerator->SetMotorState(EIOPort::Zero, EMotorState::Stop); + g_InputGenerator->SetMotorState(EIOPort::Player1, EMotorState::Stop); CSfxManager::SetChannel(CSfxManager::ESfxChannels::PauseScreen); BeginStateTransition(state, stateMgr); } diff --git a/Runtime/MP1/COptionsScreen.cpp b/Runtime/MP1/COptionsScreen.cpp index 60ecff760..9b990c27c 100644 --- a/Runtime/MP1/COptionsScreen.cpp +++ b/Runtime/MP1/COptionsScreen.cpp @@ -85,7 +85,7 @@ void COptionsScreen::OnEnumChanged(CGuiTableGroup* caller, int oldSel) { if (opt == EGameOption::Rumble && caller->GetUserSelection() > 0) { x1a8_rumble.HardStopAll(); - x1a8_rumble.Rumble(RumbleFxTable[size_t(ERumbleFxId::PlayerBump)], 1.f, ERumblePriority::One, EIOPort::Zero); + x1a8_rumble.Rumble(RumbleFxTable[size_t(ERumbleFxId::PlayerBump)], 1.f, ERumblePriority::One, EIOPort::Player1); } CPauseScreenBase::UpdateSideTable(caller);