mirror of
https://github.com/4jcraft/4jcraft.git
synced 2026-07-13 04:17:03 +00:00
added files from pr
This commit is contained in:
parent
2464d27749
commit
1c67f134b2
|
|
@ -1,536 +1,265 @@
|
|||
// 4J_Input.cpp - GLFW keyboard + mouse input for the Linux port
|
||||
// Replaces the SDL2 oldimpl with GLFW equivalents.
|
||||
// Uses glfwGetCurrentContext() to get the window the render manager created,
|
||||
// avoiding a coupling dependency on 4J_Render.h.
|
||||
|
||||
#include "4J_Input.h"
|
||||
#include "../Minecraft.Client/Platform/Common/App_enums.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "../4J.Render/4J_Render.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
C_4JInput InputManager;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// State - all static to avoid adding new fields to C_4JInput
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static const int KEY_COUNT = GLFW_KEY_LAST + 1; // 349 on GLFW 3
|
||||
|
||||
static const int KEY_COUNT = SDL_NUM_SCANCODES;
|
||||
static const float MOUSE_SCALE = 0.015f;
|
||||
// Vars
|
||||
static bool s_sdlInitialized = false;
|
||||
static bool s_keysCurrent[KEY_COUNT] = {};
|
||||
static bool s_keysPrev[KEY_COUNT] = {};
|
||||
|
||||
static bool s_keysPrev [KEY_COUNT] = {};
|
||||
static bool s_mouseLeftCurrent = false, s_mouseLeftPrev = false;
|
||||
static bool s_mouseRightCurrent = false, s_mouseRightPrev = false;
|
||||
static bool s_menuDisplayed[4] = {};
|
||||
static bool s_prevMenuDisplayed = false;
|
||||
static bool s_snapTaken = false, s_scrollSnapTaken = false;
|
||||
static float s_accumRelX = 0, s_accumRelY = 0;
|
||||
static float s_snapRelX = 0, s_snapRelY = 0;
|
||||
static float s_scrollAccum = 0, s_scrollSnap = 0;
|
||||
|
||||
// Accumulated cursor delta from the GLFW cursor-pos callback.
|
||||
// Snapshotted into s_frameRelX/Y at Tick() start, then reset to 0.
|
||||
static double s_lastCursorX = 0.0, s_lastCursorY = 0.0;
|
||||
static bool s_cursorInitialized = false;
|
||||
static float s_mouseAccumX = 0.0f, s_mouseAccumY = 0.0f; // callback accumulator
|
||||
static float s_frameRelX = 0.0f, s_frameRelY = 0.0f; // per-frame snapshot
|
||||
|
||||
// Scroll wheel
|
||||
static float s_scrollAccum = 0.0f; // callback accumulator
|
||||
static float s_scrollFrame = 0.0f; // current frame snapshot
|
||||
static float s_scrollPrevFrame = 0.0f;
|
||||
|
||||
// Mouse lock / menu state
|
||||
static bool s_mouseLocked = false;
|
||||
static bool s_menuDisplayed[4] = {};
|
||||
static bool s_prevMenuDisplayed = true; // start as "in menu" so auto-lock triggers after first frame
|
||||
|
||||
// Sensitivity: scales raw pixel delta before sqrt-compression
|
||||
// Smaller value = less mouse movement per pixel
|
||||
static const float MOUSE_SCALE = 0.012f;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GLFW window (obtained lazily via glfwGetCurrentContext on the render thread)
|
||||
// ---------------------------------------------------------------------------
|
||||
static GLFWwindow *s_inputWindow = nullptr;
|
||||
|
||||
static GLFWwindow *getWindow() {
|
||||
if (!s_inputWindow) {
|
||||
s_inputWindow = glfwGetCurrentContext();
|
||||
}
|
||||
return s_inputWindow;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GLFW callbacks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void onCursorPos(GLFWwindow * /*w*/, double x, double y) {
|
||||
if (s_cursorInitialized) {
|
||||
s_mouseAccumX += (float)(x - s_lastCursorX);
|
||||
s_mouseAccumY += (float)(y - s_lastCursorY);
|
||||
} else {
|
||||
s_cursorInitialized = true;
|
||||
}
|
||||
s_lastCursorX = x;
|
||||
s_lastCursorY = y;
|
||||
}
|
||||
|
||||
static void onScroll(GLFWwindow * /*w*/, double /*xoffset*/, double yoffset) {
|
||||
s_scrollAccum += (float)yoffset;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static inline bool KDown(int key) {
|
||||
return (key >= 0 && key < KEY_COUNT) ? s_keysCurrent[key] : false;
|
||||
}
|
||||
static inline bool KPressed(int key) {
|
||||
return (key >= 0 && key < KEY_COUNT) ? (s_keysCurrent[key] && !s_keysPrev[key]) : false;
|
||||
}
|
||||
static inline bool KReleased(int key) {
|
||||
return (key >= 0 && key < KEY_COUNT) ? (!s_keysCurrent[key] && s_keysPrev[key]) : false;
|
||||
}
|
||||
|
||||
static inline bool MouseLDown() { return s_mouseLeftCurrent; }
|
||||
static inline bool MouseLPressed() { return s_mouseLeftCurrent && !s_mouseLeftPrev; }
|
||||
static inline bool MouseLReleased() { return !s_mouseLeftCurrent && s_mouseLeftPrev; }
|
||||
static inline bool MouseRDown() { return s_mouseRightCurrent; }
|
||||
static inline bool MouseRPressed() { return s_mouseRightCurrent && !s_mouseRightPrev; }
|
||||
static inline bool MouseRReleased() { return !s_mouseRightCurrent && s_mouseRightPrev; }
|
||||
|
||||
static inline bool WheelUp() { return s_scrollFrame > 0.1f; }
|
||||
static inline bool WheelDown() { return s_scrollFrame < -0.1f; }
|
||||
static inline bool WheelUpEdge() { return s_scrollFrame > 0.1f && s_scrollPrevFrame <= 0.1f; }
|
||||
static inline bool WheelDownEdge() { return s_scrollFrame < -0.1f && s_scrollPrevFrame >= -0.1f; }
|
||||
|
||||
// Keys to snapshot each Tick (avoid iterating all 349 entries)
|
||||
// We set all the watched keys
|
||||
// I don't know if I'll need to change this if we add chat support soon.
|
||||
static const int s_watchedKeys[] = {
|
||||
GLFW_KEY_W, GLFW_KEY_A, GLFW_KEY_S, GLFW_KEY_D,
|
||||
GLFW_KEY_SPACE, GLFW_KEY_LEFT_SHIFT, GLFW_KEY_RIGHT_SHIFT,
|
||||
GLFW_KEY_E, GLFW_KEY_Q, GLFW_KEY_F,
|
||||
GLFW_KEY_ESCAPE, GLFW_KEY_ENTER,
|
||||
GLFW_KEY_F3, GLFW_KEY_F5,
|
||||
GLFW_KEY_UP, GLFW_KEY_DOWN, GLFW_KEY_LEFT, GLFW_KEY_RIGHT,
|
||||
GLFW_KEY_PAGE_UP, GLFW_KEY_PAGE_DOWN,
|
||||
GLFW_KEY_TAB,
|
||||
GLFW_KEY_LEFT_CONTROL, GLFW_KEY_RIGHT_CONTROL,
|
||||
GLFW_KEY_1, GLFW_KEY_2, GLFW_KEY_3, GLFW_KEY_4, GLFW_KEY_5,
|
||||
GLFW_KEY_6, GLFW_KEY_7, GLFW_KEY_8, GLFW_KEY_9,
|
||||
SDL_SCANCODE_W, SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_D,
|
||||
SDL_SCANCODE_SPACE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT,
|
||||
SDL_SCANCODE_E, SDL_SCANCODE_Q, SDL_SCANCODE_F,
|
||||
SDL_SCANCODE_C, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_RETURN,
|
||||
SDL_SCANCODE_F3, SDL_SCANCODE_F5,
|
||||
SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,
|
||||
SDL_SCANCODE_PAGEUP, SDL_SCANCODE_PAGEDOWN,
|
||||
SDL_SCANCODE_TAB, SDL_SCANCODE_LCTRL, SDL_SCANCODE_RCTRL,
|
||||
SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4,
|
||||
SDL_SCANCODE_5, SDL_SCANCODE_6, SDL_SCANCODE_7, SDL_SCANCODE_8,
|
||||
SDL_SCANCODE_9,
|
||||
};
|
||||
static const int s_watchedKeyCount = (int)(sizeof(s_watchedKeys) / sizeof(s_watchedKeys[0]));
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// C_4JInput::Initialise
|
||||
// ---------------------------------------------------------------------------
|
||||
void C_4JInput::Initialise(int /*iInputStateC*/, unsigned char /*ucMapC*/,
|
||||
unsigned char /*ucActionC*/, unsigned char /*ucMenuActionC*/) {
|
||||
memset(s_keysCurrent, 0, sizeof(s_keysCurrent));
|
||||
memset(s_keysPrev, 0, sizeof(s_keysPrev));
|
||||
memset(s_menuDisplayed, 0, sizeof(s_menuDisplayed));
|
||||
static inline bool KDown (int sc) { return (sc > 0 && sc < KEY_COUNT) ? s_keysCurrent[sc] : false; }
|
||||
static inline bool KPressed (int sc) { return (sc > 0 && sc < KEY_COUNT) ? !s_keysPrev[sc] && s_keysCurrent[sc] : false; }
|
||||
static inline bool KReleased(int sc) { return (sc > 0 && sc < KEY_COUNT) ? s_keysPrev[sc] && !s_keysCurrent[sc] : false; }
|
||||
|
||||
s_mouseLeftCurrent = s_mouseLeftPrev = false;
|
||||
s_mouseRightCurrent = s_mouseRightPrev = false;
|
||||
s_mouseAccumX = s_mouseAccumY = 0.0f;
|
||||
s_frameRelX = s_frameRelY = 0.0f;
|
||||
s_scrollAccum = s_scrollFrame = s_scrollPrevFrame = 0.0f;
|
||||
s_mouseLocked = false;
|
||||
s_prevMenuDisplayed = true; // triggers auto-lock once game leaves first menu
|
||||
s_cursorInitialized = false;
|
||||
static inline bool MouseLDown () { return s_mouseLeftCurrent; }
|
||||
static inline bool MouseLPressed () { return s_mouseLeftCurrent && !s_mouseLeftPrev; }
|
||||
static inline bool MouseLReleased() { return !s_mouseLeftCurrent && s_mouseLeftPrev; }
|
||||
static inline bool MouseRDown () { return s_mouseRightCurrent; }
|
||||
static inline bool MouseRPressed () { return s_mouseRightCurrent && !s_mouseRightPrev; }
|
||||
static inline bool MouseRReleased() { return !s_mouseRightCurrent && s_mouseRightPrev; }
|
||||
|
||||
GLFWwindow *w = getWindow();
|
||||
if (w) {
|
||||
glfwSetCursorPosCallback(w, onCursorPos);
|
||||
glfwSetScrollCallback(w, onScroll);
|
||||
// NOTE: GLFW_RAW_MOUSE_MOTION must only be set when cursor mode is
|
||||
// GLFW_CURSOR_DISABLED (Wayland zwp_relative_pointer_v1 requirement).
|
||||
// It is activated at the cursor-lock call sites below in Tick().
|
||||
}
|
||||
|
||||
printf("[4J_Input] GLFW input initialised\n");
|
||||
printf(" WASD=move Mouse=look LMB=attack RMB=use\n");
|
||||
printf(" Space=jump LShift=sneak E=inventory Q=drop Esc=pause\n");
|
||||
printf(" F5=3rd-person F3=debug Scroll=hotbar\n");
|
||||
fflush(stdout);
|
||||
// Snap the scroll value so that it doesn't go crazy!
|
||||
static float ScrollSnap() {
|
||||
if (!s_scrollSnapTaken) { s_scrollSnap = s_scrollAccum; s_scrollAccum = 0; s_scrollSnapTaken = true; }
|
||||
return s_scrollSnap;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// C_4JInput::Tick (called once per frame, BEFORE Present / glfwPollEvents)
|
||||
// ---------------------------------------------------------------------------
|
||||
void C_4JInput::Tick(void) {
|
||||
GLFWwindow *w = getWindow();
|
||||
if (!w) return;
|
||||
static void TakeSnapIfNeeded() {
|
||||
if (!s_snapTaken) {
|
||||
s_snapRelX = s_accumRelX; s_accumRelX = 0;
|
||||
s_snapRelY = s_accumRelY; s_accumRelY = 0;
|
||||
s_snapTaken = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We initialize the SDL input
|
||||
void C_4JInput::Initialise(int, unsigned char, unsigned char, unsigned char) {
|
||||
if (!s_sdlInitialized) {
|
||||
s_sdlInitialized = SDL_Init(SDL_INIT_VIDEO) == 0;
|
||||
}
|
||||
|
||||
memset(s_keysCurrent, 0, sizeof(s_keysCurrent));
|
||||
memset(s_keysPrev, 0, sizeof(s_keysPrev));
|
||||
memset(s_menuDisplayed, 0, sizeof(s_menuDisplayed));
|
||||
|
||||
s_mouseLeftCurrent = s_mouseLeftPrev = s_mouseRightCurrent = s_mouseRightPrev = false;
|
||||
s_accumRelX = s_accumRelY = s_snapRelX = s_snapRelY = 0;
|
||||
s_scrollAccum = s_scrollSnap = 0;
|
||||
s_snapTaken = s_scrollSnapTaken = s_prevMenuDisplayed = false;
|
||||
|
||||
if (s_sdlInitialized) {
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
// Each tick we update the input state by polling SDL, this is where we get the kbd and mouse state.
|
||||
void C_4JInput::Tick() {
|
||||
if (!s_sdlInitialized) return;
|
||||
|
||||
// 1. Save previous frame
|
||||
memcpy(s_keysPrev, s_keysCurrent, sizeof(s_keysCurrent));
|
||||
s_mouseLeftPrev = s_mouseLeftCurrent;
|
||||
s_mouseRightPrev = s_mouseRightCurrent;
|
||||
s_scrollPrevFrame = s_scrollFrame;
|
||||
s_snapTaken = s_scrollSnapTaken = false;
|
||||
s_snapRelX = s_snapRelY = s_scrollSnap = 0;
|
||||
|
||||
// 2. Snapshot current keyboard state for watched keys
|
||||
for (int i = 0; i < s_watchedKeyCount; i++) {
|
||||
int k = s_watchedKeys[i];
|
||||
s_keysCurrent[k] = (glfwGetKey(w, k) == GLFW_PRESS);
|
||||
SDL_PumpEvents();
|
||||
// Keyboard State.
|
||||
const Uint8 *state = SDL_GetKeyboardState(NULL);
|
||||
for (int i = 0; i < s_watchedKeyCount; ++i) {
|
||||
int sc = s_watchedKeys[i];
|
||||
if (sc > 0 && sc < KEY_COUNT) s_keysCurrent[sc] = state[sc] != 0;
|
||||
}
|
||||
// Scroll Wheel events. It's a bit weird, but it works.
|
||||
SDL_Event ev;
|
||||
while (SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEWHEEL, SDL_MOUSEWHEEL) > 0)
|
||||
s_scrollAccum += (float)ev.wheel.y;
|
||||
|
||||
// 3. Snapshot and reset scroll accumulator
|
||||
s_scrollFrame = s_scrollAccum;
|
||||
s_scrollAccum = 0.0f;
|
||||
|
||||
// 4. Mouse-lock management based on menu display state
|
||||
bool menuNow = s_menuDisplayed[0];
|
||||
|
||||
if (menuNow && s_mouseLocked) {
|
||||
// Re-entered a menu → release mouse cursor
|
||||
s_mouseLocked = false;
|
||||
if (glfwRawMouseMotionSupported())
|
||||
glfwSetInputMode(w, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
|
||||
glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
// Discard stale delta so the view doesn't jerk on re-lock
|
||||
s_mouseAccumX = s_mouseAccumY = 0.0f;
|
||||
s_cursorInitialized = false;
|
||||
int dx = 0, dy = 0;
|
||||
while (SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
|
||||
dx += ev.motion.xrel; dy += ev.motion.yrel;
|
||||
}
|
||||
if (!menuNow && s_prevMenuDisplayed && !s_mouseLocked) {
|
||||
// Left the menu → lock mouse for look control
|
||||
s_mouseLocked = true;
|
||||
glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
// Enable raw (un-accelerated) relative motion now that cursor is disabled.
|
||||
// On Wayland this activates zwp_relative_pointer_v1 for sub-pixel precise
|
||||
// mouse deltas; on X11 it bypasses the compositor acceleration curve.
|
||||
if (glfwRawMouseMotionSupported())
|
||||
glfwSetInputMode(w, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
|
||||
s_mouseAccumX = s_mouseAccumY = 0.0f;
|
||||
s_cursorInitialized = false;
|
||||
}
|
||||
s_prevMenuDisplayed = menuNow;
|
||||
// Mouse Position & Button Events.
|
||||
if (dx == 0 && dy == 0 && SDL_GetRelativeMouseMode())
|
||||
SDL_GetRelativeMouseState(&dx, &dy);
|
||||
s_accumRelX += (float)dx;
|
||||
s_accumRelY += (float)dy;
|
||||
|
||||
// 5. Snapshot and reset mouse delta from callback
|
||||
s_frameRelX = s_mouseAccumX;
|
||||
s_frameRelY = s_mouseAccumY;
|
||||
s_mouseAccumX = s_mouseAccumY = 0.0f;
|
||||
Uint32 btns = SDL_GetMouseState(NULL, NULL);
|
||||
s_mouseLeftCurrent = (btns & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
|
||||
s_mouseRightCurrent = (btns & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
|
||||
|
||||
// 6. Mouse buttons (only meaningful when locked in-game)
|
||||
if (s_mouseLocked) {
|
||||
s_mouseLeftCurrent = (glfwGetMouseButton(w, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS);
|
||||
s_mouseRightCurrent = (glfwGetMouseButton(w, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS);
|
||||
} else {
|
||||
// Not locked. Allow a left-click to re-lock (if not in a menu)
|
||||
bool lclick = (glfwGetMouseButton(w, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS);
|
||||
if (!menuNow && lclick) {
|
||||
s_mouseLocked = true;
|
||||
glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
if (glfwRawMouseMotionSupported())
|
||||
glfwSetInputMode(w, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
|
||||
s_mouseAccumX = s_mouseAccumY = 0.0f;
|
||||
s_cursorInitialized = false;
|
||||
}
|
||||
s_mouseLeftCurrent = false;
|
||||
s_mouseRightCurrent = false;
|
||||
s_frameRelX = s_frameRelY = 0.0f;
|
||||
if (!SDL_GetKeyboardFocus()) {
|
||||
SDL_Window *mf = SDL_GetMouseFocus();
|
||||
if (mf) { SDL_RaiseWindow(mf); SDL_SetWindowGrab(mf, SDL_TRUE); }
|
||||
}
|
||||
}
|
||||
// been a while i haven't used cases
|
||||
#define ACTION_CASES(FN) \
|
||||
case ACTION_MENU_UP: return FN(SDL_SCANCODE_UP); \
|
||||
case ACTION_MENU_DOWN: return FN(SDL_SCANCODE_DOWN); \
|
||||
case ACTION_MENU_LEFT: return FN(SDL_SCANCODE_LEFT); \
|
||||
case ACTION_MENU_RIGHT: return FN(SDL_SCANCODE_RIGHT); \
|
||||
case ACTION_MENU_PAGEUP: return FN(SDL_SCANCODE_PAGEUP); \
|
||||
case ACTION_MENU_PAGEDOWN: return FN(SDL_SCANCODE_PAGEDOWN); \
|
||||
case ACTION_MENU_OK: return FN(SDL_SCANCODE_RETURN); \
|
||||
case ACTION_MENU_CANCEL: return FN(SDL_SCANCODE_ESCAPE); \
|
||||
case MINECRAFT_ACTION_JUMP: return FN(SDL_SCANCODE_SPACE); \
|
||||
case MINECRAFT_ACTION_FORWARD: return FN(SDL_SCANCODE_W); \
|
||||
case MINECRAFT_ACTION_BACKWARD: return FN(SDL_SCANCODE_S); \
|
||||
case MINECRAFT_ACTION_LEFT: return FN(SDL_SCANCODE_A); \
|
||||
case MINECRAFT_ACTION_RIGHT: return FN(SDL_SCANCODE_D); \
|
||||
case MINECRAFT_ACTION_INVENTORY: return FN(SDL_SCANCODE_E); \
|
||||
case MINECRAFT_ACTION_PAUSEMENU: return FN(SDL_SCANCODE_ESCAPE); \
|
||||
case MINECRAFT_ACTION_DROP: return FN(SDL_SCANCODE_Q); \
|
||||
case MINECRAFT_ACTION_CRAFTING: return FN(SDL_SCANCODE_C); \
|
||||
case MINECRAFT_ACTION_RENDER_THIRD_PERSON:return FN(SDL_SCANCODE_F5); \
|
||||
case MINECRAFT_ACTION_GAME_INFO: return FN(SDL_SCANCODE_F3); \
|
||||
case MINECRAFT_ACTION_DPAD_LEFT: return FN(SDL_SCANCODE_LEFT); \
|
||||
case MINECRAFT_ACTION_DPAD_RIGHT: return FN(SDL_SCANCODE_RIGHT); \
|
||||
case MINECRAFT_ACTION_DPAD_UP: return FN(SDL_SCANCODE_UP); \
|
||||
case MINECRAFT_ACTION_DPAD_DOWN: return FN(SDL_SCANCODE_DOWN); \
|
||||
default: return false;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ButtonDown – is action held this frame?
|
||||
// ---------------------------------------------------------------------------
|
||||
bool C_4JInput::ButtonDown(int /*iPad*/, unsigned char ucAction) {
|
||||
// The part that handles Pressing a button.
|
||||
bool C_4JInput::ButtonDown(int iPad, unsigned char ucAction) {
|
||||
if (iPad != 0) return false;
|
||||
if (ucAction == 255) {
|
||||
for (int i = 0; i < s_watchedKeyCount; ++i)
|
||||
if (s_keysCurrent[s_watchedKeys[i]]) return true;
|
||||
return s_mouseLeftCurrent || s_mouseRightCurrent;
|
||||
}
|
||||
switch (ucAction) {
|
||||
// ---- Menu navigation ----
|
||||
case ACTION_MENU_UP: return KDown(GLFW_KEY_UP) || KDown(GLFW_KEY_W);
|
||||
case ACTION_MENU_DOWN: return KDown(GLFW_KEY_DOWN) || KDown(GLFW_KEY_S);
|
||||
case ACTION_MENU_LEFT: return KDown(GLFW_KEY_LEFT) || KDown(GLFW_KEY_A);
|
||||
case ACTION_MENU_RIGHT: return KDown(GLFW_KEY_RIGHT) || KDown(GLFW_KEY_D);
|
||||
|
||||
case ACTION_MENU_A:
|
||||
case ACTION_MENU_OK:
|
||||
return KDown(GLFW_KEY_ENTER) || KDown(GLFW_KEY_SPACE);
|
||||
|
||||
case ACTION_MENU_B:
|
||||
case ACTION_MENU_CANCEL:
|
||||
return KDown(GLFW_KEY_ESCAPE);
|
||||
|
||||
case ACTION_MENU_X: return KDown(GLFW_KEY_F);
|
||||
case ACTION_MENU_Y: return KDown(GLFW_KEY_E);
|
||||
|
||||
case ACTION_MENU_PAGEUP: return KDown(GLFW_KEY_PAGE_UP) || KDown(GLFW_KEY_LEFT_SHIFT);
|
||||
case ACTION_MENU_PAGEDOWN: return KDown(GLFW_KEY_PAGE_DOWN) || KDown(GLFW_KEY_RIGHT_SHIFT);
|
||||
case ACTION_MENU_RIGHT_SCROLL: return KDown(GLFW_KEY_RIGHT_SHIFT) || WheelUp();
|
||||
case ACTION_MENU_LEFT_SCROLL: return KDown(GLFW_KEY_LEFT_SHIFT) || WheelDown();
|
||||
|
||||
case ACTION_MENU_STICK_PRESS: return false;
|
||||
case ACTION_MENU_OTHER_STICK_PRESS: return false;
|
||||
case ACTION_MENU_OTHER_STICK_UP: return KDown(GLFW_KEY_UP);
|
||||
case ACTION_MENU_OTHER_STICK_DOWN: return KDown(GLFW_KEY_DOWN);
|
||||
case ACTION_MENU_OTHER_STICK_LEFT: return KDown(GLFW_KEY_LEFT);
|
||||
case ACTION_MENU_OTHER_STICK_RIGHT: return KDown(GLFW_KEY_RIGHT);
|
||||
|
||||
case ACTION_MENU_PAUSEMENU: return KDown(GLFW_KEY_ESCAPE);
|
||||
|
||||
// ---- Minecraft in-game ----
|
||||
case MINECRAFT_ACTION_JUMP: return KDown(GLFW_KEY_SPACE);
|
||||
case MINECRAFT_ACTION_FORWARD: return KDown(GLFW_KEY_W);
|
||||
case MINECRAFT_ACTION_BACKWARD: return KDown(GLFW_KEY_S);
|
||||
case MINECRAFT_ACTION_LEFT: return KDown(GLFW_KEY_A);
|
||||
case MINECRAFT_ACTION_RIGHT: return KDown(GLFW_KEY_D);
|
||||
|
||||
// Look axes are handled by analog stick RX/RY (mouse)
|
||||
case MINECRAFT_ACTION_LOOK_LEFT: return false;
|
||||
case MINECRAFT_ACTION_LOOK_RIGHT: return false;
|
||||
case MINECRAFT_ACTION_LOOK_UP: return false;
|
||||
case MINECRAFT_ACTION_LOOK_DOWN: return false;
|
||||
|
||||
case MINECRAFT_ACTION_USE: return MouseRDown();
|
||||
case MINECRAFT_ACTION_ACTION: return MouseLDown();
|
||||
case MINECRAFT_ACTION_LEFT_SCROLL: return WheelDown();
|
||||
case MINECRAFT_ACTION_RIGHT_SCROLL: return WheelUp();
|
||||
|
||||
case MINECRAFT_ACTION_INVENTORY: return KDown(GLFW_KEY_E);
|
||||
case MINECRAFT_ACTION_PAUSEMENU: return KDown(GLFW_KEY_ESCAPE);
|
||||
case MINECRAFT_ACTION_DROP: return KDown(GLFW_KEY_Q);
|
||||
case MINECRAFT_ACTION_SNEAK_TOGGLE: return KDown(GLFW_KEY_LEFT_SHIFT);
|
||||
case MINECRAFT_ACTION_CRAFTING: return KDown(GLFW_KEY_F);
|
||||
case MINECRAFT_ACTION_RENDER_THIRD_PERSON: return KDown(GLFW_KEY_F5);
|
||||
case MINECRAFT_ACTION_GAME_INFO: return KDown(GLFW_KEY_F3);
|
||||
|
||||
case MINECRAFT_ACTION_DPAD_LEFT: return KDown(GLFW_KEY_LEFT);
|
||||
case MINECRAFT_ACTION_DPAD_RIGHT: return KDown(GLFW_KEY_RIGHT);
|
||||
case MINECRAFT_ACTION_DPAD_UP: return KDown(GLFW_KEY_UP);
|
||||
case MINECRAFT_ACTION_DPAD_DOWN: return KDown(GLFW_KEY_DOWN);
|
||||
|
||||
default: return false;
|
||||
case MINECRAFT_ACTION_ACTION: return MouseLDown() || KDown(SDL_SCANCODE_RETURN);
|
||||
case MINECRAFT_ACTION_USE: return MouseRDown() || KDown(SDL_SCANCODE_F);
|
||||
case MINECRAFT_ACTION_SNEAK_TOGGLE: return KDown(SDL_SCANCODE_LSHIFT) || KDown(SDL_SCANCODE_RSHIFT) || KDown(SDL_SCANCODE_LCTRL) || KDown(SDL_SCANCODE_RCTRL);
|
||||
case MINECRAFT_ACTION_LEFT_SCROLL:
|
||||
case ACTION_MENU_LEFT_SCROLL: return ScrollSnap() > 0.1f;
|
||||
case MINECRAFT_ACTION_RIGHT_SCROLL:
|
||||
case ACTION_MENU_RIGHT_SCROLL: return ScrollSnap() < -0.1f;
|
||||
ACTION_CASES(KDown)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ButtonPressed – rising edge (press event this frame)
|
||||
// ---------------------------------------------------------------------------
|
||||
bool C_4JInput::ButtonPressed(int /*iPad*/, unsigned char ucAction) {
|
||||
// The part that handles completing the action of pressing a button.
|
||||
bool C_4JInput::ButtonPressed(int iPad, unsigned char ucAction) {
|
||||
if (iPad != 0 || ucAction == 255) return false;
|
||||
switch (ucAction) {
|
||||
case ACTION_MENU_UP: return KPressed(GLFW_KEY_UP) || KPressed(GLFW_KEY_W);
|
||||
case ACTION_MENU_DOWN: return KPressed(GLFW_KEY_DOWN) || KPressed(GLFW_KEY_S);
|
||||
case ACTION_MENU_LEFT: return KPressed(GLFW_KEY_LEFT) || KPressed(GLFW_KEY_A);
|
||||
case ACTION_MENU_RIGHT: return KPressed(GLFW_KEY_RIGHT) || KPressed(GLFW_KEY_D);
|
||||
|
||||
case ACTION_MENU_A:
|
||||
case ACTION_MENU_OK:
|
||||
return KPressed(GLFW_KEY_ENTER) || KPressed(GLFW_KEY_SPACE);
|
||||
|
||||
case ACTION_MENU_B:
|
||||
case ACTION_MENU_CANCEL:
|
||||
return KPressed(GLFW_KEY_ESCAPE);
|
||||
|
||||
case ACTION_MENU_X: return KPressed(GLFW_KEY_F);
|
||||
case ACTION_MENU_Y: return KPressed(GLFW_KEY_E);
|
||||
|
||||
case ACTION_MENU_PAGEUP: return KPressed(GLFW_KEY_PAGE_UP) || KPressed(GLFW_KEY_LEFT_SHIFT);
|
||||
case ACTION_MENU_PAGEDOWN: return KPressed(GLFW_KEY_PAGE_DOWN) || KPressed(GLFW_KEY_RIGHT_SHIFT);
|
||||
case ACTION_MENU_RIGHT_SCROLL: return KPressed(GLFW_KEY_RIGHT_SHIFT) || WheelUpEdge();
|
||||
case ACTION_MENU_LEFT_SCROLL: return KPressed(GLFW_KEY_LEFT_SHIFT) || WheelDownEdge();
|
||||
|
||||
case ACTION_MENU_STICK_PRESS: return false;
|
||||
case ACTION_MENU_OTHER_STICK_PRESS: return false;
|
||||
case ACTION_MENU_OTHER_STICK_UP: return KPressed(GLFW_KEY_UP);
|
||||
case ACTION_MENU_OTHER_STICK_DOWN: return KPressed(GLFW_KEY_DOWN);
|
||||
case ACTION_MENU_OTHER_STICK_LEFT: return KPressed(GLFW_KEY_LEFT);
|
||||
case ACTION_MENU_OTHER_STICK_RIGHT: return KPressed(GLFW_KEY_RIGHT);
|
||||
case ACTION_MENU_PAUSEMENU: return KPressed(GLFW_KEY_ESCAPE);
|
||||
|
||||
case MINECRAFT_ACTION_JUMP: return KPressed(GLFW_KEY_SPACE);
|
||||
case MINECRAFT_ACTION_FORWARD: return KPressed(GLFW_KEY_W);
|
||||
case MINECRAFT_ACTION_BACKWARD: return KPressed(GLFW_KEY_S);
|
||||
case MINECRAFT_ACTION_LEFT: return KPressed(GLFW_KEY_A);
|
||||
case MINECRAFT_ACTION_RIGHT: return KPressed(GLFW_KEY_D);
|
||||
|
||||
case MINECRAFT_ACTION_LOOK_LEFT:
|
||||
case MINECRAFT_ACTION_LOOK_RIGHT:
|
||||
case MINECRAFT_ACTION_LOOK_UP:
|
||||
case MINECRAFT_ACTION_LOOK_DOWN:
|
||||
return false;
|
||||
|
||||
case MINECRAFT_ACTION_USE: return MouseRPressed();
|
||||
case MINECRAFT_ACTION_ACTION: return MouseLPressed();
|
||||
case MINECRAFT_ACTION_LEFT_SCROLL: return WheelDownEdge();
|
||||
case MINECRAFT_ACTION_RIGHT_SCROLL: return WheelUpEdge();
|
||||
|
||||
case MINECRAFT_ACTION_INVENTORY: return KPressed(GLFW_KEY_E);
|
||||
case MINECRAFT_ACTION_PAUSEMENU: return KPressed(GLFW_KEY_ESCAPE);
|
||||
case MINECRAFT_ACTION_DROP: return KPressed(GLFW_KEY_Q);
|
||||
case MINECRAFT_ACTION_SNEAK_TOGGLE: return KPressed(GLFW_KEY_LEFT_SHIFT);
|
||||
case MINECRAFT_ACTION_CRAFTING: return KPressed(GLFW_KEY_F);
|
||||
case MINECRAFT_ACTION_RENDER_THIRD_PERSON: return KPressed(GLFW_KEY_F5);
|
||||
case MINECRAFT_ACTION_GAME_INFO: return KPressed(GLFW_KEY_F3);
|
||||
|
||||
case MINECRAFT_ACTION_DPAD_LEFT: return KPressed(GLFW_KEY_LEFT);
|
||||
case MINECRAFT_ACTION_DPAD_RIGHT: return KPressed(GLFW_KEY_RIGHT);
|
||||
case MINECRAFT_ACTION_DPAD_UP: return KPressed(GLFW_KEY_UP);
|
||||
case MINECRAFT_ACTION_DPAD_DOWN: return KPressed(GLFW_KEY_DOWN);
|
||||
|
||||
default: return false;
|
||||
case MINECRAFT_ACTION_ACTION: return MouseLPressed() || KPressed(SDL_SCANCODE_RETURN);
|
||||
case MINECRAFT_ACTION_USE: return MouseRPressed() || KPressed(SDL_SCANCODE_F);
|
||||
case MINECRAFT_ACTION_SNEAK_TOGGLE: return KPressed(SDL_SCANCODE_LSHIFT) || KPressed(SDL_SCANCODE_RSHIFT) || KPressed(SDL_SCANCODE_LCTRL) || KPressed(SDL_SCANCODE_RCTRL);
|
||||
case MINECRAFT_ACTION_LEFT_SCROLL:
|
||||
case ACTION_MENU_LEFT_SCROLL: return ScrollSnap() > 0.1f;
|
||||
case MINECRAFT_ACTION_RIGHT_SCROLL:
|
||||
case ACTION_MENU_RIGHT_SCROLL: return ScrollSnap() < -0.1f;
|
||||
ACTION_CASES(KPressed)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ButtonReleased – falling edge (released this frame)
|
||||
// ---------------------------------------------------------------------------
|
||||
bool C_4JInput::ButtonReleased(int /*iPad*/, unsigned char ucAction) {
|
||||
// The part that handles Releasing a button.
|
||||
bool C_4JInput::ButtonReleased(int iPad, unsigned char ucAction) {
|
||||
if (iPad != 0 || ucAction == 255) return false;
|
||||
switch (ucAction) {
|
||||
case ACTION_MENU_UP: return KReleased(GLFW_KEY_UP) || KReleased(GLFW_KEY_W);
|
||||
case ACTION_MENU_DOWN: return KReleased(GLFW_KEY_DOWN) || KReleased(GLFW_KEY_S);
|
||||
case ACTION_MENU_LEFT: return KReleased(GLFW_KEY_LEFT) || KReleased(GLFW_KEY_A);
|
||||
case ACTION_MENU_RIGHT: return KReleased(GLFW_KEY_RIGHT) || KReleased(GLFW_KEY_D);
|
||||
|
||||
case ACTION_MENU_A:
|
||||
case ACTION_MENU_OK:
|
||||
return KReleased(GLFW_KEY_ENTER) || KReleased(GLFW_KEY_SPACE);
|
||||
|
||||
case ACTION_MENU_B:
|
||||
case ACTION_MENU_CANCEL:
|
||||
return KReleased(GLFW_KEY_ESCAPE);
|
||||
|
||||
case ACTION_MENU_X: return KReleased(GLFW_KEY_F);
|
||||
case ACTION_MENU_Y: return KReleased(GLFW_KEY_E);
|
||||
|
||||
case ACTION_MENU_PAGEUP: return KReleased(GLFW_KEY_PAGE_UP) || KReleased(GLFW_KEY_LEFT_SHIFT);
|
||||
case ACTION_MENU_PAGEDOWN: return KReleased(GLFW_KEY_PAGE_DOWN) || KReleased(GLFW_KEY_RIGHT_SHIFT);
|
||||
case ACTION_MENU_RIGHT_SCROLL: return KReleased(GLFW_KEY_RIGHT_SHIFT);
|
||||
case ACTION_MENU_LEFT_SCROLL: return KReleased(GLFW_KEY_LEFT_SHIFT);
|
||||
|
||||
case ACTION_MENU_STICK_PRESS: return false;
|
||||
case ACTION_MENU_OTHER_STICK_PRESS: return false;
|
||||
case ACTION_MENU_OTHER_STICK_UP: return KReleased(GLFW_KEY_UP);
|
||||
case ACTION_MENU_OTHER_STICK_DOWN: return KReleased(GLFW_KEY_DOWN);
|
||||
case ACTION_MENU_OTHER_STICK_LEFT: return KReleased(GLFW_KEY_LEFT);
|
||||
case ACTION_MENU_OTHER_STICK_RIGHT: return KReleased(GLFW_KEY_RIGHT);
|
||||
case ACTION_MENU_PAUSEMENU: return KReleased(GLFW_KEY_ESCAPE);
|
||||
|
||||
case MINECRAFT_ACTION_JUMP: return KReleased(GLFW_KEY_SPACE);
|
||||
case MINECRAFT_ACTION_FORWARD: return KReleased(GLFW_KEY_W);
|
||||
case MINECRAFT_ACTION_BACKWARD: return KReleased(GLFW_KEY_S);
|
||||
case MINECRAFT_ACTION_LEFT: return KReleased(GLFW_KEY_A);
|
||||
case MINECRAFT_ACTION_RIGHT: return KReleased(GLFW_KEY_D);
|
||||
|
||||
case MINECRAFT_ACTION_LOOK_LEFT:
|
||||
case MINECRAFT_ACTION_LOOK_RIGHT:
|
||||
case MINECRAFT_ACTION_LOOK_UP:
|
||||
case MINECRAFT_ACTION_LOOK_DOWN:
|
||||
return false;
|
||||
|
||||
case MINECRAFT_ACTION_USE: return MouseRReleased();
|
||||
case MINECRAFT_ACTION_ACTION: return MouseLReleased();
|
||||
case MINECRAFT_ACTION_LEFT_SCROLL: return false; // scroll wheel has no "release"
|
||||
case MINECRAFT_ACTION_RIGHT_SCROLL: return false;
|
||||
|
||||
case MINECRAFT_ACTION_INVENTORY: return KReleased(GLFW_KEY_E);
|
||||
case MINECRAFT_ACTION_PAUSEMENU: return KReleased(GLFW_KEY_ESCAPE);
|
||||
case MINECRAFT_ACTION_DROP: return KReleased(GLFW_KEY_Q);
|
||||
case MINECRAFT_ACTION_SNEAK_TOGGLE: return KReleased(GLFW_KEY_LEFT_SHIFT);
|
||||
case MINECRAFT_ACTION_CRAFTING: return KReleased(GLFW_KEY_F);
|
||||
case MINECRAFT_ACTION_RENDER_THIRD_PERSON: return KReleased(GLFW_KEY_F5);
|
||||
case MINECRAFT_ACTION_GAME_INFO: return KReleased(GLFW_KEY_F3);
|
||||
|
||||
case MINECRAFT_ACTION_DPAD_LEFT: return KReleased(GLFW_KEY_LEFT);
|
||||
case MINECRAFT_ACTION_DPAD_RIGHT: return KReleased(GLFW_KEY_RIGHT);
|
||||
case MINECRAFT_ACTION_DPAD_UP: return KReleased(GLFW_KEY_UP);
|
||||
case MINECRAFT_ACTION_DPAD_DOWN: return KReleased(GLFW_KEY_DOWN);
|
||||
|
||||
default: return false;
|
||||
case MINECRAFT_ACTION_ACTION: return MouseLReleased() || KReleased(SDL_SCANCODE_RETURN);
|
||||
case MINECRAFT_ACTION_USE: return MouseRReleased() || KReleased(SDL_SCANCODE_F);
|
||||
case MINECRAFT_ACTION_SNEAK_TOGGLE: return KReleased(SDL_SCANCODE_LSHIFT) || KReleased(SDL_SCANCODE_RSHIFT) || KReleased(SDL_SCANCODE_LCTRL) || KReleased(SDL_SCANCODE_RCTRL);
|
||||
case MINECRAFT_ACTION_LEFT_SCROLL:
|
||||
case ACTION_MENU_LEFT_SCROLL:
|
||||
case MINECRAFT_ACTION_RIGHT_SCROLL:
|
||||
case ACTION_MENU_RIGHT_SCROLL: return false;
|
||||
ACTION_CASES(KReleased)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GetValue – returns 1 if action held, 0 otherwise
|
||||
// ---------------------------------------------------------------------------
|
||||
unsigned int C_4JInput::GetValue(int iPad, unsigned char ucAction, bool /*bRepeat*/) {
|
||||
unsigned int C_4JInput::GetValue(int iPad, unsigned char ucAction, bool) {
|
||||
return ButtonDown(iPad, ucAction) ? 1u : 0u;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Analog sticks
|
||||
//
|
||||
// Left stick = WASD keyboard (±1.0)
|
||||
// Right stick = mouse delta with sqrt-compression to linearise the quadratic
|
||||
// look-speed formula used by the console game code:
|
||||
// turnSpeed = rx * |rx| * 50 * sensitivity
|
||||
// Passing sqrt(|raw|)*sign(raw) makes turn speed proportional
|
||||
// to raw pixel delta, giving a natural mouse feel.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
float C_4JInput::GetJoypadStick_LX(int /*iPad*/, bool /*bCheckMenuDisplay*/) {
|
||||
// Return 0 while cursor is still in menu mode so Input::tick()'s
|
||||
// lReset guard sees a zero-stick frame during the menu->game transition.
|
||||
// Once s_mouseLocked becomes true (cursor captured for gameplay), return
|
||||
// the real WASD values.
|
||||
if (!s_mouseLocked) return 0.0f;
|
||||
float v = 0.0f;
|
||||
if (KDown(GLFW_KEY_A)) v -= 1.0f;
|
||||
if (KDown(GLFW_KEY_D)) v += 1.0f;
|
||||
return v;
|
||||
// Left stick movement, the one that moves the player around or selects menu options. (Soon be tested.)
|
||||
float C_4JInput::GetJoypadStick_LX(int, bool) {
|
||||
return (KDown(SDL_SCANCODE_D) ? 1.f : 0.f) - (KDown(SDL_SCANCODE_A) ? 1.f : 0.f);
|
||||
}
|
||||
float C_4JInput::GetJoypadStick_LY(int, bool) {
|
||||
return (KDown(SDL_SCANCODE_W) ? 1.f : 0.f) - (KDown(SDL_SCANCODE_S) ? 1.f : 0.f);
|
||||
}
|
||||
|
||||
float C_4JInput::GetJoypadStick_LY(int /*iPad*/, bool /*bCheckMenuDisplay*/) {
|
||||
if (!s_mouseLocked) return 0.0f;
|
||||
float v = 0.0f;
|
||||
if (KDown(GLFW_KEY_W)) v += 1.0f; // W = forward = negative Y on consoles
|
||||
if (KDown(GLFW_KEY_S)) v -= 1.0f;
|
||||
return v;
|
||||
// We use mouse movement and convert it into a Right Stick output using logarithmic scaling
|
||||
// This is the most important mouse part. Yet it's so small.
|
||||
static float MouseAxis(float raw) {
|
||||
if (fabsf(raw) < 0.0001f) return 0.f; // from glfw previous code
|
||||
return (raw >= 0.f ? 1.f : -1.f) * sqrtf(fabsf(raw));
|
||||
}
|
||||
// We apply the Stick movement on the R(Right) X(2D Position)
|
||||
float C_4JInput::GetJoypadStick_RX(int, bool) {
|
||||
if (!SDL_GetRelativeMouseMode()) return 0.f;
|
||||
TakeSnapIfNeeded();
|
||||
return MouseAxis(s_snapRelX * MOUSE_SCALE);
|
||||
}
|
||||
// Bis. but with Y(2D Position)
|
||||
float C_4JInput::GetJoypadStick_RY(int, bool) {
|
||||
if (!SDL_GetRelativeMouseMode()) return 0.f;
|
||||
TakeSnapIfNeeded();
|
||||
return MouseAxis(-s_snapRelY * MOUSE_SCALE);
|
||||
}
|
||||
|
||||
float C_4JInput::GetJoypadStick_RX(int /*iPad*/, bool /*bCheckMenuDisplay*/) {
|
||||
if (!s_mouseLocked) return 0.0f;
|
||||
float raw = s_frameRelX * MOUSE_SCALE;
|
||||
float absRaw = fabsf(raw);
|
||||
if (absRaw > 1.0f) absRaw = 1.0f;
|
||||
if (absRaw < 0.0001f) return 0.0f;
|
||||
return (raw >= 0.0f ? 1.0f : -1.0f) * sqrtf(absRaw);
|
||||
}
|
||||
|
||||
float C_4JInput::GetJoypadStick_RY(int /*iPad*/, bool /*bCheckMenuDisplay*/) {
|
||||
if (!s_mouseLocked) return 0.0f;
|
||||
float raw = -s_frameRelY * MOUSE_SCALE;
|
||||
float absRaw = fabsf(raw);
|
||||
if (absRaw > 1.0f) absRaw = 1.0f;
|
||||
if (absRaw < 0.0001f) return 0.0f;
|
||||
return (raw >= 0.0f ? 1.0f : -1.0f) * sqrtf(absRaw);
|
||||
}
|
||||
|
||||
// Left trigger = right mouse button (use/place)
|
||||
// Right trigger = left mouse button (attack/destroy)
|
||||
unsigned char C_4JInput::GetJoypadLTrigger(int /*iPad*/, bool /*bCheckMenuDisplay*/) {
|
||||
return s_mouseRightCurrent ? 255 : 0;
|
||||
}
|
||||
unsigned char C_4JInput::GetJoypadRTrigger(int /*iPad*/, bool /*bCheckMenuDisplay*/) {
|
||||
return s_mouseLeftCurrent ? 255 : 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Joypad map / sensitivity stubs (not meaningful for keyboard+mouse)
|
||||
// ---------------------------------------------------------------------------
|
||||
void C_4JInput::SetDeadzoneAndMovementRange(unsigned int /*uiDeadzone*/, unsigned int /*uiMovementRangeMax*/) {}
|
||||
void C_4JInput::SetGameJoypadMaps(unsigned char /*ucMap*/, unsigned char /*ucAction*/, unsigned int /*uiActionVal*/) {}
|
||||
unsigned int C_4JInput::GetGameJoypadMaps(unsigned char /*ucMap*/, unsigned char /*ucAction*/) { return 0; }
|
||||
void C_4JInput::SetJoypadMapVal(int /*iPad*/, unsigned char /*ucMap*/) {}
|
||||
unsigned char C_4JInput::GetJoypadMapVal(int /*iPad*/) { return 0; }
|
||||
void C_4JInput::SetJoypadSensitivity(int /*iPad*/, float /*fSensitivity*/) {}
|
||||
void C_4JInput::SetJoypadStickAxisMap(int /*iPad*/, unsigned int /*uiFrom*/, unsigned int /*uiTo*/) {}
|
||||
void C_4JInput::SetJoypadStickTriggerMap(int /*iPad*/, unsigned int /*uiFrom*/, unsigned int /*uiTo*/) {}
|
||||
void C_4JInput::SetKeyRepeatRate(float /*fRepeatDelaySecs*/, float /*fRepeatRateSecs*/) {}
|
||||
void C_4JInput::SetDebugSequence(const char * /*chSequenceA*/, int(*/*Func*/)(LPVOID), LPVOID /*lpParam*/) {}
|
||||
FLOAT C_4JInput::GetIdleSeconds(int /*iPad*/) { return 0.0f; }
|
||||
bool C_4JInput::IsPadConnected(int iPad) { return iPad == 0; } // slot 0 = keyboard+mouse
|
||||
|
||||
unsigned char C_4JInput::GetJoypadLTrigger(int, bool) { return s_mouseRightCurrent ? 255 : 0; }
|
||||
unsigned char C_4JInput::GetJoypadRTrigger(int, bool) { return s_mouseLeftCurrent ? 255 : 0; }
|
||||
// We detect if a Menu is visible on the player's screen to the mouse being stuck.
|
||||
void C_4JInput::SetMenuDisplayed(int iPad, bool bVal) {
|
||||
if (iPad >= 0 && iPad < 4) s_menuDisplayed[iPad] = bVal;
|
||||
if (!s_sdlInitialized || bVal == s_prevMenuDisplayed) return;
|
||||
SDL_SetRelativeMouseMode(bVal ? SDL_FALSE : SDL_TRUE);
|
||||
s_prevMenuDisplayed = bVal;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Keyboard (text entry) / string verification stubs
|
||||
// ---------------------------------------------------------------------------
|
||||
EKeyboardResult C_4JInput::RequestKeyboard(LPCWSTR /*Title*/, LPCWSTR /*Text*/, DWORD /*dwPad*/,
|
||||
UINT /*uiMaxChars*/,
|
||||
int(*/*Func*/)(LPVOID, const bool), LPVOID /*lpParam*/,
|
||||
C_4JInput::EKeyboardMode /*eMode*/) {
|
||||
return EKeyboard_Cancelled;
|
||||
}
|
||||
void C_4JInput::GetText(uint16_t *UTF16String) { if (UTF16String) UTF16String[0] = 0; }
|
||||
bool C_4JInput::VerifyStrings(WCHAR ** /*pwStringA*/, int /*iStringC*/,
|
||||
int(*/*Func*/)(LPVOID, STRING_VERIFY_RESPONSE *), LPVOID /*lpParam*/) { return true; }
|
||||
void C_4JInput::CancelQueuedVerifyStrings(int(*/*Func*/)(LPVOID, STRING_VERIFY_RESPONSE *), LPVOID /*lpParam*/) {}
|
||||
void C_4JInput::CancelAllVerifyInProgress(void) {}
|
||||
void C_4JInput::SetDeadzoneAndMovementRange(unsigned int, unsigned int){}
|
||||
void C_4JInput::SetGameJoypadMaps(unsigned char, unsigned char, unsigned int){}
|
||||
unsigned int C_4JInput::GetGameJoypadMaps(unsigned char, unsigned char){ return 0; }
|
||||
void C_4JInput::SetJoypadMapVal(int, unsigned char){}
|
||||
unsigned char C_4JInput::GetJoypadMapVal(int){ return 0; }
|
||||
void C_4JInput::SetJoypadSensitivity(int, float){}
|
||||
void C_4JInput::SetJoypadStickAxisMap(int, unsigned int, unsigned int){}
|
||||
void C_4JInput::SetJoypadStickTriggerMap(int, unsigned int, unsigned int){}
|
||||
void C_4JInput::SetKeyRepeatRate(float, float){}
|
||||
void C_4JInput::SetDebugSequence(const char*, int(*)(LPVOID), LPVOID){}
|
||||
FLOAT C_4JInput::GetIdleSeconds(int){ return 0.f; }
|
||||
bool C_4JInput::IsPadConnected(int iPad){ return iPad == 0; }
|
||||
|
||||
// Silly check, we check if we have a keyboard.
|
||||
EKeyboardResult C_4JInput::RequestKeyboard(LPCWSTR, LPCWSTR, DWORD, UINT,
|
||||
int(*)(LPVOID, const bool), LPVOID, C_4JInput::EKeyboardMode)
|
||||
{ return EKeyboard_Cancelled; }
|
||||
|
||||
void C_4JInput::GetText(uint16_t *s){ if (s) s[0] = 0; }
|
||||
bool C_4JInput::VerifyStrings(WCHAR**, int, int(*)(LPVOID, STRING_VERIFY_RESPONSE*), LPVOID){ return true; }
|
||||
void C_4JInput::CancelQueuedVerifyStrings(int(*)(LPVOID, STRING_VERIFY_RESPONSE*), LPVOID){}
|
||||
void C_4JInput::CancelAllVerifyInProgress(){}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,11 @@ lib_input = static_library('4J_Input',
|
|||
],
|
||||
)
|
||||
|
||||
# We import SDL2 but not SDL3.. which is a bit sad,
|
||||
sdl_dep = dependency('sdl2', required : false)
|
||||
|
||||
input_dep = declare_dependency(
|
||||
link_with : lib_input,
|
||||
dependencies : [sdl_dep],
|
||||
include_directories : include_directories('.'),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
|
||||
// TODO: ADD BETTER COMMENTS.
|
||||
#include "4J_Render.h"
|
||||
#include <cstring>
|
||||
#include <cstdlib> // getenv
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#include <GL/glext.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
#include <pthread.h>
|
||||
|
|
@ -18,38 +15,53 @@
|
|||
|
||||
C4JRender RenderManager;
|
||||
|
||||
static GLFWwindow *s_window = nullptr;
|
||||
// Hello SDL!
|
||||
static SDL_Window *s_window = nullptr;
|
||||
static SDL_GLContext s_glContext = nullptr;
|
||||
static bool s_shouldClose = false;
|
||||
static int s_textureLevels = 1;
|
||||
static int s_windowWidth = 1280; // updated to actual framebuffer size each frame
|
||||
static int s_windowHeight = 720;
|
||||
static int s_reqWidth = 0; // 0 = auto-detect from primary monitor
|
||||
static int s_windowWidth = 0;
|
||||
static int s_windowHeight = 0;
|
||||
|
||||
// We set Window size with the monitor's res, so that I can get rid of ugly values.
|
||||
static void SetInitialWindowSize()
|
||||
{
|
||||
int w = 0, h = 0;
|
||||
if (SDL_Init(SDL_INIT_VIDEO) == 0) {
|
||||
SDL_DisplayMode mode;
|
||||
if (SDL_GetCurrentDisplayMode(0, &mode) == 0) {
|
||||
w = (int)(mode.w * 0.4f);
|
||||
h = (int)(mode.h * 0.4f);
|
||||
}
|
||||
}
|
||||
if (w > 0 && h > 0) { s_windowWidth = w; s_windowHeight = h; }
|
||||
else { s_windowWidth = 1280; s_windowHeight = 720; }
|
||||
}
|
||||
// (can't believe i had to rewrite this, i literally did it TODAY.)
|
||||
static int s_reqWidth = 0;
|
||||
static int s_reqHeight = 0;
|
||||
// When we'll have a settings system in order, we'll set bool to that value, right now it's hardcoded.
|
||||
static bool s_fullscreen = false;
|
||||
|
||||
// Thread-local storage for per-thread shared GL contexts.
|
||||
// The main thread uses s_window directly; worker threads get invisible
|
||||
// windows that share objects (textures, display lists) with s_window.
|
||||
static pthread_key_t s_glCtxKey;
|
||||
static pthread_once_t s_glCtxKeyOnce = PTHREAD_ONCE_INIT;
|
||||
static void makeGLCtxKey() { pthread_key_create(&s_glCtxKey, nullptr); }
|
||||
|
||||
// Pre-created pool of shared contexts for worker threads
|
||||
|
||||
// AMD drivers (especially on Linux/Mesa) can be very sensitive to the number of shared contexts
|
||||
// and concurrent display list compilation. 8 was original, 4 was an attempt to fix it.
|
||||
// 6 covers the 5 concurrent worker threads (update + 3x rebuild + main thread).
|
||||
static const int MAX_SHARED_CONTEXTS = 6;
|
||||
static GLFWwindow *s_sharedContexts[MAX_SHARED_CONTEXTS] = {};
|
||||
// Do not touch exactly this number |
|
||||
static const int MAX_SHARED_CONTEXTS = 6; // <- this one, do not touch
|
||||
static SDL_Window *s_sharedContextWindows[MAX_SHARED_CONTEXTS] = {};
|
||||
static SDL_GLContext s_sharedContexts[MAX_SHARED_CONTEXTS] = {};
|
||||
static int s_sharedContextCount = 0;
|
||||
static int s_nextSharedContext = 0;
|
||||
static pthread_mutex_t s_sharedCtxMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
// Tells thread to do Direct GL calls, just don't touch.
|
||||
static pthread_mutex_t s_glCallMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// Track which thread is the main (rendering) thread
|
||||
static pthread_t s_mainThread;
|
||||
static bool s_mainThreadSet = false;
|
||||
|
||||
// viewport go brr
|
||||
static void onFramebufferResize(GLFWwindow * /*win*/, int w, int h)
|
||||
static void onFramebufferResize(int w, int h)
|
||||
{
|
||||
if (w < 1) w = 1;
|
||||
if (h < 1) h = 1;
|
||||
|
|
@ -58,48 +70,62 @@ static void onFramebufferResize(GLFWwindow * /*win*/, int w, int h)
|
|||
::glViewport(0, 0, w, h);
|
||||
}
|
||||
|
||||
// Initialize OpenGL & The SDL window.
|
||||
void C4JRender::Initialise()
|
||||
{
|
||||
if (!glfwInit()) {
|
||||
fprintf(stderr, "[4J_Render] Failed to initialise GLFW\n");
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||||
fprintf(stderr, "[4J_Render] Failed to initialise SDL: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
GLFWmonitor *primaryMonitor = glfwGetPrimaryMonitor();
|
||||
const GLFWvidmode *mode = primaryMonitor ? glfwGetVideoMode(primaryMonitor) : nullptr;
|
||||
SDL_DisplayMode mode;
|
||||
int haveMode = (SDL_GetCurrentDisplayMode(0, &mode) == 0);
|
||||
|
||||
if (s_reqWidth > 0 && s_reqHeight > 0) {
|
||||
s_windowWidth = s_reqWidth;
|
||||
s_windowHeight = s_reqHeight;
|
||||
} else if (mode) {
|
||||
s_windowWidth = mode->width;
|
||||
s_windowHeight = mode->height;
|
||||
} else if (haveMode) {
|
||||
s_windowWidth = mode.w;
|
||||
s_windowHeight = mode.h;
|
||||
}
|
||||
fprintf(stderr, "[4J_Render] Window %dx%d fullscreen=%s\n",
|
||||
s_windowWidth, s_windowHeight, s_fullscreen ? "yes" : "no");
|
||||
fflush(stderr);
|
||||
|
||||
// opengl 2.1!!!
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
|
||||
glfwWindowHint(GLFW_DEPTH_BITS, 24);
|
||||
glfwWindowHint(GLFW_STENCIL_BITS, 8);
|
||||
// Setting the sdl_gl ver. Change in future incase we want to use shaders
|
||||
// Yes i'm still using fixed functions, get mad at me
|
||||
// I don't care.
|
||||
// Im not gonna be rewriting the whole renderer.. AGAIN. ;w;
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
GLFWmonitor *fsMonitor = s_fullscreen ? primaryMonitor : nullptr;
|
||||
s_window = glfwCreateWindow(s_windowWidth, s_windowHeight,
|
||||
"Minecraft Console Edition", fsMonitor, nullptr);
|
||||
Uint32 winFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
|
||||
if (s_fullscreen) winFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
s_window = SDL_CreateWindow("Minecraft Console Edition",
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
s_windowWidth, s_windowHeight,
|
||||
winFlags);
|
||||
if (!s_window) {
|
||||
fprintf(stderr, "[4J_Render] Failed to create GLFW window\n");
|
||||
glfwTerminate();
|
||||
fprintf(stderr, "[4J_Render] Failed to create SDL window: %s\n", SDL_GetError());
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent(s_window);
|
||||
glfwSwapInterval(1); // vsync
|
||||
|
||||
// Keep viewport in sync with OS-driven window resizes.
|
||||
glfwSetFramebufferSizeCallback(s_window, onFramebufferResize);
|
||||
s_glContext = SDL_GL_CreateContext(s_window);
|
||||
if (!s_glContext) {
|
||||
fprintf(stderr, "[4J_Render] Failed to create GL context: %s\n", SDL_GetError());
|
||||
SDL_DestroyWindow(s_window);
|
||||
s_window = nullptr;
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
SDL_GL_SetSwapInterval(0); // V-Sync Off Please.
|
||||
|
||||
// init opengl
|
||||
int fw, fh; SDL_GetWindowSize(s_window, &fw, &fh); onFramebufferResize(fw, fh);
|
||||
|
||||
// We initialize the OpenGL states. Touching those values makes some funny artifacts appear.
|
||||
::glEnable(GL_TEXTURE_2D);
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
::glDepthFunc(GL_LEQUAL);
|
||||
|
|
@ -116,7 +142,7 @@ void C4JRender::Initialise()
|
|||
::glViewport(0, 0, s_windowWidth, s_windowHeight);
|
||||
::glEnable(GL_COLOR_MATERIAL);
|
||||
::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
|
||||
|
||||
// Print the renderer's version incase we change it in the future.
|
||||
printf("[4J_Render] OpenGL %s | %s\n",
|
||||
(const char*)::glGetString(GL_VERSION),
|
||||
(const char*)::glGetString(GL_RENDERER));
|
||||
|
|
@ -126,25 +152,34 @@ void C4JRender::Initialise()
|
|||
pthread_once(&s_glCtxKeyOnce, makeGLCtxKey);
|
||||
s_mainThread = pthread_self();
|
||||
s_mainThreadSet = true;
|
||||
pthread_setspecific(s_glCtxKey, s_window);
|
||||
pthread_setspecific(s_glCtxKey, (void*)s_window);
|
||||
|
||||
// Pre-create shared GL contexts for worker threads (chunk builders etc.)
|
||||
// Must be done on the main thread because GLFW requires it.
|
||||
// Ensure they are invisible so they don't interfere with the window manager.
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||
|
||||
// Pre-create shared GL contexts for worker threads (chunk builders & other shit etc.)
|
||||
// SDL_GL_SHARE_WITH_CURRENT_CONTEXT my saviour.
|
||||
for (int i = 0; i < MAX_SHARED_CONTEXTS; i++) {
|
||||
s_sharedContexts[i] = glfwCreateWindow(1, 1, "", nullptr, s_window);
|
||||
if (s_sharedContexts[i]) {
|
||||
s_sharedContextCount++;
|
||||
} else {
|
||||
fprintf(stderr, "[4J_Render] WARN: only created %d/%d shared contexts\n", i, MAX_SHARED_CONTEXTS);
|
||||
SDL_Window *w = SDL_CreateWindow("",
|
||||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
1, 1, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
|
||||
if (!w) break;
|
||||
// Ensure sharing
|
||||
// I've been stuck on this for a while. Im stupid..
|
||||
SDL_GL_MakeCurrent(s_window, s_glContext);
|
||||
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
|
||||
SDL_GLContext ctx = SDL_GL_CreateContext(w);
|
||||
if (!ctx) {
|
||||
SDL_DestroyWindow(w);
|
||||
break;
|
||||
}
|
||||
s_sharedContextWindows[s_sharedContextCount] = w;
|
||||
s_sharedContexts[s_sharedContextCount] = ctx;
|
||||
s_sharedContextCount++;
|
||||
}
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
|
||||
|
||||
// Ensure main thread still has the context
|
||||
glfwMakeContextCurrent(s_window);
|
||||
SDL_GL_MakeCurrent(s_window, s_glContext);
|
||||
fprintf(stderr, "[4J_Render] Created %d shared GL contexts for worker threads\n", s_sharedContextCount);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
|
@ -156,21 +191,25 @@ void C4JRender::InitialiseContext()
|
|||
|
||||
// Main thread reclaiming context (e.g. after startup thread finishes)
|
||||
if (s_mainThreadSet && pthread_equal(pthread_self(), s_mainThread)) {
|
||||
glfwMakeContextCurrent(s_window);
|
||||
pthread_setspecific(s_glCtxKey, s_window);
|
||||
SDL_GL_MakeCurrent(s_window, s_glContext);
|
||||
pthread_setspecific(s_glCtxKey, (void*)s_window);
|
||||
return;
|
||||
}
|
||||
|
||||
// Worker thread: check if it already has a shared context
|
||||
GLFWwindow *ctx = (GLFWwindow*)pthread_getspecific(s_glCtxKey);
|
||||
if (ctx) {
|
||||
glfwMakeContextCurrent(ctx);
|
||||
// Worker thread checks if there's a context, we don't want to have multiple contexts.
|
||||
void *ctxPtr = pthread_getspecific(s_glCtxKey);
|
||||
if (ctxPtr) {
|
||||
// ctxPtr -> SDL_GLContext pointer
|
||||
SDL_GLContext ctx = (SDL_GLContext)ctxPtr;
|
||||
int idx = -1;
|
||||
for (int i = 0; i < s_sharedContextCount; ++i) if (s_sharedContexts[i] == ctx) { idx = i; break; }
|
||||
if (idx >= 0 && s_sharedContextWindows[idx]) SDL_GL_MakeCurrent(s_sharedContextWindows[idx], ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab a pre-created shared context from the pool
|
||||
pthread_mutex_lock(&s_sharedCtxMutex);
|
||||
GLFWwindow *shared = nullptr;
|
||||
SDL_GLContext shared = nullptr;
|
||||
if (s_nextSharedContext < s_sharedContextCount) {
|
||||
shared = s_sharedContexts[s_nextSharedContext++];
|
||||
}
|
||||
|
|
@ -181,7 +220,10 @@ void C4JRender::InitialiseContext()
|
|||
fflush(stderr);
|
||||
return;
|
||||
}
|
||||
glfwMakeContextCurrent(shared);
|
||||
// ewww..... look at line 201-203, we gotta make a function for that....
|
||||
int idx = -1;
|
||||
for (int i = 0; i < s_sharedContextCount; ++i) if (s_sharedContexts[i] == shared) { idx = i; break; }
|
||||
if (idx >= 0 && s_sharedContextWindows[idx]) SDL_GL_MakeCurrent(s_sharedContextWindows[idx], shared);
|
||||
|
||||
// Initialize some basic state for this context to ensure consistent display list recording
|
||||
::glEnable(GL_TEXTURE_2D);
|
||||
|
|
@ -194,7 +236,7 @@ void C4JRender::InitialiseContext()
|
|||
::glEnable(GL_COLOR_MATERIAL);
|
||||
::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
|
||||
|
||||
pthread_setspecific(s_glCtxKey, shared);
|
||||
pthread_setspecific(s_glCtxKey, (void*)shared);
|
||||
fprintf(stderr, "[4J_Render] Assigned shared GL context %p to worker thread %lu\n", (void*)shared, (unsigned long)pthread_self());
|
||||
fflush(stderr);
|
||||
}
|
||||
|
|
@ -202,18 +244,28 @@ void C4JRender::InitialiseContext()
|
|||
void C4JRender::StartFrame()
|
||||
{
|
||||
if (!s_window) return;
|
||||
glfwGetFramebufferSize(s_window, &s_windowWidth, &s_windowHeight);
|
||||
if (s_windowWidth < 1) s_windowWidth = 1;
|
||||
if (s_windowHeight < 1) s_windowHeight = 1;
|
||||
int w,h; SDL_GetWindowSize(s_window, &w, &h);
|
||||
s_windowWidth = w > 0 ? w : 1;
|
||||
s_windowHeight = h > 0 ? h : 1;
|
||||
::glViewport(0, 0, s_windowWidth, s_windowHeight);
|
||||
}
|
||||
|
||||
void C4JRender::Present()
|
||||
{
|
||||
if (!s_window) return;
|
||||
SDL_Event ev;
|
||||
while (SDL_PollEvent(&ev)) {
|
||||
if (ev.type == SDL_QUIT) s_shouldClose = true;
|
||||
else if (ev.type == SDL_WINDOWEVENT) {
|
||||
if (ev.window.event == SDL_WINDOWEVENT_CLOSE) s_shouldClose = true;
|
||||
else if (ev.window.event == SDL_WINDOWEVENT_RESIZED) onFramebufferResize(ev.window.data1, ev.window.data2);
|
||||
}
|
||||
}
|
||||
// Present the rendered frame after processing input/events to avoid input timing issues
|
||||
::glFlush();
|
||||
glfwSwapBuffers(s_window);
|
||||
glfwPollEvents();
|
||||
// debug log to help diagnose mouse issues
|
||||
// printf("[4J_Render] Presenting frame (mouse lock=%d)\n", s_mouseLocked); fflush(stdout);
|
||||
SDL_GL_SwapWindow(s_window);
|
||||
}
|
||||
|
||||
void C4JRender::SetWindowSize(int w, int h)
|
||||
|
|
@ -229,21 +281,38 @@ void C4JRender::SetFullscreen(bool fs)
|
|||
|
||||
bool C4JRender::ShouldClose()
|
||||
{
|
||||
return !s_window || glfwWindowShouldClose(s_window);
|
||||
return !s_window || s_shouldClose;
|
||||
}
|
||||
|
||||
void C4JRender::Shutdown()
|
||||
{
|
||||
// Destroy the main window and terminate GLFW cleanly so that
|
||||
// Destroy the main window and clean up SDL resources so that
|
||||
// destructors running after the game loop don't touch a dead context.
|
||||
if (s_window)
|
||||
{
|
||||
glfwDestroyWindow(s_window);
|
||||
if (s_glContext) {
|
||||
SDL_GL_DeleteContext(s_glContext);
|
||||
s_glContext = nullptr;
|
||||
}
|
||||
SDL_DestroyWindow(s_window);
|
||||
s_window = nullptr;
|
||||
}
|
||||
glfwTerminate();
|
||||
|
||||
for (int i = 0; i < s_sharedContextCount; ++i) {
|
||||
if (s_sharedContexts[i]) {
|
||||
SDL_GL_DeleteContext(s_sharedContexts[i]);
|
||||
s_sharedContexts[i] = 0;
|
||||
}
|
||||
if (s_sharedContextWindows[i]) {
|
||||
SDL_DestroyWindow(s_sharedContextWindows[i]);
|
||||
s_sharedContextWindows[i] = nullptr;
|
||||
}
|
||||
}
|
||||
s_sharedContextCount = 0;
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
// rip glfw. you won't be missed. (i hope)
|
||||
void C4JRender::DoScreenGrabOnNextPresent() {}
|
||||
|
||||
void C4JRender::Clear(int flags)
|
||||
|
|
@ -272,8 +341,8 @@ void C4JRender::MatrixTranslate(float x, float y, float z) { ::glTranslatef(x, y
|
|||
|
||||
void C4JRender::MatrixRotate(float angle, float x, float y, float z)
|
||||
{
|
||||
// can't fucking afford pi
|
||||
::glRotatef(angle * (180.0f / 3.14159265358979f), x, y, z);
|
||||
// We use math from the math lib instead of hardcoding it. How Ugly.
|
||||
::glRotatef(angle * (180.0f / static_cast<float>(M_PI)), x, y, z);
|
||||
}
|
||||
|
||||
void C4JRender::MatrixScale(float x, float y, float z) { ::glScalef(x, y, z); }
|
||||
|
|
@ -284,7 +353,7 @@ void C4JRender::MatrixPerspective(float fovy, float aspect, float zNear, float z
|
|||
}
|
||||
|
||||
void C4JRender::MatrixOrthogonal(float left, float right, float bottom, float top,
|
||||
float zNear, float zFar)
|
||||
float zNear, float zFar)
|
||||
{
|
||||
::glOrtho(left, right, bottom, top, zNear, zFar);
|
||||
}
|
||||
|
|
@ -314,27 +383,29 @@ static GLenum mapPrimType(int pt)
|
|||
|
||||
// Map from ePrimitiveType enum
|
||||
switch (pt) {
|
||||
case C4JRender::PRIMITIVE_TYPE_TRIANGLE_LIST: return GL_TRIANGLES;
|
||||
case C4JRender::PRIMITIVE_TYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
|
||||
case C4JRender::PRIMITIVE_TYPE_TRIANGLE_FAN: return GL_TRIANGLE_FAN;
|
||||
case C4JRender::PRIMITIVE_TYPE_QUAD_LIST: return GL_QUADS;
|
||||
case C4JRender::PRIMITIVE_TYPE_LINE_LIST: return GL_LINES;
|
||||
case C4JRender::PRIMITIVE_TYPE_LINE_STRIP: return GL_LINE_STRIP;
|
||||
default: return GL_TRIANGLES;
|
||||
case C4JRender::PRIMITIVE_TYPE_TRIANGLE_LIST: return GL_TRIANGLES;
|
||||
case C4JRender::PRIMITIVE_TYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP;
|
||||
case C4JRender::PRIMITIVE_TYPE_TRIANGLE_FAN: return GL_TRIANGLE_FAN;
|
||||
case C4JRender::PRIMITIVE_TYPE_QUAD_LIST: return GL_QUADS;
|
||||
case C4JRender::PRIMITIVE_TYPE_LINE_LIST: return GL_LINES;
|
||||
case C4JRender::PRIMITIVE_TYPE_LINE_STRIP: return GL_LINE_STRIP;
|
||||
default: return GL_TRIANGLES;
|
||||
}
|
||||
}
|
||||
|
||||
// clientside awawawa
|
||||
// This is the clientside vertex processing.
|
||||
void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count,
|
||||
void *dataIn, eVertexType vType,
|
||||
C4JRender::ePixelShaderType psType)
|
||||
{
|
||||
if (count <= 0 || !dataIn) return;
|
||||
|
||||
// trash trash trash trash
|
||||
pthread_mutex_lock(&s_glCallMutex);
|
||||
|
||||
GLenum mode = mapPrimType((int)PrimitiveType);
|
||||
|
||||
if (vType == VERTEX_TYPE_COMPRESSED) {
|
||||
// NO NEED TO REWRITE IT ALL YAY
|
||||
int16_t *sdata = (int16_t *)dataIn;
|
||||
::glBegin(mode);
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
|
@ -352,7 +423,7 @@ void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count,
|
|||
|
||||
float fu = vert[4] / 8192.0f;
|
||||
float fv = vert[5] / 8192.0f;
|
||||
// Strip mipmap-disable flag: Tesselator adds +1.0 (= +8192) to u when mipmaps off
|
||||
// Tesselator does that. Thanks 4J.
|
||||
if (fu >= 1.0f) fu -= 1.0f;
|
||||
|
||||
// Unit 1 (lightmap) UVs
|
||||
|
|
@ -388,8 +459,7 @@ void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count,
|
|||
::glNormal3f(nx / 127.0f, ny / 127.0f, nz / 127.0f);
|
||||
}
|
||||
|
||||
// Only override current GL color when the vertex actually carries one.
|
||||
// colorInt == 0 is the Tesselator sentinel for "no colour set"
|
||||
// This breaks particle colors.. i think. fixme!
|
||||
if (colorInt != 0) {
|
||||
::glColor4ub(cr, cg, cb, ca);
|
||||
}
|
||||
|
|
@ -397,6 +467,7 @@ void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count,
|
|||
::glTexCoord2f(fdata[3], fdata[4]);
|
||||
|
||||
// Unit 1 (lightmap) UVs - 0xfe00fe00 is sentinel for "no Unit 1 UVs"
|
||||
// Ugly hack, replace soon.
|
||||
if (tex2Int != 0xfe00fe00) {
|
||||
float u2 = (float)(short)(tex2Int & 0xFFFF) / 256.0f;
|
||||
float v2 = (float)(short)((tex2Int >> 16) & 0xFFFF) / 256.0f;
|
||||
|
|
@ -408,6 +479,8 @@ void C4JRender::DrawVertices(ePrimitiveType PrimitiveType, int count,
|
|||
::glEnd();
|
||||
}
|
||||
::glFlush();
|
||||
|
||||
pthread_mutex_unlock(&s_glCallMutex);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -438,9 +511,9 @@ void C4JRender::CBuffStart(int index, bool /*full*/)
|
|||
|
||||
void C4JRender::CBuffClear(int index)
|
||||
{
|
||||
if (index > 0) {
|
||||
::glNewList(index, GL_COMPILE);
|
||||
::glEndList();
|
||||
if (index > 0) {
|
||||
::glNewList(index, GL_COMPILE);
|
||||
::glEndList();
|
||||
::glFlush();
|
||||
}
|
||||
}
|
||||
|
|
@ -508,9 +581,7 @@ void C4JRender::TextureBindVertex(int idx)
|
|||
|
||||
void C4JRender::TextureSetTextureLevels(int levels)
|
||||
{
|
||||
// Set GL_TEXTURE_MAX_LEVEL so OpenGL knows how many mip levels this texture has.
|
||||
// Without this, the default is 1000, and any texture that doesn't upload all 1000
|
||||
// levels is considered "incomplete" and renders as white.
|
||||
// base level is always 0, no mipmaps sadly. I'll add them later.
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels > 0 ? levels - 1 : 0);
|
||||
s_textureLevels = levels;
|
||||
}
|
||||
|
|
@ -519,8 +590,7 @@ int C4JRender::TextureGetTextureLevels() { return s_textureLevels; }
|
|||
void C4JRender::TextureData(int width, int height, void *data, int level,
|
||||
eTextureFormat /*format*/)
|
||||
{
|
||||
// Game produces [r,g,b,a] bytes via the non-Xbox transferFromImage/loadTexture paths.
|
||||
// Use GL_RGBA so OpenGL interprets them correctly. GL_BGRA would swap R and B channels.
|
||||
// TODO: Check if correct format.
|
||||
::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA,
|
||||
width, height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
|
|
@ -554,7 +624,7 @@ void C4JRender::TextureDynamicUpdateEnd() {}
|
|||
void C4JRender::Tick() {}
|
||||
void C4JRender::UpdateGamma(unsigned short) {}
|
||||
|
||||
// This sucks, but at least better than libpng
|
||||
// Converts RGBA data to the format expected by the texture loader.
|
||||
static HRESULT LoadFromSTB(unsigned char* data, int width, int height, D3DXIMAGE_INFO* pSrcInfo, int** ppDataOut)
|
||||
{
|
||||
int pixelCount = width * height;
|
||||
|
|
@ -614,7 +684,6 @@ HRESULT C4JRender::SaveTextureDataToMemory(void *pOutput, int outputCapacity, in
|
|||
void C4JRender::TextureGetStats() {}
|
||||
void* C4JRender::TextureGetTexture(int idx) { return nullptr; }
|
||||
|
||||
// we handle opengl calls cuz multiplatform is painful!!
|
||||
void C4JRender::StateSetColour(float r, float g, float b, float a)
|
||||
{
|
||||
::glColor4f(r, g, b, a);
|
||||
|
|
@ -755,13 +824,12 @@ void C4JRender::StateSetLightAmbientColour(float red, float green, float blue)
|
|||
float ambient[4] = {red, green, blue, 1.0f};
|
||||
float model[4] = {red, green, blue, 1.0f};
|
||||
::glLightModelfv(GL_LIGHT_MODEL_AMBIENT, model);
|
||||
// Also set on light 0 as a fallback incase
|
||||
::glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
|
||||
}
|
||||
|
||||
void C4JRender::StateSetLightDirection(int light, float x, float y, float z)
|
||||
{
|
||||
float dir[4] = {x, y, z, 0.0f}; // w=0 → directional light
|
||||
float dir[4] = {x, y, z, 0.0f}; // TODO: Java seems to do the reverse, gotta check.
|
||||
::glLightfv(GL_LIGHT0 + light, GL_POSITION, dir);
|
||||
}
|
||||
|
||||
|
|
@ -787,11 +855,11 @@ void C4JRender::StateSetTexGenCol(int col, float x, float y, float z, float w, b
|
|||
{
|
||||
GLenum coord;
|
||||
switch (col) {
|
||||
case 0: coord = GL_S; break;
|
||||
case 1: coord = GL_T; break;
|
||||
case 2: coord = GL_R; break;
|
||||
case 3: coord = GL_Q; break;
|
||||
default: coord = GL_S; break;
|
||||
case 0: coord = GL_S; break;
|
||||
case 1: coord = GL_T; break;
|
||||
case 2: coord = GL_R; break;
|
||||
case 3: coord = GL_Q; break;
|
||||
default: coord = GL_S; break;
|
||||
}
|
||||
float plane[4] = {x, y, z, w};
|
||||
GLenum planeMode = eyeSpace ? GL_EYE_PLANE : GL_OBJECT_PLANE;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ render_sources = files(
|
|||
lib_render = static_library('4J_Render',
|
||||
render_sources,
|
||||
include_directories : include_directories('.'),
|
||||
dependencies : [png_dep, glfw_dep, gl_dep, thread_dep],
|
||||
dependencies : [png_dep, sdl2_dep, gl_dep, thread_dep],
|
||||
cpp_args : global_cpp_args + global_cpp_defs + [
|
||||
'-include', meson.current_source_dir() / 'stdafx.h',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -23,26 +23,26 @@
|
|||
|
||||
// Iggy GDraw support functions - normally in the Iggy library, stubbed here
|
||||
void * IggyGDrawMallocAnnotated(SINTa size, const char *file, int line) {
|
||||
(void)file; (void)line;
|
||||
return malloc((size_t)size);
|
||||
(void)file; (void)line;
|
||||
return malloc((size_t)size);
|
||||
}
|
||||
|
||||
void IggyGDrawFree(void *ptr) {
|
||||
free(ptr);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void IggyGDrawSendWarning(Iggy *f, char const *message, ...) {
|
||||
(void)f;
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
fprintf(stderr, "[Iggy GDraw Warning] ");
|
||||
vfprintf(stderr, message, args);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(args);
|
||||
(void)f;
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
fprintf(stderr, "[Iggy GDraw Warning] ");
|
||||
vfprintf(stderr, message, args);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void IggyDiscardVertexBufferCallback(void *owner, void *buf) {
|
||||
(void)owner; (void)buf;
|
||||
(void)owner; (void)buf;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -56,56 +56,56 @@ void IggyDiscardVertexBufferCallback(void *owner, void *buf) {
|
|||
//
|
||||
|
||||
#define GDRAW_GL_EXTENSION_LIST \
|
||||
/* identifier import procname */ \
|
||||
/* GL_ARB_vertex_buffer_object */ \
|
||||
GLE(GenBuffers, "GenBuffersARB", GENBUFFERSARB) \
|
||||
GLE(DeleteBuffers, "DeleteBuffersARB", DELETEBUFFERSARB) \
|
||||
GLE(BindBuffer, "BindBufferARB", BINDBUFFERARB) \
|
||||
GLE(BufferData, "BufferDataARB", BUFFERDATAARB) \
|
||||
GLE(MapBuffer, "MapBufferARB", MAPBUFFERARB) \
|
||||
GLE(UnmapBuffer, "UnmapBufferARB", UNMAPBUFFERARB) \
|
||||
GLE(VertexAttribPointer, "VertexAttribPointerARB", VERTEXATTRIBPOINTERARB) \
|
||||
GLE(EnableVertexAttribArray, "EnableVertexAttribArrayARB", ENABLEVERTEXATTRIBARRAYARB) \
|
||||
GLE(DisableVertexAttribArray, "DisableVertexAttribArrayARB", DISABLEVERTEXATTRIBARRAYARB) \
|
||||
/* GL_ARB_shader_objects */ \
|
||||
GLE(CreateShader, "CreateShaderObjectARB", CREATESHADEROBJECTARB) \
|
||||
GLE(DeleteShader, "DeleteObjectARB", DELETEOBJECTARB) \
|
||||
GLE(ShaderSource, "ShaderSourceARB", SHADERSOURCEARB) \
|
||||
GLE(CompileShader, "CompileShaderARB", COMPILESHADERARB) \
|
||||
GLE(GetShaderiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \
|
||||
GLE(GetShaderInfoLog, "GetInfoLogARB", GETINFOLOGARB) \
|
||||
GLE(CreateProgram, "CreateProgramObjectARB", CREATEPROGRAMOBJECTARB) \
|
||||
GLE(DeleteProgram, "DeleteObjectARB", DELETEOBJECTARB) \
|
||||
GLE(AttachShader, "AttachObjectARB", ATTACHOBJECTARB) \
|
||||
GLE(LinkProgram, "LinkProgramARB", LINKPROGRAMARB) \
|
||||
GLE(GetUniformLocation, "GetUniformLocationARB", GETUNIFORMLOCATIONARB) \
|
||||
GLE(UseProgram, "UseProgramObjectARB", USEPROGRAMOBJECTARB) \
|
||||
GLE(GetProgramiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \
|
||||
GLE(GetProgramInfoLog, "GetInfoLogARB", GETINFOLOGARB) \
|
||||
GLE(Uniform1i, "Uniform1iARB", UNIFORM1IARB) \
|
||||
GLE(Uniform4f, "Uniform4fARB", UNIFORM4FARB) \
|
||||
GLE(Uniform4fv, "Uniform4fvARB", UNIFORM4FVARB) \
|
||||
/* GL_ARB_vertex_shader */ \
|
||||
GLE(BindAttribLocation, "BindAttribLocationARB", BINDATTRIBLOCATIONARB) \
|
||||
/* Missing from WGL but needed by shared code */ \
|
||||
GLE(Uniform1f, "Uniform1fARB", UNIFORM1FARB) \
|
||||
/* GL_EXT_framebuffer_object */ \
|
||||
GLE(GenRenderbuffers, "GenRenderbuffersEXT", GENRENDERBUFFERSEXT) \
|
||||
GLE(DeleteRenderbuffers, "DeleteRenderbuffersEXT", DELETERENDERBUFFERSEXT) \
|
||||
GLE(BindRenderbuffer, "BindRenderbufferEXT", BINDRENDERBUFFEREXT) \
|
||||
GLE(RenderbufferStorage, "RenderbufferStorageEXT", RENDERBUFFERSTORAGEEXT) \
|
||||
GLE(GenFramebuffers, "GenFramebuffersEXT", GENFRAMEBUFFERSEXT) \
|
||||
GLE(DeleteFramebuffers, "DeleteFramebuffersEXT", DELETEFRAMEBUFFERSEXT) \
|
||||
GLE(BindFramebuffer, "BindFramebufferEXT", BINDFRAMEBUFFEREXT) \
|
||||
GLE(CheckFramebufferStatus, "CheckFramebufferStatusEXT", CHECKFRAMEBUFFERSTATUSEXT) \
|
||||
GLE(FramebufferRenderbuffer, "FramebufferRenderbufferEXT", FRAMEBUFFERRENDERBUFFEREXT) \
|
||||
GLE(FramebufferTexture2D, "FramebufferTexture2DEXT", FRAMEBUFFERTEXTURE2DEXT) \
|
||||
GLE(GenerateMipmap, "GenerateMipmapEXT", GENERATEMIPMAPEXT) \
|
||||
/* GL_EXT_framebuffer_blit */ \
|
||||
GLE(BlitFramebuffer, "BlitFramebufferEXT", BLITFRAMEBUFFEREXT) \
|
||||
/* GL_EXT_framebuffer_multisample */ \
|
||||
GLE(RenderbufferStorageMultisample, "RenderbufferStorageMultisampleEXT",RENDERBUFFERSTORAGEMULTISAMPLEEXT) \
|
||||
/* <end> */
|
||||
/* identifier import procname */ \
|
||||
/* GL_ARB_vertex_buffer_object */ \
|
||||
GLE(GenBuffers, "GenBuffersARB", GENBUFFERSARB) \
|
||||
GLE(DeleteBuffers, "DeleteBuffersARB", DELETEBUFFERSARB) \
|
||||
GLE(BindBuffer, "BindBufferARB", BINDBUFFERARB) \
|
||||
GLE(BufferData, "BufferDataARB", BUFFERDATAARB) \
|
||||
GLE(MapBuffer, "MapBufferARB", MAPBUFFERARB) \
|
||||
GLE(UnmapBuffer, "UnmapBufferARB", UNMAPBUFFERARB) \
|
||||
GLE(VertexAttribPointer, "VertexAttribPointerARB", VERTEXATTRIBPOINTERARB) \
|
||||
GLE(EnableVertexAttribArray, "EnableVertexAttribArrayARB", ENABLEVERTEXATTRIBARRAYARB) \
|
||||
GLE(DisableVertexAttribArray, "DisableVertexAttribArrayARB", DISABLEVERTEXATTRIBARRAYARB) \
|
||||
/* GL_ARB_shader_objects */ \
|
||||
GLE(CreateShader, "CreateShaderObjectARB", CREATESHADEROBJECTARB) \
|
||||
GLE(DeleteShader, "DeleteObjectARB", DELETEOBJECTARB) \
|
||||
GLE(ShaderSource, "ShaderSourceARB", SHADERSOURCEARB) \
|
||||
GLE(CompileShader, "CompileShaderARB", COMPILESHADERARB) \
|
||||
GLE(GetShaderiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \
|
||||
GLE(GetShaderInfoLog, "GetInfoLogARB", GETINFOLOGARB) \
|
||||
GLE(CreateProgram, "CreateProgramObjectARB", CREATEPROGRAMOBJECTARB) \
|
||||
GLE(DeleteProgram, "DeleteObjectARB", DELETEOBJECTARB) \
|
||||
GLE(AttachShader, "AttachObjectARB", ATTACHOBJECTARB) \
|
||||
GLE(LinkProgram, "LinkProgramARB", LINKPROGRAMARB) \
|
||||
GLE(GetUniformLocation, "GetUniformLocationARB", GETUNIFORMLOCATIONARB) \
|
||||
GLE(UseProgram, "UseProgramObjectARB", USEPROGRAMOBJECTARB) \
|
||||
GLE(GetProgramiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \
|
||||
GLE(GetProgramInfoLog, "GetInfoLogARB", GETINFOLOGARB) \
|
||||
GLE(Uniform1i, "Uniform1iARB", UNIFORM1IARB) \
|
||||
GLE(Uniform4f, "Uniform4fARB", UNIFORM4FARB) \
|
||||
GLE(Uniform4fv, "Uniform4fvARB", UNIFORM4FVARB) \
|
||||
/* GL_ARB_vertex_shader */ \
|
||||
GLE(BindAttribLocation, "BindAttribLocationARB", BINDATTRIBLOCATIONARB) \
|
||||
/* Missing from WGL but needed by shared code */ \
|
||||
GLE(Uniform1f, "Uniform1fARB", UNIFORM1FARB) \
|
||||
/* GL_EXT_framebuffer_object */ \
|
||||
GLE(GenRenderbuffers, "GenRenderbuffersEXT", GENRENDERBUFFERSEXT) \
|
||||
GLE(DeleteRenderbuffers, "DeleteRenderbuffersEXT", DELETERENDERBUFFERSEXT) \
|
||||
GLE(BindRenderbuffer, "BindRenderbufferEXT", BINDRENDERBUFFEREXT) \
|
||||
GLE(RenderbufferStorage, "RenderbufferStorageEXT", RENDERBUFFERSTORAGEEXT) \
|
||||
GLE(GenFramebuffers, "GenFramebuffersEXT", GENFRAMEBUFFERSEXT) \
|
||||
GLE(DeleteFramebuffers, "DeleteFramebuffersEXT", DELETEFRAMEBUFFERSEXT) \
|
||||
GLE(BindFramebuffer, "BindFramebufferEXT", BINDFRAMEBUFFEREXT) \
|
||||
GLE(CheckFramebufferStatus, "CheckFramebufferStatusEXT", CHECKFRAMEBUFFERSTATUSEXT) \
|
||||
GLE(FramebufferRenderbuffer, "FramebufferRenderbufferEXT", FRAMEBUFFERRENDERBUFFEREXT) \
|
||||
GLE(FramebufferTexture2D, "FramebufferTexture2DEXT", FRAMEBUFFERTEXTURE2DEXT) \
|
||||
GLE(GenerateMipmap, "GenerateMipmapEXT", GENERATEMIPMAPEXT) \
|
||||
/* GL_EXT_framebuffer_blit */ \
|
||||
GLE(BlitFramebuffer, "BlitFramebufferEXT", BLITFRAMEBUFFEREXT) \
|
||||
/* GL_EXT_framebuffer_multisample */ \
|
||||
GLE(RenderbufferStorageMultisample, "RenderbufferStorageMultisampleEXT",RENDERBUFFERSTORAGEMULTISAMPLEEXT) \
|
||||
/* <end> */
|
||||
|
||||
#define gdraw_GLx_(id) gdraw_GL_##id
|
||||
#define GDRAW_GLx_(id) GDRAW_GL_##id
|
||||
|
|
@ -125,19 +125,19 @@ GDRAW_GL_EXTENSION_LIST
|
|||
|
||||
static void load_extensions(void)
|
||||
{
|
||||
#define GLE(id, import, procname) gl##id = (PFNGL##procname##PROC) glfwGetProcAddress("gl" import);
|
||||
GDRAW_GL_EXTENSION_LIST
|
||||
#undef GLE
|
||||
#define GLE(id, import, procname) gl##id = (PFNGL##procname##PROC) SDL_GL_GetProcAddress("gl" import);
|
||||
GDRAW_GL_EXTENSION_LIST
|
||||
#undef GLE
|
||||
}
|
||||
|
||||
static void clear_renderstate_platform_specific(void)
|
||||
{
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
}
|
||||
|
||||
static void error_msg_platform_specific(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "[GDraw GL] %s\n", msg);
|
||||
fprintf(stderr, "[GDraw GL] %s\n", msg);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -162,80 +162,80 @@ static void error_msg_platform_specific(const char *msg)
|
|||
|
||||
GDrawFunctions *gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples)
|
||||
{
|
||||
static const TextureFormatDesc tex_formats[] = {
|
||||
{ IFT_FORMAT_rgba_8888, 1, 1, 4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_rgba_4444_LE, 1, 1, 2, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 },
|
||||
{ IFT_FORMAT_rgba_5551_LE, 1, 1, 2, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 },
|
||||
{ IFT_FORMAT_la_88, 1, 1, 2, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_la_44, 1, 1, 1, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_i_8, 1, 1, 1, GL_INTENSITY8, GL_ALPHA, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_i_4, 1, 1, 1, GL_INTENSITY4, GL_ALPHA, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_l_8, 1, 1, 1, GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_l_4, 1, 1, 1, GL_LUMINANCE4, GL_LUMINANCE, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_DXT1, 4, 4, 8, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_DXT3, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_DXT5, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, GL_UNSIGNED_BYTE },
|
||||
{ 0, 0, 0, 0, 0, 0, 0 },
|
||||
};
|
||||
static const TextureFormatDesc tex_formats[] = {
|
||||
{ IFT_FORMAT_rgba_8888, 1, 1, 4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_rgba_4444_LE, 1, 1, 2, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 },
|
||||
{ IFT_FORMAT_rgba_5551_LE, 1, 1, 2, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 },
|
||||
{ IFT_FORMAT_la_88, 1, 1, 2, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_la_44, 1, 1, 1, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_i_8, 1, 1, 1, GL_INTENSITY8, GL_ALPHA, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_i_4, 1, 1, 1, GL_INTENSITY4, GL_ALPHA, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_l_8, 1, 1, 1, GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_l_4, 1, 1, 1, GL_LUMINANCE4, GL_LUMINANCE, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_DXT1, 4, 4, 8, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_DXT3, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, GL_UNSIGNED_BYTE },
|
||||
{ IFT_FORMAT_DXT5, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, GL_UNSIGNED_BYTE },
|
||||
{ 0, 0, 0, 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
GDrawFunctions *funcs;
|
||||
const char *s;
|
||||
GLint n;
|
||||
GDrawFunctions *funcs;
|
||||
const char *s;
|
||||
GLint n;
|
||||
|
||||
// check for the extensions we need
|
||||
s = (const char *) glGetString(GL_EXTENSIONS);
|
||||
if (s == NULL) {
|
||||
fprintf(stderr, "[GDraw GL] glGetString(GL_EXTENSIONS) returned NULL - GL context not current?\n");
|
||||
assert(s != NULL);
|
||||
return NULL;
|
||||
}
|
||||
// check for the extensions we need
|
||||
s = (const char *) glGetString(GL_EXTENSIONS);
|
||||
if (s == NULL) {
|
||||
fprintf(stderr, "[GDraw GL] glGetString(GL_EXTENSIONS) returned NULL - GL context not current?\n");
|
||||
assert(s != NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// check for the extensions we won't work without
|
||||
if (!hasext(s, "GL_ARB_multitexture") ||
|
||||
!hasext(s, "GL_ARB_texture_compression") ||
|
||||
!hasext(s, "GL_ARB_texture_mirrored_repeat") ||
|
||||
!hasext(s, "GL_ARB_texture_non_power_of_two") ||
|
||||
!hasext(s, "GL_ARB_vertex_buffer_object") ||
|
||||
!hasext(s, "GL_EXT_framebuffer_object") ||
|
||||
!hasext(s, "GL_ARB_shader_objects") ||
|
||||
!hasext(s, "GL_ARB_vertex_shader") ||
|
||||
!hasext(s, "GL_ARB_fragment_shader"))
|
||||
{
|
||||
fprintf(stderr, "[GDraw GL] Required GL extensions not available\n");
|
||||
return NULL;
|
||||
}
|
||||
// check for the extensions we won't work without
|
||||
if (!hasext(s, "GL_ARB_multitexture") ||
|
||||
!hasext(s, "GL_ARB_texture_compression") ||
|
||||
!hasext(s, "GL_ARB_texture_mirrored_repeat") ||
|
||||
!hasext(s, "GL_ARB_texture_non_power_of_two") ||
|
||||
!hasext(s, "GL_ARB_vertex_buffer_object") ||
|
||||
!hasext(s, "GL_EXT_framebuffer_object") ||
|
||||
!hasext(s, "GL_ARB_shader_objects") ||
|
||||
!hasext(s, "GL_ARB_vertex_shader") ||
|
||||
!hasext(s, "GL_ARB_fragment_shader"))
|
||||
{
|
||||
fprintf(stderr, "[GDraw GL] Required GL extensions not available\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// if user requests multisampling and HW doesn't support it, bail
|
||||
if (!hasext(s, "GL_EXT_framebuffer_multisample") && msaa_samples > 1)
|
||||
return NULL;
|
||||
// if user requests multisampling and HW doesn't support it, bail
|
||||
if (!hasext(s, "GL_EXT_framebuffer_multisample") && msaa_samples > 1)
|
||||
return NULL;
|
||||
|
||||
load_extensions();
|
||||
funcs = create_context(w, h);
|
||||
if (!funcs)
|
||||
return NULL;
|
||||
load_extensions();
|
||||
funcs = create_context(w, h);
|
||||
if (!funcs)
|
||||
return NULL;
|
||||
|
||||
gdraw->tex_formats = tex_formats;
|
||||
gdraw->tex_formats = tex_formats;
|
||||
|
||||
// check for optional extensions
|
||||
gdraw->has_mapbuffer = true; // part of core VBO extension on regular GL
|
||||
gdraw->has_depth24 = true; // we just assume.
|
||||
gdraw->has_texture_max_level = true; // core on regular GL
|
||||
// check for optional extensions
|
||||
gdraw->has_mapbuffer = true; // part of core VBO extension on regular GL
|
||||
gdraw->has_depth24 = true; // we just assume.
|
||||
gdraw->has_texture_max_level = true; // core on regular GL
|
||||
|
||||
if (hasext(s, "GL_EXT_packed_depth_stencil")) gdraw->has_packed_depth_stencil = true;
|
||||
if (hasext(s, "GL_EXT_packed_depth_stencil")) gdraw->has_packed_depth_stencil = true;
|
||||
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &n);
|
||||
gdraw->has_conditional_non_power_of_two = n < 8192;
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &n);
|
||||
gdraw->has_conditional_non_power_of_two = n < 8192;
|
||||
|
||||
// clamp number of multisampling levels to max supported
|
||||
if (msaa_samples > 1) {
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &n);
|
||||
gdraw->multisampling = RR_MIN(msaa_samples, n);
|
||||
}
|
||||
// clamp number of multisampling levels to max supported
|
||||
if (msaa_samples > 1) {
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &n);
|
||||
gdraw->multisampling = RR_MIN(msaa_samples, n);
|
||||
}
|
||||
|
||||
opengl_check();
|
||||
opengl_check();
|
||||
|
||||
fprintf(stderr, "[GDraw GL] Context created successfully (%dx%d, msaa=%d)\n", w, h, msaa_samples);
|
||||
return funcs;
|
||||
fprintf(stderr, "[GDraw GL] Context created successfully (%dx%d, msaa=%d)\n", w, h, msaa_samples);
|
||||
return funcs;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -245,12 +245,12 @@ GDrawFunctions *gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples)
|
|||
|
||||
void gdraw_GL_BeginCustomDraw_4J(IggyCustomDrawCallbackRegion *region, F32 *matrix)
|
||||
{
|
||||
// Same as BeginCustomDraw but uses different depth param
|
||||
clear_renderstate();
|
||||
gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, depth_from_id(0), 1);
|
||||
// Same as BeginCustomDraw but uses different depth param
|
||||
clear_renderstate();
|
||||
gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, depth_from_id(0), 1);
|
||||
}
|
||||
|
||||
void gdraw_GL_CalculateCustomDraw_4J(IggyCustomDrawCallbackRegion *region, F32 *matrix)
|
||||
{
|
||||
gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, 0.0f, 0);
|
||||
gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, 0.0f, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ client = executable('Minecraft.Client',
|
|||
world_dep,
|
||||
gl_dep,
|
||||
glu_dep,
|
||||
glfw_dep,
|
||||
thread_dep,
|
||||
thread_dep,
|
||||
dl_dep,
|
||||
dependency('zlib'),
|
||||
|
|
@ -80,10 +80,10 @@ custom_target('copy_assets_to_client',
|
|||
command : [
|
||||
python, meson.project_source_root() / 'scripts/copy_assets_to_client.py',
|
||||
meson.project_source_root(),
|
||||
meson.project_build_root(),
|
||||
meson.build_root(),
|
||||
meson.current_build_dir(),
|
||||
'@INPUT1@',
|
||||
'@OUTPUT@',
|
||||
],
|
||||
build_by_default: true,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
11
README.md
11
README.md
|
|
@ -22,9 +22,6 @@ At the moment, we're aiming to support the following platforms:
|
|||
- iOS (not started)
|
||||
- Android (not started)
|
||||
|
||||
> [!WARNING]
|
||||
> There NO Windows support, for that, go to [smartcmd/MinecraftConsoles](https://github.com/smartcmd/MinecraftConsoles/).
|
||||
|
||||
> All efforts are focused towards a native Linux port, OpenGL rendering pipeline, and modernizing the existing LCE codebase/tooling to make future platform ports easier.
|
||||
>
|
||||
> `Windows64` and other platforms originally supported by LCE are currently unsupported, since the original Visual Studio tooling has been stripped from this repository and replaced with our own.
|
||||
|
|
@ -40,7 +37,7 @@ Install the following packages before building (Debian/Ubuntu names shown):
|
|||
```bash
|
||||
sudo apt install \
|
||||
build-essential cmake \
|
||||
libglfw3-dev libgl-dev libglu1-mesa-dev \
|
||||
libsdl2-dev libgl-dev libglu1-mesa-dev \
|
||||
libopenal-dev libvorbis-dev \
|
||||
libpng-dev libpthread-stubs0-dev
|
||||
```
|
||||
|
|
@ -48,16 +45,16 @@ sudo apt install \
|
|||
#### Arch/Manjaro
|
||||
|
||||
```bash
|
||||
sudo pacman -S base-devel gcc pkgconf cmake glfw-x11 mesa openal libvorbis glu
|
||||
sudo pacman -S base-devel gcc pkgconf cmake sdl2 mesa openal libvorbis glu
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> If you are on wayland, you may swap `glfw-x11` to `glfw-wayland` for native wayland windowing instead of xwayland.
|
||||
> SDL2 supports both X11 and Wayland backends; no package swap is necessary for Wayland!!!!!!!
|
||||
|
||||
#### Fedora/Red Hat/Nobara
|
||||
|
||||
```bash
|
||||
sudo dnf in gcc gcc-c++ make cmake glfw-devel mesa-libGL-devel mesa-libGLU-devel openal-soft-devel libvorbis-devel libpng-devel openssl-devel
|
||||
sudo dnf in gcc gcc-c++ make cmake SDL2-devel mesa-libGL-devel mesa-libGLU-devel openal-soft-devel libvorbis-devel libpng-devel openssl-devel
|
||||
```
|
||||
|
||||
#### Docker
|
||||
|
|
|
|||
|
|
@ -21,18 +21,17 @@
|
|||
{
|
||||
devShells = forAllSystems ({ pkgs }:
|
||||
let
|
||||
libs = with pkgs; [
|
||||
libs = with pkgs; [
|
||||
openssl.dev
|
||||
libGL
|
||||
libGLU
|
||||
glfw
|
||||
sdl2
|
||||
libpng
|
||||
zlib
|
||||
openal
|
||||
libvorbis
|
||||
];
|
||||
packages = with pkgs; [
|
||||
python3
|
||||
gcc15
|
||||
lld
|
||||
cmake
|
||||
|
|
|
|||
14
meson.build
14
meson.build
|
|
@ -1,10 +1,11 @@
|
|||
project('4jcraft-chucklegrounds', ['cpp', 'c'],
|
||||
version : '0.1.0',
|
||||
default_options : [
|
||||
'buildtype=debug', # for now
|
||||
'unity=on', # merge source files per target
|
||||
'unity_size=8', # TODO: mess around with this
|
||||
'b_pch=true', # precompiled headers
|
||||
'warning_level=0',
|
||||
'buildtype=debug',
|
||||
'unity=on', # merge source files per target → fewer compile units
|
||||
'unity_size=8', # files per unity batch (tune up for faster full builds)
|
||||
'b_pch=true', # precompiled headers
|
||||
],
|
||||
)
|
||||
|
||||
|
|
@ -16,7 +17,8 @@ cc = meson.get_compiler('cpp')
|
|||
# system deps
|
||||
gl_dep = dependency('gl')
|
||||
glu_dep = dependency('glu')
|
||||
glfw_dep = dependency('glfw3')
|
||||
sdl2_dep = dependency('sdl2') # Yes.. i know sdl3 is out, but there's not point upgrading right now except when
|
||||
# someone is gonna ask me "Hey juicey can you make it SDL3?" and i'd be like fuck you and still do it.
|
||||
png_dep = dependency('libpng')
|
||||
thread_dep = dependency('threads')
|
||||
dl_dep = cc.find_library('dl')
|
||||
|
|
@ -52,4 +54,4 @@ subdir('4J.Profile')
|
|||
subdir('4J.Storage')
|
||||
subdir('Minecraft.Assets')
|
||||
subdir('Minecraft.World')
|
||||
subdir('Minecraft.Client')
|
||||
subdir('Minecraft.Client')
|
||||
|
|
|
|||
Loading…
Reference in a new issue