Drive the engine's mouse cursor from the VR aim ray on the game quad
build / build (push) Successful in 1m44s
build / build (push) Successful in 1m44s
The game quad's laser beam was purely visual - aiming at the game's own UI (inventory, menus, dialogs) had no way to interact with it. Add a real mouse pointer: absolute cursor position plus a single left click, mirroring a plain point-and-click mouse (no mouselook, no right-click, no drag). New xr_mouse.c/h bridges the aim-ray hit-test to the vendored engine's own public mouse API - mouse_put_xy() for position, a synthetic SDL_MOUSEBUTTONDOWN/UP (SDL_BUTTON_LEFT) via SDL_PushEvent for clicks, the same technique nativeSendPrintableChar() already uses for synthetic keyboard input. No engine patch needed. Deliberately bypasses SDLActivity.onNativeMouse()/SDL_MOUSEMOTION/SetMouseXY(), whose physical-window-size scaling is unreliable in this headless immersive build. xr_input_try_game_quad() now reports the hit u,v; a new g_game_touch_active[] tracks a held click on the game quad so it survives the ray straying onto the keyboard/menu before release, gating those overlays' claims the same way pending overlay touches already do.
This commit is contained in:
@@ -171,7 +171,7 @@ android {
|
||||
"-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH", \
|
||||
"-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH", \
|
||||
"-DCMAKE_SHARED_LINKER_FLAGS=-Wl,-z,max-page-size=16384", \
|
||||
"-DANDROID_EXTRA_SOURCES=${projectDir}/src/main/cpp/questshock_native.c;${projectDir}/src/main/cpp/xr_session.c;${projectDir}/src/main/cpp/xr_input.c;${projectDir}/src/main/cpp/xr_overlay.c;${projectDir}/src/main/cpp/xr_swapchain.c"
|
||||
"-DANDROID_EXTRA_SOURCES=${projectDir}/src/main/cpp/questshock_native.c;${projectDir}/src/main/cpp/xr_session.c;${projectDir}/src/main/cpp/xr_input.c;${projectDir}/src/main/cpp/xr_overlay.c;${projectDir}/src/main/cpp/xr_swapchain.c;${projectDir}/src/main/cpp/xr_mouse.c"
|
||||
abiFilters 'arm64-v8a'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <GLES3/gl3.h>
|
||||
|
||||
#include "xr_mouse.h"
|
||||
#include "xr_overlay.h"
|
||||
#include "xr_session.h"
|
||||
#include "xr_swapchain.h"
|
||||
@@ -53,6 +54,7 @@ static XrSpace g_aim_space[2] = {XR_NULL_HANDLE, XR_NULL_HANDLE};
|
||||
static bool g_prev_select[2] = {false, false};
|
||||
static bool g_prev_menu = false;
|
||||
static bool g_game_prev_hit[2] = {false, false};
|
||||
static bool g_game_touch_active[2] = {false, false};
|
||||
static bool g_menu_prev_hit[2] = {false, false};
|
||||
static bool g_menu_touch_active[2] = {false, false};
|
||||
static bool g_keyboard_prev_hit[2] = {false, false};
|
||||
@@ -524,12 +526,17 @@ static void xr_input_build_beam(int hand, const XrSpaceLocation *location, float
|
||||
|
||||
// Hit-tests this hand's ray against the game quad (xr_get_game_quad_extent())
|
||||
// - the lowest-priority target, tried only once neither the keyboard nor
|
||||
// menu overlay claimed the ray this frame. There's no touch/drag state
|
||||
// for the game quad yet (see the file comment above) - this purely drives
|
||||
// the laser-beam visual, symmetric with xr_input_try_overlay()'s `hit`
|
||||
// term but without a touch/drag component. Returns plain `hit`.
|
||||
// menu overlay claimed the ray this frame (or, symmetrically, once this
|
||||
// hand already has a pending mouse-down on the game quad - see
|
||||
// g_game_touch_active and the keyboard/menu call sites in
|
||||
// xr_input_sync_and_draw()). outU/outV (only meaningful when the
|
||||
// returned hit is true) let the caller drive the game's own mouse cursor
|
||||
// via xr_mouse.h. Returns plain `hit` - drag/touch dispatch for actual
|
||||
// clicks is the caller's responsibility (xr_mouse_click()), not this
|
||||
// function's, unlike xr_input_try_overlay().
|
||||
static bool xr_input_try_game_quad(int hand, const XrSpaceLocation *location, float fx, float fy,
|
||||
float fz, bool *outPlaneHit, float *outPlaneDistance) {
|
||||
float fz, bool *outPlaneHit, float *outPlaneDistance,
|
||||
float *outU, float *outV) {
|
||||
float distance, halfWidth, halfHeight;
|
||||
xr_get_game_quad_extent(&distance, &halfWidth, &halfHeight);
|
||||
|
||||
@@ -548,6 +555,8 @@ static bool xr_input_try_game_quad(int hand, const XrSpaceLocation *location, fl
|
||||
}
|
||||
*outPlaneHit = planeHit;
|
||||
*outPlaneDistance = t;
|
||||
*outU = u;
|
||||
*outV = v;
|
||||
|
||||
if (hit != g_game_prev_hit[hand]) {
|
||||
LOGI("XR: %s aim ray %s game quad (u=%.2f v=%.2f)", kHandName[hand],
|
||||
@@ -671,10 +680,18 @@ void xr_input_sync_and_draw(XrSpace baseSpace, XrTime time, bool draw) {
|
||||
// xr_input_try_overlay()'s doc comment) - the game quad itself.
|
||||
// Whichever target claims the ray gets a laser-beam quad built for
|
||||
// it (if a head pose is available and the ray actually crosses
|
||||
// that target's plane) - see xr_input_build_beam().
|
||||
// that target's plane) - see xr_input_build_beam(). Keyboard/menu
|
||||
// are additionally gated on !g_game_touch_active[hand]: a pending
|
||||
// mouse-down on the game quad (trigger still held since a
|
||||
// down-edge dispatched there) must keep claiming the ray even if
|
||||
// it strays onto another panel before release, the cross-target
|
||||
// analogue of xr_input_try_overlay()'s own hadTouch/hadDrag - the
|
||||
// eventual mouse-up has to reach the game quad, not whatever the
|
||||
// ray happens to be over on the release frame.
|
||||
bool keyboardPlaneHit = false;
|
||||
float keyboardPlaneDistance = 0.0f;
|
||||
if (xr_input_try_overlay(keyboardOverlay, true, hand, &location, fx, fy, fz,
|
||||
if (!g_game_touch_active[hand] &&
|
||||
xr_input_try_overlay(keyboardOverlay, true, hand, &location, fx, fy, fz,
|
||||
selectDownEdge, selectUpEdge, &g_keyboard_touch_active[hand],
|
||||
&g_keyboard_prev_hit[hand], "keyboard", &keyboardCursorU,
|
||||
&keyboardCursorV, &keyboardCursorHit, &keyboardPlaneHit,
|
||||
@@ -686,7 +703,8 @@ void xr_input_sync_and_draw(XrSpace baseSpace, XrTime time, bool draw) {
|
||||
}
|
||||
bool menuPlaneHit = false;
|
||||
float menuPlaneDistance = 0.0f;
|
||||
if (xr_input_try_overlay(menuOverlay, false, hand, &location, fx, fy, fz, selectDownEdge,
|
||||
if (!g_game_touch_active[hand] &&
|
||||
xr_input_try_overlay(menuOverlay, false, hand, &location, fx, fy, fz, selectDownEdge,
|
||||
selectUpEdge, &g_menu_touch_active[hand], &g_menu_prev_hit[hand],
|
||||
"menu", &menuCursorU, &menuCursorV, &menuCursorHit, &menuPlaneHit,
|
||||
&menuPlaneDistance)) {
|
||||
@@ -696,13 +714,36 @@ void xr_input_sync_and_draw(XrSpace baseSpace, XrTime time, bool draw) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Lowest priority: the game quad itself - purely visual (beam
|
||||
// only, same as the other two targets), no click/touch dispatch
|
||||
// here yet (see the file comment above).
|
||||
// Lowest priority (for a fresh hit): the game quad itself - drives
|
||||
// the engine's own mouse cursor/clicks via xr_mouse.h, exactly
|
||||
// like a desktop mouse (absolute position + left button), plus the
|
||||
// same laser-beam visual the other two targets get.
|
||||
bool gamePlaneHit = false;
|
||||
float gamePlaneDistance = 0.0f;
|
||||
bool gameHit =
|
||||
xr_input_try_game_quad(hand, &location, fx, fy, fz, &gamePlaneHit, &gamePlaneDistance);
|
||||
float gamePlaneDistance = 0.0f, gameU = 0.0f, gameV = 0.0f;
|
||||
bool gameHit = xr_input_try_game_quad(hand, &location, fx, fy, fz, &gamePlaneHit,
|
||||
&gamePlaneDistance, &gameU, &gameV);
|
||||
if (gameHit) {
|
||||
int gameWidth, gameHeight;
|
||||
xr_get_game_resolution(&gameWidth, &gameHeight);
|
||||
int px = (int)(gameU * (float)gameWidth);
|
||||
int py = (int)(gameV * (float)gameHeight);
|
||||
if (px < 0)
|
||||
px = 0;
|
||||
else if (px >= gameWidth)
|
||||
px = gameWidth - 1;
|
||||
if (py < 0)
|
||||
py = 0;
|
||||
else if (py >= gameHeight)
|
||||
py = gameHeight - 1;
|
||||
xr_mouse_move(px, py);
|
||||
}
|
||||
if (selectDownEdge && gameHit) {
|
||||
xr_mouse_click(true);
|
||||
g_game_touch_active[hand] = true;
|
||||
} else if (selectUpEdge && g_game_touch_active[hand]) {
|
||||
xr_mouse_click(false);
|
||||
g_game_touch_active[hand] = false;
|
||||
}
|
||||
// gameHit implies gamePlaneHit (both only ever set together above),
|
||||
// kept as a separate out-param for symmetry with
|
||||
// xr_input_try_overlay()'s outPlaneHit/outPlaneDistance pair.
|
||||
@@ -746,6 +787,7 @@ void xr_input_shutdown(void) {
|
||||
g_session = XR_NULL_HANDLE;
|
||||
memset(g_prev_select, 0, sizeof(g_prev_select));
|
||||
memset(g_game_prev_hit, 0, sizeof(g_game_prev_hit));
|
||||
memset(g_game_touch_active, 0, sizeof(g_game_touch_active));
|
||||
memset(g_menu_prev_hit, 0, sizeof(g_menu_prev_hit));
|
||||
memset(g_menu_touch_active, 0, sizeof(g_menu_touch_active));
|
||||
memset(g_keyboard_prev_hit, 0, sizeof(g_keyboard_prev_hit));
|
||||
|
||||
@@ -6,13 +6,18 @@
|
||||
// this frame (landing on it now, or continuing a touch/drag begun on a
|
||||
// previous frame - not merely because that overlay happens to be visible)
|
||||
// - the game quad xr_session.c submits, forwarding trigger edges to
|
||||
// whichever overlay claims the ray as synthetic touches. Since none of
|
||||
// the three targets' own feedback (the overlays' on-quad cursor, drawn by
|
||||
// their Java views; nothing at all for the game quad, which has no
|
||||
// click/touch dispatch here yet) gives any indication while the ray is
|
||||
// short of actually landing on something, every claimed target also
|
||||
// builds a thin, billboarded laser-beam quad per hand from the controller
|
||||
// to wherever the ray currently crosses that target's plane (see
|
||||
// whichever overlay claims the ray as synthetic touches. The game quad
|
||||
// itself acts as a plain desktop-style mouse pointer into the engine's
|
||||
// own UI (see xr_mouse.h) - absolute cursor position while the ray hits
|
||||
// it, plus a left click on the trigger edge; a pending click there
|
||||
// likewise keeps claiming the ray ahead of the keyboard/menu until
|
||||
// released, so the eventual mouse-up isn't lost if the ray strays. Since
|
||||
// none of the three targets' own feedback (the overlays' on-quad cursor,
|
||||
// drawn by their Java views; the engine's own mouse cursor sprite for the
|
||||
// game quad) gives any indication while the ray is short of actually
|
||||
// landing on something, every claimed target also builds a thin,
|
||||
// billboarded laser-beam quad per hand from the controller to wherever
|
||||
// the ray currently crosses that target's plane (see
|
||||
// xr_input_get_beam_layer()) - so aiming looks and behaves the same
|
||||
// whether the target is an overlay or the game quad itself.
|
||||
#ifndef QUESTSHOCK_XR_INPUT_H
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "xr_mouse.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "mouse.h" // engine/src/Libraries/INPUT/Source/mouse.h - mouse_put_xy()
|
||||
|
||||
void xr_mouse_move(int x, int y) { mouse_put_xy((short)x, (short)y); }
|
||||
|
||||
void xr_mouse_click(bool down) {
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = down ? SDL_MOUSEBUTTONDOWN : SDL_MOUSEBUTTONUP;
|
||||
event.button.timestamp = SDL_GetTicks();
|
||||
event.button.windowID = 0;
|
||||
event.button.which = 0;
|
||||
event.button.button = SDL_BUTTON_LEFT;
|
||||
event.button.state = down ? SDL_PRESSED : SDL_RELEASED;
|
||||
event.button.clicks = 1;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Bridges VR aim-ray hit-testing (xr_input.c) to the vendored engine's
|
||||
// mouse input (engine/src/Libraries/INPUT/Source/mouse.h) - lets the
|
||||
// controller's laser pointer drive the game's own UI (inventory, menus,
|
||||
// dialogs) exactly like a desktop mouse: absolute cursor position plus a
|
||||
// single left button, no relative/mouselook mode, no right button.
|
||||
#ifndef QUESTSHOCK_XR_MOUSE_H
|
||||
#define QUESTSHOCK_XR_MOUSE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Sets the engine's absolute mouse position (mouse_put_xy()) - call every
|
||||
// frame the aim ray is actually hitting the game quad; x/y are pixel
|
||||
// coordinates in the game's own logical resolution (see
|
||||
// xr_get_game_resolution() in xr_session.h), already clamped by the
|
||||
// caller. Do not call on frames the ray isn't hitting the game quad - the
|
||||
// cursor should hold its last position, not snap elsewhere.
|
||||
void xr_mouse_move(int x, int y);
|
||||
|
||||
// Pushes a synthetic SDL_MOUSEBUTTONDOWN/UP (SDL_BUTTON_LEFT) - matches
|
||||
// KeyboardOverlay's nativeSendPrintableChar() in questshock_native.c: a
|
||||
// plain SDL_Event built by hand and handed to the public SDL_PushEvent(),
|
||||
// no engine patch needed. pump_events() (sdl_events.c) reads whatever
|
||||
// mouse_put_xy() last set, not any x/y carried on the event itself - call
|
||||
// xr_mouse_move() first if position needs updating this frame.
|
||||
void xr_mouse_click(bool down);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -469,3 +469,8 @@ void xr_get_game_quad_extent(float *distance_m, float *half_width_m, float *half
|
||||
*half_height_m =
|
||||
QUAD_WIDTH_METERS * 0.5f * (float)g_game_swapchain.height / (float)g_game_swapchain.width;
|
||||
}
|
||||
|
||||
void xr_get_game_resolution(int *width, int *height) {
|
||||
*width = g_game_swapchain.width;
|
||||
*height = g_game_swapchain.height;
|
||||
}
|
||||
|
||||
@@ -74,6 +74,13 @@ void xr_shutdown(void);
|
||||
// laser pointer always matches whatever's actually visible.
|
||||
void xr_get_game_quad_extent(float *distance_m, float *half_width_m, float *half_height_m);
|
||||
|
||||
// Pixel resolution of the game quad's own swapchain - i.e. game_width/
|
||||
// game_height as passed to xr_init() (grd_cap->w/h, see Shock.c's
|
||||
// InitSDL()) - the logical coordinate space xr_input.c needs to convert a
|
||||
// game-quad ray hit's normalized u,v into engine mouse coordinates (see
|
||||
// xr_mouse.h).
|
||||
void xr_get_game_resolution(int *width, int *height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user