SDL2: Added gamepad support.

This commit is contained in:
Branimir Karadžić 2014-12-15 20:58:54 -08:00
parent 504af5216b
commit 73a227cefd
10 changed files with 372 additions and 49 deletions

View file

@ -1111,7 +1111,7 @@ int _main_(int /*_argc*/, char** /*_argv*/)
bgfx::dbgTextPrintf(0, 3, 0x0f, "Frame: % 7.3f[ms]", double(frameTime)*toMs);
// Update camera.
cameraUpdate(deltaTime, mouseState.m_mx, mouseState.m_my, !!mouseState.m_buttons[entry::MouseButton::Right]);
cameraUpdate(deltaTime, mouseState);
cameraGetViewMtx(viewState.m_view);
static float lightTimeAccumulator = 0.0f;

View file

@ -2197,7 +2197,7 @@ int _main_(int /*_argc*/, char** /*_argv*/)
s_uniforms.m_time = time;
// Update camera.
cameraUpdate(deltaTime, mouseState.m_mx, mouseState.m_my, !!mouseState.m_buttons[entry::MouseButton::Right]);
cameraUpdate(deltaTime, mouseState);
// Set view and projection matrix for view 0.
const bgfx::HMD* hmd = bgfx::getHMD();

View file

@ -2239,7 +2239,7 @@ int _main_(int /*_argc*/, char** /*_argv*/)
bgfx::dbgTextPrintf(0, 3, 0x0f, "Frame: % 7.3f[ms]", double(frameTime)*toMs);
// Update camera.
cameraUpdate(deltaTime, mouseState.m_mx, mouseState.m_my, !!mouseState.m_buttons[entry::MouseButton::Right]);
cameraUpdate(deltaTime, mouseState);
// Update view mtx.
cameraGetViewMtx(viewState.m_view);

View file

@ -439,7 +439,7 @@ int _main_(int /*_argc*/, char** /*_argv*/)
imguiEndFrame();
// Update camera.
cameraUpdate(deltaTime, mouseState.m_mx, mouseState.m_my, !!mouseState.m_buttons[entry::MouseButton::Right]);
cameraUpdate(deltaTime, mouseState);
cameraGetViewMtx(view);
// Setup views

View file

@ -17,7 +17,6 @@ extern "C" int _main_(int _argc, char** _argv);
namespace entry
{
const uint16_t WindowHandle::invalidHandle = UINT16_MAX;
static uint32_t s_debug = BGFX_DEBUG_NONE;
static uint32_t s_reset = BGFX_RESET_NONE;
static bool s_exit = false;
@ -112,16 +111,17 @@ namespace entry
static const InputBinding s_bindings[] =
{
{ entry::Key::KeyQ, entry::Modifier::LeftCtrl, 1, cmd, "exit" },
{ entry::Key::F1, entry::Modifier::None, 1, cmd, "graphics stats" },
{ entry::Key::F1, entry::Modifier::LeftShift, 1, cmd, "graphics stats 0\ngraphics text 0" },
{ entry::Key::F3, entry::Modifier::None, 1, cmd, "graphics wireframe" },
{ entry::Key::F4, entry::Modifier::None, 1, cmd, "graphics hmd" },
{ entry::Key::F4, entry::Modifier::LeftShift, 1, cmd, "graphics hmdrecenter" },
{ entry::Key::F4, entry::Modifier::LeftCtrl, 1, cmd, "graphics hmddbg" },
{ entry::Key::F7, entry::Modifier::None, 1, cmd, "graphics vsync" },
{ entry::Key::F8, entry::Modifier::None, 1, cmd, "graphics msaa" },
{ entry::Key::Print, entry::Modifier::None, 1, cmd, "graphics screenshot" },
{ entry::Key::KeyQ, entry::Modifier::LeftCtrl, 1, cmd, "exit" },
{ entry::Key::F1, entry::Modifier::None, 1, cmd, "graphics stats" },
{ entry::Key::GamepadStart, entry::Modifier::None, 1, cmd, "graphics stats" },
{ entry::Key::F1, entry::Modifier::LeftShift, 1, cmd, "graphics stats 0\ngraphics text 0" },
{ entry::Key::F3, entry::Modifier::None, 1, cmd, "graphics wireframe" },
{ entry::Key::F4, entry::Modifier::None, 1, cmd, "graphics hmd" },
{ entry::Key::F4, entry::Modifier::LeftShift, 1, cmd, "graphics hmdrecenter" },
{ entry::Key::F4, entry::Modifier::LeftCtrl, 1, cmd, "graphics hmddbg" },
{ entry::Key::F7, entry::Modifier::None, 1, cmd, "graphics vsync" },
{ entry::Key::F8, entry::Modifier::None, 1, cmd, "graphics msaa" },
{ entry::Key::Print, entry::Modifier::None, 1, cmd, "graphics screenshot" },
INPUT_BINDING_END
};
@ -157,6 +157,16 @@ namespace entry
return result;
}
static const char* s_gamepadAxisName[GamepadAxis::Count] =
{
"LeftX",
"LeftY",
"LeftZ",
"RightX",
"RightY",
"RightZ",
};
bool processEvents(uint32_t& _width, uint32_t& _height, uint32_t& _debug, uint32_t& _reset, MouseState* _mouse)
{
s_debug = _debug;
@ -176,6 +186,20 @@ namespace entry
{
switch (ev->m_type)
{
case Event::Axis:
{
const AxisEvent* axis = static_cast<const AxisEvent*>(ev);
inputSetGamepadAxis(axis->m_gamepad, axis->m_axis, axis->m_value);
}
break;
case Event::Char:
{
const CharEvent* chev = static_cast<const CharEvent*>(ev);
inputChar(chev->m_len, chev->m_char);
}
break;
case Event::Exit:
return true;
@ -219,13 +243,6 @@ namespace entry
}
break;
case Event::Char:
{
const CharEvent* chev = static_cast<const CharEvent*>(ev);
inputChar(chev->m_len, chev->m_char);
}
break;
case Event::Size:
{
const SizeEvent* size = static_cast<const SizeEvent*>(ev);
@ -302,6 +319,21 @@ namespace entry
switch (ev->m_type)
{
case Event::Axis:
{
const AxisEvent* axis = static_cast<const AxisEvent*>(ev);
inputSetGamepadAxis(axis->m_gamepad, axis->m_axis, axis->m_value);
}
break;
case Event::Char:
{
const CharEvent* chev = static_cast<const CharEvent*>(ev);
win.m_handle = chev->m_handle;
inputChar(chev->m_len, chev->m_char);
}
break;
case Event::Exit:
return true;
@ -344,14 +376,6 @@ namespace entry
}
break;
case Event::Char:
{
const CharEvent* chev = static_cast<const CharEvent*>(ev);
win.m_handle = chev->m_handle;
inputChar(chev->m_len, chev->m_char);
}
break;
case Event::Size:
{
const SizeEvent* size = static_cast<const SizeEvent*>(ev);

View file

@ -20,8 +20,11 @@ extern "C" int _main_(int _argc, char** _argv);
namespace entry
{
struct WindowHandle { uint16_t idx; static const uint16_t invalidHandle; };
inline bool isValid(WindowHandle _handle) { return WindowHandle::invalidHandle != _handle.idx; }
struct WindowHandle { uint16_t idx; };
inline bool isValid(WindowHandle _handle) { return UINT16_MAX != _handle.idx; }
struct GamepadHandle { uint16_t idx; };
inline bool isValid(GamepadHandle _handle) { return UINT16_MAX != _handle.idx; }
struct MouseButton
{
@ -36,6 +39,21 @@ namespace entry
};
};
struct GamepadAxis
{
enum Enum
{
LeftX,
LeftY,
LeftZ,
RightX,
RightY,
RightZ,
Count
};
};
struct Modifier
{
enum Enum
@ -132,7 +150,22 @@ namespace entry
KeyY,
KeyZ,
Count,
GamepadA,
GamepadB,
GamepadX,
GamepadY,
GamepadThumbL,
GamepadThumbR,
GamepadShoulderL,
GamepadShoulderR,
GamepadUp,
GamepadDown,
GamepadLeft,
GamepadRight,
GamepadBack,
GamepadStart,
Count
};
};
@ -155,6 +188,16 @@ namespace entry
uint8_t m_buttons[entry::MouseButton::Count];
};
struct GamepadState
{
GamepadState()
{
memset(m_axis, 0, sizeof(m_axis) );
}
int32_t m_axis[entry::GamepadAxis::Count];
};
bool processEvents(uint32_t& _width, uint32_t& _height, uint32_t& _debug, uint32_t& _reset, MouseState* _mouse = NULL);
bx::FileReaderI* getFileReader();

View file

@ -26,6 +26,10 @@
# define ENTRY_CONFIG_MAX_WINDOWS 8
#endif // ENTRY_CONFIG_MAX_WINDOWS
#ifndef ENTRY_CONFIG_MAX_GAMEPADS
# define ENTRY_CONFIG_MAX_GAMEPADS 4
#endif // ENTRY_CONFIG_MAX_GAMEPADS
#if !defined(ENTRY_DEFAULT_WIDTH) && !defined(ENTRY_DEFAULT_HEIGHT)
# define ENTRY_DEFAULT_WIDTH 1280
# define ENTRY_DEFAULT_HEIGHT 720
@ -44,9 +48,10 @@ namespace entry
{
enum Enum
{
Axis,
Char,
Exit,
Key,
Char,
Mouse,
Size,
Window,
@ -68,13 +73,13 @@ namespace entry
WindowHandle m_handle;
};
struct KeyEvent : public Event
struct AxisEvent : public Event
{
ENTRY_IMPLEMENT_EVENT(KeyEvent, Event::Key);
ENTRY_IMPLEMENT_EVENT(AxisEvent, Event::Axis);
Key::Enum m_key;
uint8_t m_modifiers;
bool m_down;
GamepadAxis::Enum m_axis;
int32_t m_value;
GamepadHandle m_gamepad;
};
struct CharEvent : public Event
@ -97,6 +102,15 @@ namespace entry
bool m_move;
};
struct KeyEvent : public Event
{
ENTRY_IMPLEMENT_EVENT(KeyEvent, Event::Key);
Key::Enum m_key;
uint8_t m_modifiers;
bool m_down;
};
struct SizeEvent : public Event
{
ENTRY_IMPLEMENT_EVENT(SizeEvent, Event::Size);
@ -119,6 +133,23 @@ namespace entry
class EventQueue
{
public:
void postAxisEvent(WindowHandle _handle, GamepadHandle _gamepad, GamepadAxis::Enum _axis, int32_t _value)
{
AxisEvent* ev = new AxisEvent(_handle);
ev->m_gamepad = _gamepad;
ev->m_axis = _axis;
ev->m_value = _value;
m_queue.push(ev);
}
void postCharEvent(WindowHandle _handle, uint8_t _len, const uint8_t _char[4])
{
CharEvent* ev = new CharEvent(_handle);
ev->m_len = _len;
memcpy(ev->m_char, _char, 4);
m_queue.push(ev);
}
void postExitEvent()
{
Event* ev = new Event(Event::Exit);
@ -134,14 +165,6 @@ namespace entry
m_queue.push(ev);
}
void postCharEvent(WindowHandle _handle, uint8_t _len, const uint8_t _char[4])
{
CharEvent* ev = new CharEvent(_handle);
ev->m_len = _len;
memcpy(ev->m_char, _char, 4);
m_queue.push(ev);
}
void postMouseEvent(WindowHandle _handle, int32_t _mx, int32_t _my, int32_t _mz)
{
MouseEvent* ev = new MouseEvent(_handle);

View file

@ -49,6 +49,80 @@ namespace entry
return (Key::Enum)s_translateKey[_sdl&0xff];
}
static uint8_t s_translateGamepad[256];
static void initTranslateGamepad(uint8_t _sdl, Key::Enum _button)
{
s_translateGamepad[_sdl] = _button;
}
static Key::Enum translateGamepad(uint8_t _sdl)
{
return Key::Enum(s_translateGamepad[_sdl]);
}
static uint8_t s_translateGamepadAxis[256];
static void initTranslateGamepadAxis(uint8_t _sdl, GamepadAxis::Enum _axis)
{
s_translateGamepadAxis[_sdl] = uint8_t(_axis);
}
static GamepadAxis::Enum translateGamepadAxis(uint8_t _sdl)
{
return GamepadAxis::Enum(s_translateGamepadAxis[_sdl]);
}
struct GamepadSDL
{
GamepadSDL()
: m_controller(NULL)
, m_jid(INT32_MAX)
{
memset(m_value, 0, sizeof(m_value) );
// Deadzone values from xinput.h
m_deadzone[GamepadAxis::LeftX ] =
m_deadzone[GamepadAxis::LeftY ] = 7849;
m_deadzone[GamepadAxis::RightX] =
m_deadzone[GamepadAxis::RightY] = 8689;
m_deadzone[GamepadAxis::LeftZ ] =
m_deadzone[GamepadAxis::RightZ] = 30;
}
void create(int32_t _jid)
{
m_controller = SDL_GameControllerOpen(_jid);
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(m_controller);
m_jid = SDL_JoystickInstanceID(joystick);
}
void destroy()
{
SDL_GameControllerClose(m_controller);
m_controller = NULL;
m_jid = INT32_MAX;
}
bool filter(GamepadAxis::Enum _axis, int32_t* _value)
{
const int32_t old = m_value[_axis];
const int32_t deadzone = m_deadzone[_axis];
int32_t value = *_value;
value = value > deadzone || value < -deadzone ? value : 0;
m_value[_axis] = value;
*_value = value;
return old != value;
}
int32_t m_value[GamepadAxis::Count];
int32_t m_deadzone[GamepadAxis::Count];
SDL_GameController* m_controller;
// SDL_Haptic* m_haptic;
SDL_JoystickID m_jid;
};
struct MainThreadEntry
{
int m_argc;
@ -213,6 +287,30 @@ namespace entry
initTranslateKey(SDL_SCANCODE_X, Key::KeyX);
initTranslateKey(SDL_SCANCODE_Y, Key::KeyY);
initTranslateKey(SDL_SCANCODE_Z, Key::KeyZ);
memset(s_translateGamepad, uint8_t(Key::Count), sizeof(s_translateGamepad) );
initTranslateGamepad(SDL_CONTROLLER_BUTTON_A, Key::GamepadA);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_B, Key::GamepadB);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_X, Key::GamepadX);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_Y, Key::GamepadY);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_LEFTSTICK, Key::GamepadThumbL);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_RIGHTSTICK, Key::GamepadThumbR);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, Key::GamepadShoulderL);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, Key::GamepadShoulderR);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_DPAD_UP, Key::GamepadUp);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_DPAD_DOWN, Key::GamepadDown);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_DPAD_LEFT, Key::GamepadLeft);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_DPAD_RIGHT, Key::GamepadRight);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_BACK, Key::GamepadBack);
initTranslateGamepad(SDL_CONTROLLER_BUTTON_START, Key::GamepadStart);
memset(s_translateGamepadAxis, uint8_t(GamepadAxis::Count), sizeof(s_translateGamepadAxis) );
initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_LEFTX, GamepadAxis::LeftX);
initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_LEFTY, GamepadAxis::LeftY);
initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_TRIGGERLEFT, GamepadAxis::LeftZ);
initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_RIGHTX, GamepadAxis::RightX);
initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_RIGHTY, GamepadAxis::RightY);
initTranslateGamepadAxis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT, GamepadAxis::RightZ);
}
void run(int _argc, char** _argv)
@ -220,7 +318,10 @@ namespace entry
m_mte.m_argc = _argc;
m_mte.m_argv = _argv;
SDL_Init(SDL_INIT_VIDEO);
SDL_Init(0
| SDL_INIT_VIDEO
| SDL_INIT_GAMECONTROLLER
);
m_windowAlloc.alloc();
m_window[0] = SDL_CreateWindow("bgfx"
@ -248,6 +349,12 @@ namespace entry
WindowHandle defaultWindow = { 0 };
setWindowSize(defaultWindow, m_width, m_height, true);
SDL_RWops* rw = SDL_RWFromFile("gamecontrollerdb.txt", "rb");
if (NULL != rw)
{
SDL_GameControllerAddMappingsFromRW(rw, 1);
}
bool exit = false;
SDL_Event event;
while (!exit)
@ -355,6 +462,68 @@ namespace entry
}
break;
case SDL_CONTROLLERAXISMOTION:
{
const SDL_ControllerAxisEvent& aev = event.caxis;
GamepadHandle handle = findGamepad(aev.which);
if (isValid(handle) )
{
GamepadAxis::Enum axis = translateGamepadAxis(aev.axis);
int32_t value = aev.value;
if (m_gamepad[handle.idx].filter(axis, &value) )
{
m_eventQueue.postAxisEvent(defaultWindow, handle, axis, value);
}
}
}
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
{
const SDL_ControllerButtonEvent& bev = event.cbutton;
GamepadHandle handle = findGamepad(bev.which);
if (isValid(handle) )
{
Key::Enum key = translateGamepad(bev.button);
if (Key::Count != key)
{
m_eventQueue.postKeyEvent(defaultWindow, key, 0, event.type == SDL_CONTROLLERBUTTONDOWN);
}
}
}
break;
case SDL_CONTROLLERDEVICEADDED:
{
const SDL_ControllerDeviceEvent& cev = event.cdevice;
GamepadHandle handle = { m_gamepadAlloc.alloc() };
if (isValid(handle) )
{
m_gamepad[handle.idx].create(cev.which);
}
}
break;
case SDL_CONTROLLERDEVICEREMAPPED:
{
}
break;
case SDL_CONTROLLERDEVICEREMOVED:
{
const SDL_ControllerDeviceEvent& cev = event.cdevice;
GamepadHandle handle = findGamepad(cev.which);
if (isValid(handle) )
{
m_gamepad[handle.idx].destroy();
m_gamepadAlloc.free(handle.idx);
}
}
break;
default:
{
const SDL_UserEvent& uev = event.user;
@ -515,6 +684,22 @@ namespace entry
}
}
GamepadHandle findGamepad(SDL_JoystickID _jid)
{
for (uint32_t ii = 0, num = m_gamepadAlloc.getNumHandles(); ii < num; ++ii)
{
uint16_t idx = m_gamepadAlloc.getHandleAt(ii);
if (_jid == m_gamepad[idx].m_jid)
{
GamepadHandle handle = { idx };
return handle;
}
}
GamepadHandle invalid = { UINT16_MAX };
return invalid;
}
MainThreadEntry m_mte;
bx::Thread m_thread;
@ -525,6 +710,9 @@ namespace entry
SDL_Window* m_window[ENTRY_CONFIG_MAX_WINDOWS];
uint32_t m_flags[ENTRY_CONFIG_MAX_WINDOWS];
bx::HandleAllocT<ENTRY_CONFIG_MAX_GAMEPADS> m_gamepadAlloc;
GamepadSDL m_gamepad[ENTRY_CONFIG_MAX_GAMEPADS];
uint32_t m_width;
uint32_t m_height;
float m_aspectRatio;

View file

@ -139,6 +139,31 @@ struct Keyboard
uint8_t m_char[256];
};
struct Gamepad
{
Gamepad()
{
reset();
}
void reset()
{
memset(m_axis, 0, sizeof(m_axis) );
}
void setAxis(entry::GamepadAxis::Enum _axis, int32_t _value)
{
m_axis[_axis] = _value;
}
int32_t getAxis(entry::GamepadAxis::Enum _axis)
{
return m_axis[_axis];
}
int32_t m_axis[entry::GamepadAxis::Count];
};
struct Input
{
Input()
@ -211,12 +236,17 @@ struct Input
{
m_mouse.reset();
m_keyboard.reset();
for (uint32_t ii = 0; ii < BX_COUNTOF(m_gamepad); ++ii)
{
m_gamepad[ii].reset();
}
}
typedef stl::unordered_map<const char*, const InputBinding*> InputBindingMap;
InputBindingMap m_inputBindingsMap;
Mouse m_mouse;
Keyboard m_keyboard;
Gamepad m_gamepad[ENTRY_CONFIG_MAX_GAMEPADS];
};
static Input s_input;
@ -301,3 +331,13 @@ void inputSetMouseLock(bool _lock)
}
}
}
void inputSetGamepadAxis(entry::GamepadHandle _handle, entry::GamepadAxis::Enum _axis, int32_t _value)
{
s_input.m_gamepad[_handle.idx].setAxis(_axis, _value);
}
int32_t inputGetGamepadAxis(entry::GamepadHandle _handle, entry::GamepadAxis::Enum _axis)
{
return s_input.m_gamepad[_handle.idx].getAxis(_axis);
}

View file

@ -6,7 +6,6 @@
#ifndef INPUT_H_HEADER_GUARD
#define INPUT_H_HEADER_GUARD
#include <stdint.h>
#include "entry.h"
typedef void (*InputBindingFn)(const void* _userData);
@ -61,4 +60,10 @@ void inputGetMouse(float _mouse[3]);
///
bool inputIsMouseLocked();
///
void inputSetGamepadAxis(entry::GamepadHandle _handle, entry::GamepadAxis::Enum _axis, int32_t _value);
///
int32_t inputGetGamepadAxis(entry::GamepadHandle _handle, entry::GamepadAxis::Enum _axis);
#endif // INPUT_H_HEADER_GUARD