#ifdef WIN32
#include "Base.h"
#include "Platform.h"
#include "FileSystem.h"
#include "Game.h"
#include "Form.h"
#include "Vector2.h"
#include "ScriptController.h"
#include <GL/wglew.h>
#include <windowsx.h>
#ifdef USE_XINPUT
#include <XInput.h>
#endif
using gameplay::print;
// Window defaults
#define DEFAULT_RESOLUTION_X 1024
#define DEFAULT_RESOLUTION_Y 768
#define DEFAULT_COLOR_BUFFER_SIZE 32
#define DEFAULT_DEPTH_BUFFER_SIZE 24
#define DEFAULT_STENCIL_BUFFER_SIZE 8
static double __timeTicksPerMillis;
static double __timeStart;
static double __timeAbsolute;
static bool __vsync = WINDOW_VSYNC;
static float __roll;
static float __pitch;
static HINSTANCE __hinstance = 0;
static HWND __attachToWindow = 0;
static HWND __hwnd = 0;
static HDC __hdc = 0;
static HGLRC __hrc = 0;
static bool __mouseCaptured = false;
static POINT __mouseCapturePoint = { 0, 0 };
static bool __cursorVisible = true;
static unsigned int __gamepadsConnected = 0;
#ifdef USE_XINPUT
struct XInputGamepad
{
static const unsigned int BUTTON_COUNT = 14;
static const unsigned int JOYSTICK_COUNT = 2;
static const unsigned int TRIGGER_COUNT = 2;
std::string id;
int handle;
bool connected;
XINPUT_STATE state;
};
static XInputGamepad* __xinputGamepads = NULL;
static DWORD getXInputGamepadButtonMask(unsigned int buttonHandle)
{
switch(buttonHandle)
{
case 0:
return XINPUT_GAMEPAD_DPAD_UP;
case 1:
return XINPUT_GAMEPAD_DPAD_DOWN;
case 2:
return XINPUT_GAMEPAD_DPAD_LEFT;
case 3:
return XINPUT_GAMEPAD_DPAD_RIGHT;
case 4:
return XINPUT_GAMEPAD_START;
case 5:
return XINPUT_GAMEPAD_BACK;
case 6:
return XINPUT_GAMEPAD_LEFT_THUMB;
case 7:
return XINPUT_GAMEPAD_RIGHT_THUMB;
case 8:
return XINPUT_GAMEPAD_LEFT_SHOULDER;
case 9:
return XINPUT_GAMEPAD_RIGHT_SHOULDER;
case 10:
return XINPUT_GAMEPAD_A;
case 11:
return XINPUT_GAMEPAD_B;
case 12:
return XINPUT_GAMEPAD_X;
case 13:
return XINPUT_GAMEPAD_Y;
default:
return 0;
}
}
static bool getXInputState(unsigned long gamepadHandle)
{
GP_ASSERT(0 <= gamepadHandle && gamepadHandle < XUSER_MAX_COUNT);
if (XInputGetState((DWORD)gamepadHandle, &__xinputGamepads[gamepadHandle].state) == ERROR_SUCCESS)
return (__xinputGamepads[gamepadHandle].connected = true);
else
return (__xinputGamepads[gamepadHandle].connected = false);
}
static bool getXInputButtonState(unsigned long gamepadHandle, unsigned long buttonHandle)
{
GP_ASSERT(0 <= gamepadHandle && gamepadHandle < XUSER_MAX_COUNT);
GP_ASSERT(0 <= buttonHandle && buttonHandle < 14);
WORD buttonMask = getXInputGamepadButtonMask(buttonHandle); // Conversion to button mask.
if ((__xinputGamepads[gamepadHandle].state.Gamepad.wButtons & buttonMask) == buttonMask)
return true;
else
return false;
}
static float normalizeXInputJoystickAxis(int axisValue, int deadZone)
{
int absAxisValue = abs(axisValue);
if (absAxisValue < deadZone)
{
return 0.0f;
}
else
{
int value = axisValue;
if (value < 0)
value = -1;
else if (value > 0)
value = 1;
else
value = 0;
return value * (absAxisValue - deadZone) / (float)(32768 - deadZone);
}
}
static float getXInputJoystickAxisX(unsigned long gamepadHandle, unsigned long joystickHandle)
{
GP_ASSERT(0 <= gamepadHandle && gamepadHandle < XUSER_MAX_COUNT);
GP_ASSERT(0 <= joystickHandle && joystickHandle < 2);
switch(joystickHandle)
{
case 0:
return normalizeXInputJoystickAxis(__xinputGamepads[gamepadHandle].state.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
case 1:
return normalizeXInputJoystickAxis(__xinputGamepads[gamepadHandle].state.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
default: return 0.0f;
}
}
static float getXInputJoystickAxisY(unsigned long gamepadHandle, unsigned long joystickHandle)
{
GP_ASSERT(0 <= gamepadHandle && gamepadHandle < XUSER_MAX_COUNT);
GP_ASSERT(0 <= joystickHandle && joystickHandle < 2);
switch(joystickHandle)
{
case 0:
return normalizeXInputJoystickAxis(__xinputGamepads[gamepadHandle].state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
case 1:
return normalizeXInputJoystickAxis(__xinputGamepads[gamepadHandle].state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
default: return 0.0f;
}
}
static void getXInputJoystickAxisValues(unsigned long gamepadHandle, unsigned long joystickHandle, gameplay::Vector2* outValue)
{
GP_ASSERT(0 <= gamepadHandle && gamepadHandle < XUSER_MAX_COUNT);
GP_ASSERT(0 <= joystickHandle && joystickHandle < 2);
switch(joystickHandle)
{
case 0:
outValue->x = normalizeXInputJoystickAxis(__xinputGamepads[gamepadHandle].state.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
outValue->y = normalizeXInputJoystickAxis(__xinputGamepads[gamepadHandle].state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
break;
case 1:
outValue->x = normalizeXInputJoystickAxis(__xinputGamepads[gamepadHandle].state.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
outValue->y = normalizeXInputJoystickAxis(__xinputGamepads[gamepadHandle].state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
break;
}
}
#endif
static gameplay::Keyboard::Key getKey(WPARAM win32KeyCode, bool shiftDown)
{
switch (win32KeyCode)
{
case VK_PAUSE:
return gameplay::Keyboard::KEY_PAUSE;
case VK_SCROLL:
return gameplay::Keyboard::KEY_SCROLL_LOCK;
case VK_PRINT:
return gameplay::Keyboard::KEY_PRINT;
case VK_ESCAPE:
return gameplay::Keyboard::KEY_ESCAPE;
case VK_BACK:
return gameplay::Keyboard::KEY_BACKSPACE;
case VK_TAB:
return shiftDown ? gameplay::Keyboard::KEY_BACK_TAB : gameplay::Keyboard::KEY_TAB;
case VK_RETURN:
return gameplay::Keyboard::KEY_RETURN;
case VK_CAPITAL:
return gameplay::Keyboard::KEY_CAPS_LOCK;
case VK_SHIFT:
return gameplay::Keyboard::KEY_SHIFT;
case VK_CONTROL:
return gameplay::Keyboard::KEY_CTRL;
case VK_MENU:
return gameplay::Keyboard::KEY_ALT;
case VK_APPS:
return gameplay::Keyboard::KEY_MENU;
case VK_LSHIFT:
return gameplay::Keyboard::KEY_SHIFT;
case VK_RSHIFT:
return gameplay::Keyboard::KEY_SHIFT;
case VK_LCONTROL:
return gameplay::Keyboard::KEY_CTRL;
case VK_RCONTROL:
return gameplay::Keyboard::KEY_CTRL;
case VK_LMENU:
return gameplay::Keyboard::KEY_ALT;
case VK_RMENU:
return gameplay::Keyboard::KEY_ALT;
case VK_LWIN:
case VK_RWIN:
return gameplay::Keyboard::KEY_HYPER;
case VK_BROWSER_SEARCH:
return gameplay::Keyboard::KEY_SEARCH;
case VK_INSERT:
return gameplay::Keyboard::KEY_INSERT;
case VK_HOME:
return gameplay::Keyboard::KEY_HOME;
case VK_PRIOR:
return gameplay::Keyboard::KEY_PG_UP;
case VK_DELETE:
return gameplay::Keyboard::KEY_DELETE;
case VK_END:
return gameplay::Keyboard::KEY_END;
case VK_NEXT:
return gameplay::Keyboard::KEY_PG_DOWN;
case VK_LEFT:
return gameplay::Keyboard::KEY_LEFT_ARROW;
case VK_RIGHT:
return gameplay::Keyboard::KEY_RIGHT_ARROW;
case VK_UP:
return gameplay::Keyboard::KEY_UP_ARROW;
case VK_DOWN:
return gameplay::Keyboard::KEY_DOWN_ARROW;
case VK_NUMLOCK:
return gameplay::Keyboard::KEY_NUM_LOCK;
case VK_ADD:
return gameplay::Keyboard::KEY_KP_PLUS;
case VK_SUBTRACT:
return gameplay::Keyboard::KEY_KP_MINUS;
case VK_MULTIPLY:
return gameplay::Keyboard::KEY_KP_MULTIPLY;
case VK_DIVIDE:
return gameplay::Keyboard::KEY_KP_DIVIDE;
case VK_NUMPAD7:
return gameplay::Keyboard::KEY_KP_HOME;
case VK_NUMPAD8:
return gameplay::Keyboard::KEY_KP_UP;
case VK_NUMPAD9:
return gameplay::Keyboard::KEY_KP_PG_UP;
case VK_NUMPAD4:
return gameplay::Keyboard::KEY_KP_LEFT;
case VK_NUMPAD5:
return gameplay::Keyboard::KEY_KP_FIVE;
case VK_NUMPAD6:
return gameplay::Keyboard::KEY_KP_RIGHT;
case VK_NUMPAD1:
return gameplay::Keyboard::KEY_KP_END;
case VK_NUMPAD2:
return gameplay::Keyboard::KEY_KP_DOWN;
case VK_NUMPAD3:
return gameplay::Keyboard::KEY_KP_PG_DOWN;
case VK_NUMPAD0:
return gameplay::Keyboard::KEY_KP_INSERT;
case VK_DECIMAL:
return gameplay::Keyboard::KEY_KP_DELETE;
case VK_F1:
return gameplay::Keyboard::KEY_F1;
case VK_F2:
return gameplay::Keyboard::KEY_F2;
case VK_F3:
return gameplay::Keyboard::KEY_F3;
case VK_F4:
return gameplay::Keyboard::KEY_F4;
case VK_F5:
return gameplay::Keyboard::KEY_F5;
case VK_F6:
return gameplay::Keyboard::KEY_F6;
case VK_F7:
return gameplay::Keyboard::KEY_F7;
case VK_F8:
return gameplay::Keyboard::KEY_F8;
case VK_F9:
return gameplay::Keyboard::KEY_F9;
case VK_F10:
return gameplay::Keyboard::KEY_F10;
case VK_F11:
return gameplay::Keyboard::KEY_F11;
case VK_F12:
return gameplay::Keyboard::KEY_F12;
case VK_SPACE:
return gameplay::Keyboard::KEY_SPACE;
case 0x30:
return shiftDown ? gameplay::Keyboard::KEY_RIGHT_PARENTHESIS : gameplay::Keyboard::KEY_ZERO;
case 0x31:
return shiftDown ? gameplay::Keyboard::KEY_EXCLAM : gameplay::Keyboard::KEY_ONE;
case 0x32:
return shiftDown ? gameplay::Keyboard::KEY_AT : gameplay::Keyboard::KEY_TWO;
case 0x33:
return shiftDown ? gameplay::Keyboard::KEY_NUMBER : gameplay::Keyboard::KEY_THREE;
case 0x34:
return shiftDown ? gameplay::Keyboard::KEY_DOLLAR : gameplay::Keyboard::KEY_FOUR;
case 0x35:
return shiftDown ? gameplay::Keyboard::KEY_PERCENT : gameplay::Keyboard::KEY_FIVE;
case 0x36:
return shiftDown ? gameplay::Keyboard::KEY_CIRCUMFLEX : gameplay::Keyboard::KEY_SIX;
case 0x37:
return shiftDown ? gameplay::Keyboard::KEY_AMPERSAND : gameplay::Keyboard::KEY_SEVEN;
case 0x38:
return shiftDown ? gameplay::Keyboard::KEY_ASTERISK : gameplay::Keyboard::KEY_EIGHT;
case 0x39:
return shiftDown ? gameplay::Keyboard::KEY_LEFT_PARENTHESIS : gameplay::Keyboard::KEY_NINE;
case VK_OEM_PLUS:
return shiftDown ? gameplay::Keyboard::KEY_EQUAL : gameplay::Keyboard::KEY_PLUS;
case VK_OEM_COMMA:
return shiftDown ? gameplay::Keyboard::KEY_LESS_THAN : gameplay::Keyboard::KEY_COMMA;
case VK_OEM_MINUS:
return shiftDown ? gameplay::Keyboard::KEY_UNDERSCORE : gameplay::Keyboard::KEY_MINUS;
case VK_OEM_PERIOD:
return shiftDown ? gameplay::Keyboard::KEY_GREATER_THAN : gameplay::Keyboard::KEY_PERIOD;
case VK_OEM_1:
return shiftDown ? gameplay::Keyboard::KEY_COLON : gameplay::Keyboard::KEY_SEMICOLON;
case VK_OEM_2:
return shiftDown ? gameplay::Keyboard::KEY_QUESTION : gameplay::Keyboard::KEY_SLASH;
case VK_OEM_3:
return shiftDown ? gameplay::Keyboard::KEY_TILDE : gameplay::Keyboard::KEY_GRAVE;
case VK_OEM_4:
return shiftDown ? gameplay::Keyboard::KEY_LEFT_BRACE : gameplay::Keyboard::KEY_LEFT_BRACKET;
case VK_OEM_5:
return shiftDown ? gameplay::Keyboard::KEY_BAR : gameplay::Keyboard::KEY_BACK_SLASH;
case VK_OEM_6:
return shiftDown ? gameplay::Keyboard::KEY_RIGHT_BRACE : gameplay::Keyboard::KEY_RIGHT_BRACKET;
case VK_OEM_7:
return shiftDown ? gameplay::Keyboard::KEY_QUOTE : gameplay::Keyboard::KEY_APOSTROPHE;
case 0x41:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_A : gameplay::Keyboard::KEY_A;
case 0x42:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_B : gameplay::Keyboard::KEY_B;
case 0x43:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_C : gameplay::Keyboard::KEY_C;
case 0x44:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_D : gameplay::Keyboard::KEY_D;
case 0x45:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_E : gameplay::Keyboard::KEY_E;
case 0x46:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_F : gameplay::Keyboard::KEY_F;
case 0x47:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_G : gameplay::Keyboard::KEY_G;
case 0x48:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_H : gameplay::Keyboard::KEY_H;
case 0x49:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_I : gameplay::Keyboard::KEY_I;
case 0x4A:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_J : gameplay::Keyboard::KEY_J;
case 0x4B:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_K : gameplay::Keyboard::KEY_K;
case 0x4C:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_L : gameplay::Keyboard::KEY_L;
case 0x4D:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_M : gameplay::Keyboard::KEY_M;
case 0x4E:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_N : gameplay::Keyboard::KEY_N;
case 0x4F:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_O : gameplay::Keyboard::KEY_O;
case 0x50:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_P : gameplay::Keyboard::KEY_P;
case 0x51:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_Q : gameplay::Keyboard::KEY_Q;
case 0x52:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_R : gameplay::Keyboard::KEY_R;
case 0x53:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_S : gameplay::Keyboard::KEY_S;
case 0x54:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_T : gameplay::Keyboard::KEY_T;
case 0x55:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_U : gameplay::Keyboard::KEY_U;
case 0x56:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_V : gameplay::Keyboard::KEY_V;
case 0x57:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_W : gameplay::Keyboard::KEY_W;
case 0x58:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_X : gameplay::Keyboard::KEY_X;
case 0x59:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_Y : gameplay::Keyboard::KEY_Y;
case 0x5A:
return shiftDown ? gameplay::Keyboard::KEY_CAPITAL_Z : gameplay::Keyboard::KEY_Z;
default:
return gameplay::Keyboard::KEY_NONE;
}
}
void UpdateCapture(LPARAM lParam)
{
if ((lParam & MK_LBUTTON) || (lParam & MK_MBUTTON) || (lParam & MK_RBUTTON))
SetCapture(__hwnd);
else
ReleaseCapture();
}
void WarpMouse(int clientX, int clientY)
{
POINT p = { clientX, clientY };
ClientToScreen(__hwnd, &p);
SetCursorPos(p.x, p.y);
}
LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static gameplay::Game* game = gameplay::Game::getInstance();
if (!game->isInitialized() || hwnd != __hwnd)
{
// Ignore messages that are not for our game window.
return DefWindowProc(hwnd, msg, wParam, lParam);
}
static int lx, ly;
static bool shiftDown = false;
static bool capsOn = false;
switch (msg)
{
case WM_CLOSE:
DestroyWindow(__hwnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_LBUTTONDOWN:
{
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
UpdateCapture(wParam);
if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON, x, y, 0))
{
gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_PRESS, x, y, 0);
}
return 0;
}
case WM_LBUTTONUP:
{
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_LEFT_BUTTON, x, y, 0))
{
gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_RELEASE, x, y, 0);
}
UpdateCapture(wParam);
return 0;
}
case WM_RBUTTONDOWN:
UpdateCapture(wParam);
lx = GET_X_LPARAM(lParam);
ly = GET_Y_LPARAM(lParam);
gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_PRESS_RIGHT_BUTTON, lx, ly, 0);
break;
case WM_RBUTTONUP:
gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
UpdateCapture(wParam);
break;
case WM_MBUTTONDOWN:
UpdateCapture(wParam);
gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
break;
case WM_MBUTTONUP:
gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
UpdateCapture(wParam);
break;
case WM_MOUSEMOVE:
{
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
if (__mouseCaptured)
{
// If the incoming position is the mouse capture point, ignore this event
// since this is the event that warped the cursor back.
if (x == __mouseCapturePoint.x && y == __mouseCapturePoint.y)
break;
// Convert to deltas
x -= __mouseCapturePoint.x;
y -= __mouseCapturePoint.y;
// Warp mouse back to center of screen.
WarpMouse(__mouseCapturePoint.x, __mouseCapturePoint.y);
}
// Allow Game::mouseEvent a chance to handle (and possibly consume) the event.
if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
{
if ((wParam & MK_LBUTTON) == MK_LBUTTON)
{
// Mouse move events should be interpreted as touch move only if left mouse is held and the game did not consume the mouse event.
gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, x, y, 0);
return 0;
}
else if ((wParam & MK_RBUTTON) == MK_RBUTTON)
{
// Scale factors for the mouse movement used to simulate the accelerometer.
RECT clientRect;
GetClientRect(__hwnd, &clientRect);
const float xFactor = 90.0f / clientRect.right;
const float yFactor = 90.0f / clientRect.bottom;
// Update the pitch and roll by adding the scaled deltas.
__roll += (float)(x - lx) * xFactor;
__pitch += -(float)(y - ly) * yFactor;
// Clamp the values to the valid range.
__roll = max(min(__roll, 90.0f), -90.0f);
__pitch = max(min(__pitch, 90.0f), -90.0f);
// Update the last X/Y values.
lx = x;
ly = y;
}
}
break;
}
case WM_MOUSEWHEEL:
tagPOINT point;
point.x = GET_X_LPARAM(lParam);
point.y = GET_Y_LPARAM(lParam);
ScreenToClient(__hwnd, &point);
gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_WHEEL, point.x, point.y, GET_WHEEL_DELTA_WPARAM(wParam) / 120);
break;
case WM_KEYDOWN:
if (wParam == VK_SHIFT || wParam == VK_LSHIFT || wParam == VK_RSHIFT)
shiftDown = true;
if (wParam == VK_CAPITAL)
capsOn = !capsOn;
// Suppress key repeats.
if ((lParam & 0x40000000) == 0)
gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_PRESS, getKey(wParam, shiftDown ^ capsOn));
break;
case WM_KEYUP:
if (wParam == VK_SHIFT || wParam == VK_LSHIFT || wParam == VK_RSHIFT)
shiftDown = false;
gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_RELEASE, getKey(wParam, shiftDown ^ capsOn));
break;
case WM_CHAR:
// Suppress key repeats.
if ((lParam & 0x40000000) == 0)
gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, wParam);
break;
case WM_UNICHAR:
// Suppress key repeats.
if ((lParam & 0x40000000) == 0)
gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, wParam);
break;
case WM_SETFOCUS:
break;
case WM_KILLFOCUS:
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
namespace gameplay
{
struct WindowCreationParams
{
RECT rect;
std::wstring windowName;
bool fullscreen;
int samples;
};
extern void print(const char* format, ...)
{
va_list argptr;
va_start(argptr, format);
int sz = vfprintf(stderr, format, argptr);
if (sz > 0)
{
char* buf = new char[sz + 1];
vsprintf(buf, format, argptr);
buf[sz] = 0;
OutputDebugStringA(buf);
SAFE_DELETE_ARRAY(buf);
}
va_end(argptr);
}
Platform::Platform(Game* game)
: _game(game)
{
}
Platform::~Platform()
{
if (__hwnd)
{
DestroyWindow(__hwnd);
__hwnd = 0;
}
#ifdef USE_XINPUT
SAFE_DELETE_ARRAY(__xinputGamepads);
#endif
}
bool createWindow(WindowCreationParams* params, HWND* hwnd, HDC* hdc)
{
bool fullscreen = false;
RECT rect = { CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT };
std::wstring windowName;
if (params)
{
windowName = params->windowName;
memcpy(&rect, ¶ms->rect, sizeof(RECT));
fullscreen = params->fullscreen;
}
// Set the window style.
DWORD style = (fullscreen ? WS_POPUP : WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU) | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
DWORD styleEx = (fullscreen ? WS_EX_APPWINDOW : WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
// Adjust the window rectangle so the client size is the requested size.
AdjustWindowRectEx(&rect, style, FALSE, styleEx);
// Create the native Windows window.
*hwnd = CreateWindowEx(styleEx, L"gameplay", windowName.c_str(), style, 0, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, __hinstance, NULL);
if (*hwnd == NULL)
{
GP_ERROR("Failed to create window.");
return false;
}
// Get the drawing context.
*hdc = GetDC(*hwnd);
if (*hdc == NULL)
{
GP_ERROR("Failed to get device context.");
return false;
}
// Center the window
GetWindowRect(*hwnd, &rect);
const int screenX = (GetSystemMetrics(SM_CXSCREEN) - rect.right) / 2;
const int screenY = (GetSystemMetrics(SM_CYSCREEN) - rect.bottom) / 2;
SetWindowPos(*hwnd, *hwnd, screenX, screenY, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
return true;
}
bool initializeGL(WindowCreationParams* params)
{
// Create a temporary window and context to we can initialize GLEW and get access
// to additional OpenGL extension functions. This is a neccessary evil since the
// function for querying GL extensions is a GL extension itself.
HWND hwnd = NULL;
HDC hdc = NULL;
if (!createWindow(NULL, &hwnd, &hdc))
return false;
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = DEFAULT_COLOR_BUFFER_SIZE;
pfd.cDepthBits = DEFAULT_DEPTH_BUFFER_SIZE;
pfd.cStencilBits = DEFAULT_STENCIL_BUFFER_SIZE;
pfd.iLayerType = PFD_MAIN_PLANE;
int pixelFormat = ChoosePixelFormat(hdc, &pfd);
if (pixelFormat == 0)
{
DestroyWindow(hwnd);
GP_ERROR("Failed to choose a pixel format.");
return false;
}
if (!SetPixelFormat(hdc, pixelFormat, &pfd))
{
DestroyWindow(hwnd);
GP_ERROR("Failed to set the pixel format.");
return false;
}
HGLRC tempContext = wglCreateContext(hdc);
if (!tempContext)
{
DestroyWindow(hwnd);
GP_ERROR("Failed to create temporary context for initialization.");
return false;
}
wglMakeCurrent(hdc, tempContext);
// Initialize GLEW
if (GLEW_OK != glewInit())
{
wglDeleteContext(tempContext);
DestroyWindow(hwnd);
GP_ERROR("Failed to initialize GLEW.");
return false;
}
// Choose pixel format using wglChoosePixelFormatARB, which allows us to specify
// additional attributes such as multisampling.
//
// Note: Keep multisampling attributes at the start of the attribute lists since code below
// assumes they are array elements 0 through 3.
int attribList[] = {
WGL_SAMPLES_ARB, params->samples,
WGL_SAMPLE_BUFFERS_ARB, params->samples > 0 ? 1 : 0,
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, DEFAULT_COLOR_BUFFER_SIZE,
WGL_DEPTH_BITS_ARB, DEFAULT_DEPTH_BUFFER_SIZE,
WGL_STENCIL_BITS_ARB, DEFAULT_STENCIL_BUFFER_SIZE,
0
};
UINT numFormats;
if (!wglChoosePixelFormatARB(hdc, attribList, NULL, 1, &pixelFormat, &numFormats) || numFormats == 0)
{
bool valid = false;
if (params->samples > 0)
{
GP_WARN("Failed to choose pixel format with WGL_SAMPLES_ARB == %d. Attempting to fallback to lower samples setting.", params->samples);
while (params->samples > 0)
{
params->samples /= 2;
attribList[1] = params->samples;
attribList[3] = params->samples > 0 ? 1 : 0;
if (wglChoosePixelFormatARB(hdc, attribList, NULL, 1, &pixelFormat, &numFormats) && numFormats > 0)
{
valid = true;
GP_WARN("Found pixel format with WGL_SAMPLES_ARB == %d.", params->samples);
break;
}
}
}
if (!valid)
{
wglDeleteContext(tempContext);
DestroyWindow(hwnd);
GP_ERROR("Failed to choose a pixel format.");
return false;
}
}
// Destroy old window
DestroyWindow(hwnd);
hwnd = NULL;
hdc = NULL;
// Create new/final window if needed
if (params)
{
if (!createWindow(params, &__hwnd, &__hdc))
{
wglDeleteContext(tempContext);
return false;
}
}
// Set final pixel format for window
if (!SetPixelFormat(__hdc, pixelFormat, &pfd))
{
GP_ERROR("Failed to set the pixel format: %d.", (int)GetLastError());
return false;
}
// Create our new GL context
int attribs[] =
{
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
0
};
if (!(__hrc = wglCreateContextAttribsARB(__hdc, 0, attribs) ) )
{
wglDeleteContext(tempContext);
GP_ERROR("Failed to create OpenGL context.");
return false;
}
// Delete the old/temporary context and window
wglDeleteContext(tempContext);
// Make the new context current
if (!wglMakeCurrent(__hdc, __hrc) || !__hrc)
{
GP_ERROR("Failed to make the window current.");
return false;
}
// Vertical sync.
wglSwapIntervalEXT(__vsync ? 1 : 0);
return true;
}
Platform* Platform::create(Game* game, void* attachToWindow)
{
GP_ASSERT(game);
FileSystem::setResourcePath("./");
Platform* platform = new Platform(game);
// Get the application module handle.
__hinstance = ::GetModuleHandle(NULL);
__attachToWindow = (HWND)attachToWindow;
// Read window settings from config.
WindowCreationParams params;
params.fullscreen = false;
params.rect.left = 0;
params.rect.top = 0;
params.rect.right = 0;
params.rect.bottom = 0;
params.samples = 0;
if (game->getConfig())
{
Properties* config = game->getConfig()->getNamespace("window", true);
if (config)
{
// Read window title.
const char* title = config->getString("title");
if (title)
{
int len = MultiByteToWideChar(CP_ACP, 0, title, -1, NULL, 0);
wchar_t* wtitle = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, title, -1, wtitle, len);
params.windowName = wtitle;
SAFE_DELETE_ARRAY(wtitle);
}
// Read window rect.
int x = config->getInt("x");
if (x != 0)
params.rect.left = x;
int y = config->getInt("y");
if (y != 0)
params.rect.top = y;
int width = config->getInt("width");
if (width != 0)
params.rect.right = params.rect.left + width;
int height = config->getInt("height");
if (height != 0)
params.rect.bottom = params.rect.top + height;
// Read fullscreen state.
params.fullscreen = config->getBool("fullscreen");
// Read multisampling state.
params.samples = config->getInt("samples");
}
}
// If window size was not specified, set it to a default value
if (params.rect.right == 0)
params.rect.right = params.rect.left + DEFAULT_RESOLUTION_X;
if (params.rect.bottom == 0)
params.rect.bottom = params.rect.top + DEFAULT_RESOLUTION_Y;
int width = params.rect.right - params.rect.left;
int height = params.rect.bottom - params.rect.top;
if (params.fullscreen)
{
// Enumerate all supposed display settings
bool modeSupported = false;
DWORD modeNum = 0;
DEVMODE devMode;
memset(&devMode, 0, sizeof(DEVMODE));
devMode.dmSize = sizeof(DEVMODE);
devMode.dmDriverExtra = 0;
while (EnumDisplaySettings(NULL, modeNum++, &devMode) != 0)
{
// Is mode supported?
if (devMode.dmPelsWidth == width &&
devMode.dmPelsHeight == height &&
devMode.dmBitsPerPel == DEFAULT_COLOR_BUFFER_SIZE)
{
modeSupported = true;
break;
}
}
// If the requested mode is not supported, fall back to a safe default
if (!modeSupported)
{
width = DEFAULT_RESOLUTION_X;
height = DEFAULT_RESOLUTION_Y;
params.rect.right = params.rect.left + width;
params.rect.bottom = params.rect.top + height;
}
}
if (!__attachToWindow)
{
// Register our window class.
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)__WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = __hinstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL; // No brush - we are going to paint our own background
wc.lpszMenuName = NULL; // No default menu
wc.lpszClassName = L"gameplay";
if (!::RegisterClassEx(&wc))
{
GP_ERROR("Failed to register window class.");
goto error;
}
if (params.fullscreen)
{
DEVMODE dm;
memset(&dm, 0, sizeof(dm));
dm.dmSize= sizeof(dm);
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
dm.dmBitsPerPel = DEFAULT_COLOR_BUFFER_SIZE;
dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
// Try to set selected mode and get results. NOTE: CDS_FULLSCREEN gets rid of start bar.
if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
params.fullscreen = false;
GP_ERROR("Failed to start game in full-screen mode with resolution %dx%d.", width, height);
goto error;
}
}
if (!initializeGL(¶ms))
goto error;
// Show the window.
ShowWindow(__hwnd, SW_SHOW);
}
else
{
// Attach to a previous windows
__hwnd = (HWND)__attachToWindow;
__hdc = GetDC(__hwnd);
SetWindowLongPtr(__hwnd, GWL_WNDPROC, (LONG)(WNDPROC)__WndProc);
if (!initializeGL(NULL))
goto error;
}
#ifdef USE_XINPUT
// Initialize XInputGamepads.
__xinputGamepads = new XInputGamepad[XUSER_MAX_COUNT];
for (unsigned int i = 0; i < XUSER_MAX_COUNT; i++)
{
switch(i)
{
case 0:
__xinputGamepads[i].id = "XINPUT 1";
break;
case 1:
__xinputGamepads[i].id = "XINPUT 2";
break;
case 2:
__xinputGamepads[i].id = "XINPUT 3";
break;
case 3:
__xinputGamepads[i].id = "XINPUT 4";
break;
}
__xinputGamepads[i].connected = false;
__xinputGamepads[i].handle = -1;
}
#endif
return platform;
error:
exit(0);
return NULL;
}
int Platform::enterMessagePump()
{
GP_ASSERT(_game);
// Get the initial time.
LARGE_INTEGER tps;
QueryPerformanceFrequency(&tps);
__timeTicksPerMillis = (double)(tps.QuadPart / 1000L);
LARGE_INTEGER queryTime;
QueryPerformanceCounter(&queryTime);
GP_ASSERT(__timeTicksPerMillis);
__timeStart = queryTime.QuadPart / __timeTicksPerMillis;
// Set the initial pitch and roll values.
__pitch = 0.0;
__roll = 0.0;
SwapBuffers(__hdc);
if (_game->getState() != Game::RUNNING)
_game->run();
if (__attachToWindow)
return 0;
// Enter event dispatch loop.
MSG msg;
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT)
{
_game->exit();
break;
}
}
else
{
_game->frame();
SwapBuffers(__hdc);
}
// If we are done, then exit.
if (_game->getState() == Game::UNINITIALIZED)
break;
}
return msg.wParam;
}
void Platform::signalShutdown()
{
// nothing to do
}
bool Platform::canExit()
{
return true;
}
unsigned int Platform::getDisplayWidth()
{
static RECT rect;
GetClientRect(__hwnd, &rect);
return rect.right;
}
unsigned int Platform::getDisplayHeight()
{
static RECT rect;
GetClientRect(__hwnd, &rect);
return rect.bottom;
}
double Platform::getAbsoluteTime()
{
LARGE_INTEGER queryTime;
QueryPerformanceCounter(&queryTime);
GP_ASSERT(__timeTicksPerMillis);
__timeAbsolute = queryTime.QuadPart / __timeTicksPerMillis;
return __timeAbsolute;
}
void Platform::setAbsoluteTime(double time)
{
__timeAbsolute = time;
}
bool Platform::isVsync()
{
return __vsync;
}
void Platform::setVsync(bool enable)
{
wglSwapIntervalEXT(enable ? 1 : 0);
__vsync = enable;
}
void Platform::swapBuffers()
{
if (__hdc)
SwapBuffers(__hdc);
}
void Platform::sleep(long ms)
{
Sleep(ms);
}
void Platform::setMultiTouch(bool enabled)
{
// not supported
}
bool Platform::isMultiTouch()
{
return false;
}
void Platform::getAccelerometerValues(float* pitch, float* roll)
{
GP_ASSERT(pitch);
GP_ASSERT(roll);
*pitch = __pitch;
*roll = __roll;
}
bool Platform::hasMouse()
{
return true;
}
void Platform::setMouseCaptured(bool captured)
{
if (captured != __mouseCaptured)
{
if (captured)
{
// Hide the cursor and warp it to the center of the screen
__mouseCapturePoint.x = getDisplayWidth() / 2;
__mouseCapturePoint.y = getDisplayHeight() / 2;
ShowCursor(FALSE);
WarpMouse(__mouseCapturePoint.x, __mouseCapturePoint.y);
}
else
{
// Restore cursor
WarpMouse(__mouseCapturePoint.x, __mouseCapturePoint.y);
ShowCursor(TRUE);
}
__mouseCaptured = captured;
}
}
bool Platform::isMouseCaptured()
{
return __mouseCaptured;
}
void Platform::setCursorVisible(bool visible)
{
if (visible != __cursorVisible)
{
ShowCursor(visible ? TRUE : FALSE);
__cursorVisible = visible;
}
}
bool Platform::isCursorVisible()
{
return __cursorVisible;
}
void Platform::displayKeyboard(bool display)
{
// Do nothing.
}
bool Platform::isGestureSupported(Gesture::GestureEvent evt)
{
return false;
}
void Platform::registerGesture(Gesture::GestureEvent evt)
{
}
void Platform::unregisterGesture(Gesture::GestureEvent evt)
{
}
bool Platform::isGestureRegistered(Gesture::GestureEvent evt)
{
return false;
}
unsigned int Platform::getGamepadsConnected()
{
// Check to see what xbox360 gamepads are connected.
__gamepadsConnected = 0;
#ifdef USE_XINPUT
for (unsigned int i = 0; i < XUSER_MAX_COUNT; i++)
{
if (isGamepadConnected(i))
__gamepadsConnected++;
}
#endif
return __gamepadsConnected;
}
bool Platform::isGamepadConnected(unsigned int gamepadHandle)
{
#ifdef USE_XINPUT
GP_ASSERT(0 <= gamepadHandle);
GP_ASSERT(gamepadHandle < XUSER_MAX_COUNT);
Game* game = Game::getInstance();
GP_ASSERT(game);
if (__xinputGamepads[gamepadHandle].handle == -1)
__xinputGamepads[gamepadHandle].handle = game->createGamepad(__xinputGamepads[gamepadHandle].id.c_str(), gamepadHandle, __xinputGamepads[gamepadHandle].BUTTON_COUNT,
__xinputGamepads[gamepadHandle].JOYSTICK_COUNT, __xinputGamepads[gamepadHandle].TRIGGER_COUNT);
bool isConnected = __xinputGamepads[gamepadHandle].connected;
if (getXInputState(gamepadHandle))
{
if (!isConnected)
{
__xinputGamepads[gamepadHandle].connected = true;
Gamepad* gamepad = game->getGamepad(__xinputGamepads[gamepadHandle].handle);
GP_ASSERT(gamepad);
if (game->isInitialized())
game->gamepadEvent(Gamepad::CONNECTED_EVENT, game->getGamepad(__xinputGamepads[gamepadHandle].handle));
}
return true;
}
else
{
if (isConnected)
{
// if it was connected, but now isn't pass the detached event to gamepadEvent()
__xinputGamepads[gamepadHandle].connected = false;
Gamepad* gamepad = game->getGamepad(__xinputGamepads[gamepadHandle].handle);
GP_ASSERT(gamepad);
if (game->isInitialized())
game->gamepadEvent(Gamepad::DISCONNECTED_EVENT, game->getGamepad(__xinputGamepads[gamepadHandle].handle));
}
return false;
}
#endif
return false;
}
const char* Platform::getGamepadId(unsigned int gamepadHandle)
{
#ifdef USE_XINPUT
GP_ASSERT(0 <= gamepadHandle);
GP_ASSERT(gamepadHandle < XUSER_MAX_COUNT);
return __xinputGamepads[gamepadHandle].id.c_str();
#endif
return NULL;
}
unsigned int Platform::getGamepadButtonCount(unsigned int gamepadHandle)
{
#ifdef USE_XINPUT
GP_ASSERT(0 <= gamepadHandle);
GP_ASSERT(gamepadHandle < XUSER_MAX_COUNT);
if (!__xinputGamepads[gamepadHandle].connected)
return 0;
return XInputGamepad::BUTTON_COUNT;
#endif
return 0;
}
bool Platform::getGamepadButtonState(unsigned int gamepadHandle, unsigned int buttonIndex)
{
#ifdef USE_XINPUT
GP_ASSERT(0 <= gamepadHandle);
GP_ASSERT(gamepadHandle < XUSER_MAX_COUNT);
return getXInputButtonState(gamepadHandle, buttonIndex);
#endif
return false;
}
unsigned int Platform::getGamepadJoystickCount(unsigned int gamepadHandle)
{
#ifdef USE_XINPUT
GP_ASSERT(0 <= gamepadHandle);
GP_ASSERT(gamepadHandle < XUSER_MAX_COUNT);
if (!__xinputGamepads[gamepadHandle].connected)
return 0;
return XInputGamepad::JOYSTICK_COUNT;
#endif
return 0;
}
float Platform::getGamepadJoystickAxisX(unsigned int gamepadHandle, unsigned int joystickIndex)
{
#ifdef USE_XINPUT
GP_ASSERT(0 <= gamepadHandle);
GP_ASSERT(gamepadHandle < XUSER_MAX_COUNT);
return getXInputJoystickAxisX(gamepadHandle, joystickIndex);
#endif
return 0.0f;
}
float Platform::getGamepadJoystickAxisY(unsigned int gamepadHandle, unsigned int joystickIndex)
{
#ifdef USE_XINPUT
GP_ASSERT(0 <= gamepadHandle);
GP_ASSERT(gamepadHandle < XUSER_MAX_COUNT);
return getXInputJoystickAxisY(gamepadHandle, joystickIndex);
#endif
return 0.0f;
}
void Platform::getGamepadJoystickAxisValues(unsigned int gamepadHandle, unsigned int joystickIndex, Vector2* outValue)
{
#ifdef USE_XINPUT
GP_ASSERT(0 <= gamepadHandle);
GP_ASSERT(gamepadHandle < XUSER_MAX_COUNT);
getXInputJoystickAxisValues(gamepadHandle, joystickIndex, outValue);
#endif
}
bool Platform::isGamepadJoystickActive(unsigned int gamepadHandle, unsigned int joystickIndex)
{
#ifdef USE_XINPUT
GP_ASSERT(0 <= gamepadHandle);
GP_ASSERT(gamepadHandle < XUSER_MAX_COUNT);
return (getXInputJoystickAxisX(gamepadHandle, joystickIndex) != 0.0f || getXInputJoystickAxisY(gamepadHandle, joystickIndex) != 0.0f);
#endif
return false;
}
unsigned int Platform::getGamepadTriggerCount(unsigned int gamepadHandle)
{
#ifdef USE_XINPUT
GP_ASSERT(0 <= gamepadHandle);
GP_ASSERT(gamepadHandle < XUSER_MAX_COUNT);
return XInputGamepad::TRIGGER_COUNT;
#endif
return 0;
}
float Platform::getGamepadTriggerValue(unsigned int gamepadHandle, unsigned int triggerIndex)
{
#ifdef USE_XINPUT
GP_ASSERT(0 <= gamepadHandle);
GP_ASSERT(gamepadHandle < XUSER_MAX_COUNT);
//TODO:
#endif
return 0.0f;
}
void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
{
if (!Form::touchEventInternal(evt, x, y, contactIndex))
{
Game::getInstance()->touchEvent(evt, x, y, contactIndex);
Game::getInstance()->getScriptController()->touchEvent(evt, x, y, contactIndex);
}
}
void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
{
if (!Form::keyEventInternal(evt, key))
{
Game::getInstance()->keyEvent(evt, key);
Game::getInstance()->getScriptController()->keyEvent(evt, key);
}
}
bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
{
if (Form::mouseEventInternal(evt, x, y, wheelDelta))
{
return true;
}
else if (Game::getInstance()->mouseEvent(evt, x, y, wheelDelta))
{
return true;
}
else
{
return Game::getInstance()->getScriptController()->mouseEvent(evt, x, y, wheelDelta);
}
}
}
#endif