Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Hazel/src/Hazel/Core/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ namespace Hazel {
m_Window = Window::Create(WindowProps(m_Specification.Name));
m_Window->SetEventCallback(HZ_BIND_EVENT_FN(Application::OnEvent));

Input::Init();
Input::SetEventCallback(HZ_BIND_EVENT_FN(Application::OnEvent));

Renderer::Init();

m_ImGuiLayer = new ImGuiLayer();
Expand Down Expand Up @@ -108,6 +111,8 @@ namespace Hazel {
m_ImGuiLayer->End();
}

Input::Update();

m_Window->OnUpdate();
}
}
Expand Down
34 changes: 34 additions & 0 deletions Hazel/src/Hazel/Core/GamepadCodes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

namespace Hazel
{
const uint32_t MaxGamepadCount = 4;

using GamepadCode = uint16_t;

namespace Gamepad
{
enum : GamepadCode
{
DPadUp,
DPadDown,
DPadLeft,
DPadRight,
Start,
Select,
Back = Select,
A,
B,
X,
Y,
Cross = B,
Square = Y,
Circle = A,
Triangle = X,
LeftThumb,
RightThumb,
LeftShoulder,
RightShoulder
};
}
}
13 changes: 13 additions & 0 deletions Hazel/src/Hazel/Core/Input.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "Hazel/Core/KeyCodes.h"
#include "Hazel/Core/MouseCodes.h"
#include "Hazel/Core/GamepadCodes.h"
#include "Hazel/Events/Event.h"

#include <glm/glm.hpp>

Expand All @@ -10,11 +12,22 @@ namespace Hazel {
class Input
{
public:
static void Init();
static void SetEventCallback(const EventCallbackFn& callback);
static void Update();

static bool IsKeyPressed(KeyCode key);

static bool IsMouseButtonPressed(MouseCode button);
static glm::vec2 GetMousePosition();
static float GetMouseX();
static float GetMouseY();

static bool IsGamepadButtonPressed(uint32_t playerIndex, GamepadCode code);
static bool IsGamepadButtonReleased(uint32_t playerIndex, GamepadCode code);
static glm::vec2 GetGamepadLeftJoystick(uint32_t playerIndex);
static glm::vec2 GetGamepadRightJoystick(uint32_t playerIndex);
static glm::vec2 GetGamepadTriggers(uint32_t playerIndex);
static void SetGamepadVibration(uint32_t playerIndex, const glm::vec2& vibration);
};
}
2 changes: 0 additions & 2 deletions Hazel/src/Hazel/Core/Window.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ namespace Hazel {
class Window
{
public:
using EventCallbackFn = std::function<void(Event&)>;

virtual ~Window() = default;

virtual void OnUpdate() = 0;
Expand Down
7 changes: 5 additions & 2 deletions Hazel/src/Hazel/Events/Event.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ namespace Hazel {
WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved,
AppTick, AppUpdate, AppRender,
KeyPressed, KeyReleased, KeyTyped,
MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled
MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled,
GamepadConnected, GamepadDisconnected
};

enum EventCategory
Expand All @@ -28,7 +29,8 @@ namespace Hazel {
EventCategoryInput = BIT(1),
EventCategoryKeyboard = BIT(2),
EventCategoryMouse = BIT(3),
EventCategoryMouseButton = BIT(4)
EventCategoryMouseButton = BIT(4),
EventCategoryGamepad = BIT(5),
};

#define EVENT_CLASS_TYPE(type) static EventType GetStaticType() { return EventType::type; }\
Expand Down Expand Up @@ -83,5 +85,6 @@ namespace Hazel {
return os << e.ToString();
}

using EventCallbackFn = std::function<void(Event&)>;
}

52 changes: 52 additions & 0 deletions Hazel/src/Hazel/Events/GamepadEvent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once

#include "Hazel/Events/Event.h"

namespace Hazel {

class GamepadEvent : public Event
{
public:
GamepadEvent(uint32_t gamepadIndex)
: m_GamepadIndex(gamepadIndex) {}

uint32_t GetGamepadIndex() { return m_GamepadIndex; }

EVENT_CLASS_CATEGORY(EventCategoryGamepad)
protected:
uint32_t m_GamepadIndex;
};

class GamepadConnectedEvent : public GamepadEvent
{
public:
GamepadConnectedEvent(uint32_t gamepadIndex)
: GamepadEvent(gamepadIndex) {}

std::string ToString() const override
{
std::stringstream ss;
ss << "GamepadConnectedEvent: Player Index = " << m_GamepadIndex;
return ss.str();
}

EVENT_CLASS_TYPE(GamepadConnected)
};

class GamepadDisconnectedEvent : public GamepadEvent
{
public:
GamepadDisconnectedEvent(uint32_t gamepadIndex)
: GamepadEvent(gamepadIndex) {}

std::string ToString() const override
{
std::stringstream ss;
ss << "GamepadDisconnectedEvent: Player Index = " << m_GamepadIndex;
return ss.str();
}

EVENT_CLASS_TYPE(GamepadDisconnected)
};

}
175 changes: 174 additions & 1 deletion Hazel/src/Platform/Windows/WindowsInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,152 @@
#include "Hazel/Core/Application.h"
#include <GLFW/glfw3.h>

#include "Hazel/Events/GamepadEvent.h"

#include <xinput.h>

// Define the layout of the function we're going to load. Make a default one in case we're unable to load it from the dll.
#define XINPUT_GET_STATE(name) DWORD name(DWORD dwUserIndex, XINPUT_STATE* pState)
typedef XINPUT_GET_STATE(xinput_get_state);
XINPUT_GET_STATE(XInputGetStateStub) { return ERROR_DEVICE_NOT_CONNECTED; }
static xinput_get_state* XInputGetState_ = XInputGetStateStub;
#define XInputGetState XInputGetState_

// Same here.
#define XINPUT_SET_STATE(name) DWORD name(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration)
typedef XINPUT_SET_STATE(xinput_set_state);
XINPUT_SET_STATE(XInputSetStateStub) { return ERROR_DEVICE_NOT_CONNECTED; }
static xinput_set_state* XInputSetState_ = XInputSetStateStub;
#define XInputSetState XInputSetState_


namespace Hazel {

const float DeadzoneX = 0.05f;
const float DeadzoneY = 0.02f;

struct GamepadState
{
bool IsConnected;
std::unordered_map<GamepadCode, bool> Buttons;
glm::vec2 LeftJoystick;
glm::vec2 RightJoystick;
glm::vec2 Triggers;
glm::vec2 Vibration;
};

static std::array<GamepadState, MaxGamepadCount> s_Gamepads;
static EventCallbackFn s_EventCallback;

void Input::Init()
{
HZ_PROFILE_FUNCTION();

s_Gamepads = {};

HMODULE Module = LoadLibraryA("xinput1_4.dll");
if (!Module)
{
HZ_CORE_ERROR("Failed to load xinput1_4.dll!");
return;
}

XInputGetState = (xinput_get_state*)GetProcAddress(Module, "XInputGetState");
XInputSetState = (xinput_set_state*)GetProcAddress(Module, "XInputSetState");
}

void Input::SetEventCallback(const EventCallbackFn& callback)
{
s_EventCallback = callback;
}

void Input::Update()
{
HZ_PROFILE_FUNCTION();

for (uint32_t i = 0; i < MaxGamepadCount; i++)
{
XINPUT_STATE state;
ZeroMemory(&state, sizeof(XINPUT_STATE));

DWORD result = XInputGetState(i, &state);
bool connected = (result == ERROR_SUCCESS);

if (s_Gamepads[i].IsConnected != connected)
{
if (connected == true)
{
GamepadConnectedEvent event(i);
s_EventCallback(event);
}
else
{
GamepadDisconnectedEvent event(i);
s_EventCallback(event);
}
}

s_Gamepads[i].IsConnected = connected;
if (connected)
{
// Diamond Buttons
s_Gamepads[i].Buttons[Gamepad::A] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_A);
s_Gamepads[i].Buttons[Gamepad::B] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_B);
s_Gamepads[i].Buttons[Gamepad::X] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_X);
s_Gamepads[i].Buttons[Gamepad::Y] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y);

// DPad Buttons
s_Gamepads[i].Buttons[Gamepad::DPadDown] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN);
s_Gamepads[i].Buttons[Gamepad::DPadUp] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP);
s_Gamepads[i].Buttons[Gamepad::DPadLeft] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT);
s_Gamepads[i].Buttons[Gamepad::DPadRight] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT);

// Navigation buttons
s_Gamepads[i].Buttons[Gamepad::Start] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_START);
s_Gamepads[i].Buttons[Gamepad::Back] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK);

// Shoulder buttons
s_Gamepads[i].Buttons[Gamepad::LeftShoulder] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);
s_Gamepads[i].Buttons[Gamepad::RightShoulder] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);

// Thumbstick buttons
s_Gamepads[i].Buttons[Gamepad::LeftThumb] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB);
s_Gamepads[i].Buttons[Gamepad::RightThumb] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB);

// Triggers
s_Gamepads[i].Triggers.x = (float)state.Gamepad.bLeftTrigger / 255;
s_Gamepads[i].Triggers.y = (float)state.Gamepad.bRightTrigger / 255;

// Left joystick
float normLX = fmaxf(-1, (float)state.Gamepad.sThumbLX / 32767);
float normLY = fmaxf(-1, (float)state.Gamepad.sThumbLY / 32767);

s_Gamepads[i].LeftJoystick.x = (abs(normLX) < DeadzoneX ? 0 : (abs(normLX) - DeadzoneX) * (normLX / abs(normLX)));
s_Gamepads[i].LeftJoystick.y = (abs(normLY) < DeadzoneY ? 0 : (abs(normLY) - DeadzoneY) * (normLY / abs(normLY)));
s_Gamepads[i].LeftJoystick.x *= 1 / (1 - DeadzoneX);
s_Gamepads[i].LeftJoystick.y *= 1 / (1 - DeadzoneY);

// Right joystick
float normRX = fmaxf(-1, (float)state.Gamepad.sThumbRX / 32767);
float normRY = fmaxf(-1, (float)state.Gamepad.sThumbRY / 32767);

s_Gamepads[i].RightJoystick.x = (abs(normRX) < DeadzoneX ? 0 : (abs(normRX) - DeadzoneX) * (normRX / abs(normRX)));
s_Gamepads[i].RightJoystick.y = (abs(normRY) < DeadzoneY ? 0 : (abs(normRY) - DeadzoneY) * (normRY / abs(normRY)));
s_Gamepads[i].RightJoystick.x *= 1 / (1 - DeadzoneX);
s_Gamepads[i].RightJoystick.y *= 1 / (1 - DeadzoneY);

// Set vibration
XINPUT_VIBRATION vibration = {};
vibration.wLeftMotorSpeed = (uint16_t)(s_Gamepads[i].Vibration.x * 65335.0f);
vibration.wRightMotorSpeed = (uint16_t)(s_Gamepads[i].Vibration.y * 65335.0f);
XInputSetState(i, &vibration);

// Reset vibration
s_Gamepads[i].Vibration = glm::vec2(0.0f);
}
}
}

bool Input::IsKeyPressed(const KeyCode key)
{
auto* window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
Expand Down Expand Up @@ -39,4 +183,33 @@ namespace Hazel {
return GetMousePosition().y;
}

}
bool Input::IsGamepadButtonPressed(uint32_t playerIndex, GamepadCode code)
{
return s_Gamepads[playerIndex].Buttons[code] == true;
}

bool Input::IsGamepadButtonReleased(uint32_t playerIndex, GamepadCode code)
{
return s_Gamepads[playerIndex].Buttons[code] == false;
}

glm::vec2 Input::GetGamepadLeftJoystick(uint32_t playerIndex)
{
return s_Gamepads[playerIndex].LeftJoystick;
}

glm::vec2 Input::GetGamepadRightJoystick(uint32_t playerIndex)
{
return s_Gamepads[playerIndex].RightJoystick;
}

glm::vec2 Input::GetGamepadTriggers(uint32_t playerIndex)
{
return s_Gamepads[playerIndex].Triggers;
}

void Input::SetGamepadVibration(uint32_t playerIndex, const glm::vec2& vibration)
{
s_Gamepads[playerIndex].Vibration = vibration;
}
}