mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 15:00:27 +02:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59b1fb422b | ||
|
|
5ecd3075a0 | ||
|
|
8e8713681d | ||
|
|
d7099f3e40 | ||
|
|
4ba36d7305 | ||
|
|
855f083117 | ||
|
|
6f3786de91 | ||
|
|
493713b0a6 | ||
|
|
bf356fc58a | ||
|
|
6a7b919522 | ||
|
|
c1305fa94f |
No files matched your search
@@ -36,10 +36,40 @@ What differs is found at run time, not by a build of its own.
|
||||
| 2 | The game's data where Lepton keeps it | done: `/home/steamos/Documents/HaloCE` |
|
||||
| 3 | Menus, HUD and text visible (quad layers) | done: their alpha from their colour |
|
||||
| 4 | The freeze at the cryo bay's calibration | not seen again (2026-10-04) |
|
||||
| 5 | The Frame's controllers | |
|
||||
| 6 | Frame rate, resolution | |
|
||||
| 5 | The Frame's controllers | done: their own profile, the Xbox's layout |
|
||||
| 6 | Frame rate, resolution | done: 72 Hz steady, multiview on |
|
||||
| 7 | Multiplayer, voice, cross-play with the Quest | |
|
||||
| 8 | Installing it, for others | |
|
||||
| 8 | A button in the Steam library | done: `steam_frame_launch.sh` |
|
||||
|
||||
## Starting it on the Frame
|
||||
|
||||
A button in the Steam library ("Halo VR", a non-Steam game) runs
|
||||
`~/Documents/HaloCE/halo-vr.sh` (`port/android/steam_frame_launch.sh`),
|
||||
which starts the game as Steam starts an Android title of its own:
|
||||
`lepton waitforexitandrun -- ~/Documents/HaloCE/HaloVR.apk`, in a container
|
||||
of its own (`steamlaunch-4290000001`) that Lepton installs the APK into
|
||||
anew whenever the file is newer. Updating the game is replacing that APK.
|
||||
|
||||
What the script does that Steam would for an Android title:
|
||||
- a compat data path (`steamapps/compatdata/4290000001/internal`): after an
|
||||
install Lepton links the app's data to it, a dangling link without it;
|
||||
and the shader path, which Lepton reads Steam's shader dump path against;
|
||||
- a container id of its own: with the shortcut's id, SteamVR's client in the
|
||||
container stalled in xrCreateInstance;
|
||||
- a few starts: a new APK's first start only installs it, and a start that
|
||||
ends within 30 s is tried again (up to three times), unless the game was
|
||||
quit (it writes `quit_requested.txt`).
|
||||
|
||||
Steam's "Exit game" does not reach the game (its process is the container's,
|
||||
not Steam's): quit it with the VR options' "Quit Halo" (Y in the main or
|
||||
pause menu, twice). A container ended within 30 s of its start makes
|
||||
Lepton drop the app's install, so the next start installs it again.
|
||||
|
||||
The shortcut is in `userdata/<id>/config/shortcuts.vdf` (Steam reads it when
|
||||
it starts; appid 3151454348 for this name and path), its pictures in
|
||||
`userdata/<id>/config/grid/` (from `port/android/steam_frame_grid/`). From
|
||||
the Mac, over USB the Frame's SteamOS is an adb device of its own, `frame`,
|
||||
beside Lepton's `<frame-ip>:5555`.
|
||||
|
||||
## The game's data
|
||||
|
||||
@@ -57,6 +87,19 @@ After a Lepton restart only the APK is installed again. (Use the path under
|
||||
/home, not /sdcard/Documents: Android 11's scoped storage stands between the
|
||||
app and the latter.)
|
||||
|
||||
## The controllers
|
||||
|
||||
SteamVR offers the Frame's controllers as themselves
|
||||
(`XR_VALVE_frame_controller_interaction`,
|
||||
`/interaction_profiles/valve/frame_controller_valve`; Valve's "Steam Frame
|
||||
Input" page). Without bindings for it, SteamVR maps the Quest's onto them,
|
||||
and the left Touch controller's X and Y land on the D-pad. With them the
|
||||
game gets the Xbox's layout, as the right controller has it: A jump, B
|
||||
flashlight (held: switch grenades), X action and reload, Y switch weapon,
|
||||
menu pause; the left one's View is Back (scores), its D-pad the D-pad, the
|
||||
bumpers switch grenades (left) and the flashlight (right). The VR options'
|
||||
page of buttons shows this layout on the Frame.
|
||||
|
||||
## Findings
|
||||
|
||||
- 2026-10-02: runs flat and in VR (GLES 3.2 by Mesa zink on Turnip, eyes
|
||||
@@ -72,3 +115,11 @@ app and the latter.)
|
||||
home connection for a while (too many connections from tests that day);
|
||||
broker.emqx.io came back within minutes. Lepton's network is the Frame's
|
||||
own (same IP), DNS works.
|
||||
- 2026-10-04: the refresh rate is SteamOS's setting; the runtime offers
|
||||
only that one (the game asks for the highest at or below
|
||||
`vr.refresh_rate`, and logs the rates offered). At 72 Hz the game holds
|
||||
72 fps (about 11.6 ms a frame with multiview, eyes 7.6 ms; without it
|
||||
eyes about 9 ms). At 90 Hz it cannot (11.1 ms a frame), and SteamVR's
|
||||
making up of the missed frames showed as a doubled picture when turning
|
||||
the head. Keep the Frame at 72 Hz, or lower `vr.resolution_scale` (about
|
||||
0.85) for 90.
|
||||
+130
-1
@@ -102,6 +102,14 @@ static struct
|
||||
XrAction button_b; /* B on the right controller, Y on the left */
|
||||
XrAction menu;
|
||||
XrAction thumbrest;
|
||||
/* the Steam Frame's controllers' own (XR_VALVE_frame_controller_interaction) */
|
||||
int has_frame_controller;
|
||||
int frame_layout;
|
||||
XrAction button_x;
|
||||
XrAction button_y;
|
||||
XrAction view;
|
||||
XrAction bumper;
|
||||
XrAction dpad[4];
|
||||
XrAction haptic;
|
||||
struct xr_hand_actions hands[HALO_XR_HAND_COUNT];
|
||||
|
||||
@@ -589,6 +597,14 @@ static int actions_initialize(void)
|
||||
xr.button_b = action_create("button_upper", "B / Y", XR_ACTION_TYPE_BOOLEAN_INPUT);
|
||||
xr.menu = action_create("menu", "Menu", XR_ACTION_TYPE_BOOLEAN_INPUT);
|
||||
xr.thumbrest = action_create("thumbrest", "Thumb rest", XR_ACTION_TYPE_BOOLEAN_INPUT);
|
||||
xr.button_x = action_create("button_x", "X", XR_ACTION_TYPE_BOOLEAN_INPUT);
|
||||
xr.button_y = action_create("button_y", "Y", XR_ACTION_TYPE_BOOLEAN_INPUT);
|
||||
xr.view = action_create("view", "View", XR_ACTION_TYPE_BOOLEAN_INPUT);
|
||||
xr.bumper = action_create("bumper", "Bumper", XR_ACTION_TYPE_BOOLEAN_INPUT);
|
||||
xr.dpad[0] = action_create("dpad_up", "D-pad up", XR_ACTION_TYPE_BOOLEAN_INPUT);
|
||||
xr.dpad[1] = action_create("dpad_down", "D-pad down", XR_ACTION_TYPE_BOOLEAN_INPUT);
|
||||
xr.dpad[2] = action_create("dpad_left", "D-pad left", XR_ACTION_TYPE_BOOLEAN_INPUT);
|
||||
xr.dpad[3] = action_create("dpad_right", "D-pad right", XR_ACTION_TYPE_BOOLEAN_INPUT);
|
||||
xr.haptic = action_create("haptic", "Rumble", XR_ACTION_TYPE_VIBRATION_OUTPUT);
|
||||
|
||||
{
|
||||
@@ -653,6 +669,44 @@ static int actions_initialize(void)
|
||||
{ &xr.haptic, "/user/hand/right/output/haptic" },
|
||||
};
|
||||
|
||||
/* the Steam Frame's controllers, as themselves: the right one has
|
||||
A, B, X, Y and a menu button, the left one a D-pad and a View button
|
||||
(port/android/STEAM_FRAME.md) */
|
||||
static const struct binding frame_controller[] =
|
||||
{
|
||||
{ &xr.grip_pose, "/user/hand/left/input/grip/pose" },
|
||||
{ &xr.grip_pose, "/user/hand/right/input/grip/pose" },
|
||||
{ &xr.aim_pose, "/user/hand/left/input/aim/pose" },
|
||||
{ &xr.aim_pose, "/user/hand/right/input/aim/pose" },
|
||||
{ &xr.trigger, "/user/hand/left/input/trigger/value" },
|
||||
{ &xr.trigger, "/user/hand/right/input/trigger/value" },
|
||||
{ &xr.squeeze, "/user/hand/left/input/squeeze/value" },
|
||||
{ &xr.squeeze, "/user/hand/right/input/squeeze/value" },
|
||||
{ &xr.thumbstick, "/user/hand/left/input/thumbstick" },
|
||||
{ &xr.thumbstick, "/user/hand/right/input/thumbstick" },
|
||||
{ &xr.thumbstick_click, "/user/hand/left/input/thumbstick/click" },
|
||||
{ &xr.thumbstick_click, "/user/hand/right/input/thumbstick/click" },
|
||||
{ &xr.button_a, "/user/hand/right/input/a/click" },
|
||||
{ &xr.button_b, "/user/hand/right/input/b/click" },
|
||||
{ &xr.button_x, "/user/hand/right/input/x/click" },
|
||||
{ &xr.button_y, "/user/hand/right/input/y/click" },
|
||||
{ &xr.menu, "/user/hand/right/input/menu/click" },
|
||||
{ &xr.view, "/user/hand/left/input/view/click" },
|
||||
{ &xr.bumper, "/user/hand/left/input/bumper/click" },
|
||||
{ &xr.bumper, "/user/hand/right/input/bumper/click" },
|
||||
{ &xr.dpad[0], "/user/hand/left/input/dpad_up/click" },
|
||||
{ &xr.dpad[1], "/user/hand/left/input/dpad_down/click" },
|
||||
{ &xr.dpad[2], "/user/hand/left/input/dpad_left/click" },
|
||||
{ &xr.dpad[3], "/user/hand/left/input/dpad_right/click" },
|
||||
{ &xr.haptic, "/user/hand/left/output/haptic" },
|
||||
{ &xr.haptic, "/user/hand/right/output/haptic" },
|
||||
};
|
||||
|
||||
if (xr.has_frame_controller)
|
||||
{
|
||||
suggest("/interaction_profiles/valve/frame_controller_valve", frame_controller,
|
||||
sizeof(frame_controller) / sizeof(frame_controller[0]));
|
||||
}
|
||||
suggest("/interaction_profiles/oculus/touch_controller", touch, sizeof(touch) / sizeof(touch[0]));
|
||||
suggest("/interaction_profiles/valve/index_controller", index, sizeof(index) / sizeof(index[0]));
|
||||
suggest("/interaction_profiles/khr/simple_controller", simple, sizeof(simple) / sizeof(simple[0]));
|
||||
@@ -722,6 +776,30 @@ static void hands_read(struct halo_xr_frame *frame)
|
||||
sync.activeActionSets = &active;
|
||||
if (!XR_SUCCEEDED(xrSyncActions(xr.session, &sync)))
|
||||
return;
|
||||
{
|
||||
/* (the controllers' profile the runtime chose, in the log as it
|
||||
changes) */
|
||||
static XrPath logged[HALO_XR_HAND_COUNT];
|
||||
|
||||
for (hand = 0; hand < HALO_XR_HAND_COUNT; hand++)
|
||||
{
|
||||
XrInteractionProfileState state = { XR_TYPE_INTERACTION_PROFILE_STATE };
|
||||
|
||||
if (XR_SUCCEEDED(xrGetCurrentInteractionProfile(xr.session, xr.hands[hand].path, &state)) &&
|
||||
state.interactionProfile != logged[hand])
|
||||
{
|
||||
char name[XR_MAX_PATH_LENGTH] = "none";
|
||||
uint32_t length = 0;
|
||||
|
||||
logged[hand] = state.interactionProfile;
|
||||
if (state.interactionProfile != XR_NULL_PATH)
|
||||
xrPathToString(xr.instance, state.interactionProfile, sizeof(name), &length, name);
|
||||
if (hand == HALO_XR_HAND_RIGHT)
|
||||
xr.frame_layout = strstr(name, "frame_controller") != NULL;
|
||||
host_logf(HOST_LOG_INFO, "xr: the %s hand's controller: %s", hand ? "right" : "left", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (hand = 0; hand < HALO_XR_HAND_COUNT; hand++)
|
||||
{
|
||||
struct halo_xr_hand *out = &frame->hands[hand];
|
||||
@@ -766,7 +844,31 @@ static void hands_read(struct halo_xr_frame *frame)
|
||||
(action_boolean(xr.menu, hand) ? HALO_XR_BUTTON_MENU : 0) |
|
||||
(action_boolean(xr.thumbrest, hand) ? HALO_XR_BUTTON_THUMBREST : 0) |
|
||||
(out->trigger > 0.5f ? HALO_XR_BUTTON_TRIGGER : 0) |
|
||||
(out->squeeze > 0.5f ? HALO_XR_BUTTON_SQUEEZE : 0);
|
||||
(out->squeeze > 0.5f ? HALO_XR_BUTTON_SQUEEZE : 0) |
|
||||
(action_boolean(xr.view, hand) ? HALO_XR_BUTTON_VIEW : 0) |
|
||||
(action_boolean(xr.bumper, hand) ? HALO_XR_BUTTON_BUMPER : 0) |
|
||||
(action_boolean(xr.dpad[0], hand) ? HALO_XR_BUTTON_DPAD_UP : 0) |
|
||||
(action_boolean(xr.dpad[1], hand) ? HALO_XR_BUTTON_DPAD_DOWN : 0) |
|
||||
(action_boolean(xr.dpad[2], hand) ? HALO_XR_BUTTON_DPAD_LEFT : 0) |
|
||||
(action_boolean(xr.dpad[3], hand) ? HALO_XR_BUTTON_DPAD_RIGHT : 0);
|
||||
}
|
||||
frame->controller_layout = xr.frame_layout ? HALO_XR_LAYOUT_FRAME : HALO_XR_LAYOUT_TOUCH;
|
||||
if (xr.frame_layout)
|
||||
{
|
||||
/* (the Frame's right X, Y and menu button: the game's X, Y and Start,
|
||||
which the Quest has on the left) */
|
||||
struct halo_xr_hand *left = &frame->hands[HALO_XR_HAND_LEFT];
|
||||
struct halo_xr_hand *right = &frame->hands[HALO_XR_HAND_RIGHT];
|
||||
|
||||
if (action_boolean(xr.button_x, HALO_XR_HAND_RIGHT))
|
||||
left->buttons |= HALO_XR_BUTTON_A;
|
||||
if (action_boolean(xr.button_y, HALO_XR_HAND_RIGHT))
|
||||
left->buttons |= HALO_XR_BUTTON_B;
|
||||
if (right->buttons & HALO_XR_BUTTON_MENU)
|
||||
{
|
||||
right->buttons &= ~HALO_XR_BUTTON_MENU;
|
||||
left->buttons |= HALO_XR_BUTTON_MENU;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,6 +885,13 @@ static void refresh_rate_choose(float wanted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
{
|
||||
char line[128] = "";
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
snprintf(line + strlen(line), sizeof(line) - strlen(line), " %.0f", rates[index]);
|
||||
host_logf(HOST_LOG_INFO, "xr: refresh rates offered:%s Hz", line);
|
||||
}
|
||||
/* the highest rate not above the one wanted (0: the highest) */
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
@@ -937,6 +1046,23 @@ int host_xr_probe(float resolution_scale, int *eye_width, int *eye_height)
|
||||
extensions[index].next = NULL;
|
||||
}
|
||||
xrEnumerateInstanceExtensionProperties(NULL, extension_count, &extension_count, extensions);
|
||||
{
|
||||
/* (the runtime's extensions, in the log: what a headset offers) */
|
||||
char line[512] = "";
|
||||
|
||||
for (index = 0; index < extension_count; index++)
|
||||
{
|
||||
if (strlen(line) + strlen(extensions[index].extensionName) + 2 >= sizeof(line))
|
||||
{
|
||||
host_logf(HOST_LOG_INFO, "xr: extensions %s", line);
|
||||
line[0] = 0;
|
||||
}
|
||||
strcat(line, " ");
|
||||
strcat(line, extensions[index].extensionName);
|
||||
}
|
||||
if (line[0])
|
||||
host_logf(HOST_LOG_INFO, "xr: extensions %s", line);
|
||||
}
|
||||
if (!has_extension(extensions, extension_count, XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME))
|
||||
{
|
||||
host_logf(HOST_LOG_ERROR, "xr: the runtime has no OpenGL ES support");
|
||||
@@ -948,6 +1074,9 @@ int host_xr_probe(float resolution_scale, int *eye_width, int *eye_height)
|
||||
xr.has_performance_settings = has_extension(extensions, extension_count, XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME);
|
||||
if (xr.has_performance_settings)
|
||||
enabled[enabled_count++] = XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME;
|
||||
xr.has_frame_controller = has_extension(extensions, extension_count, "XR_VALVE_frame_controller_interaction");
|
||||
if (xr.has_frame_controller)
|
||||
enabled[enabled_count++] = "XR_VALVE_frame_controller_interaction";
|
||||
xr.has_refresh_rate = has_extension(extensions, extension_count, XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME);
|
||||
if (xr.has_refresh_rate)
|
||||
enabled[enabled_count++] = XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME;
|
||||
|
||||
@@ -53,6 +53,23 @@ enum
|
||||
HALO_XR_BUTTON_TRIGGER = 1 << 4, /* trigger past its click point */
|
||||
HALO_XR_BUTTON_SQUEEZE = 1 << 5, /* grip past its click point */
|
||||
HALO_XR_BUTTON_THUMBREST = 1 << 6, /* thumb resting (touch) */
|
||||
/* the Steam Frame's controllers' (HALO_XR_LAYOUT_FRAME): the left one's
|
||||
View button and D-pad, either one's bumper (their right one's X, Y and
|
||||
menu button come as the left hand's A, B and MENU: the game's X, Y and
|
||||
Start, as on the Quest) */
|
||||
HALO_XR_BUTTON_VIEW = 1 << 7,
|
||||
HALO_XR_BUTTON_BUMPER = 1 << 8,
|
||||
HALO_XR_BUTTON_DPAD_UP = 1 << 9,
|
||||
HALO_XR_BUTTON_DPAD_DOWN = 1 << 10,
|
||||
HALO_XR_BUTTON_DPAD_LEFT = 1 << 11,
|
||||
HALO_XR_BUTTON_DPAD_RIGHT = 1 << 12,
|
||||
};
|
||||
|
||||
/* the controllers' layout, for what the game tells the player */
|
||||
enum
|
||||
{
|
||||
HALO_XR_LAYOUT_TOUCH = 0, /* the Quest's: X, Y and menu on the left */
|
||||
HALO_XR_LAYOUT_FRAME = 1, /* the Steam Frame's: A, B, X, Y and menu on the right, a D-pad on the left */
|
||||
};
|
||||
|
||||
/* halo_xr_hand.flags, halo_xr_view.flags, halo_xr_frame.head_flags */
|
||||
@@ -112,6 +129,7 @@ struct halo_xr_frame
|
||||
struct halo_xr_pose head;
|
||||
struct halo_xr_view views[HALO_XR_EYE_COUNT];
|
||||
struct halo_xr_hand hands[HALO_XR_HAND_COUNT];
|
||||
int controller_layout; /* HALO_XR_LAYOUT_* */
|
||||
};
|
||||
|
||||
/* halo_xr_submit.overlay_mode */
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Executable
+74
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
# Halo VR on the Steam Frame: Lepton runs the APK in a container of its own,
|
||||
# and installs it anew whenever the APK is newer than the installed one
|
||||
# (port/android/STEAM_FRAME.md). The game's data: ~/Documents/HaloCE.
|
||||
# (a container of its own, not the shortcut's id: with that, SteamVR's
|
||||
# client in the container waited on xrCreateInstance)
|
||||
export SteamAppId="${HALO_LEPTON_APP_ID:-4290000001}"
|
||||
# (what Steam gives an Android title of its own, and a non-Steam game not:
|
||||
# the app's data in the container links to the compat data path's
|
||||
# internal/, a dangling link without it; and the shader paths: Lepton reads
|
||||
# the dump path against the other)
|
||||
export STEAM_COMPAT_DATA_PATH="$HOME/.local/share/Steam/steamapps/compatdata/$SteamAppId"
|
||||
export STEAM_COMPAT_SHADER_PATH="$HOME/.local/share/Steam/steamapps/shadercache/$SteamAppId"
|
||||
mkdir -p "$STEAM_COMPAT_DATA_PATH/internal" "$STEAM_COMPAT_SHADER_PATH"
|
||||
unset STEAM_FOSSILIZE_DUMP_PATH LD_PRELOAD
|
||||
LEPTON="$HOME/.local/share/Steam/steamapps/common/Lepton/lepton"
|
||||
APK="$HOME/Documents/HaloCE/HaloVR.apk"
|
||||
LOG="$HOME/Documents/HaloCE/launcher.log"
|
||||
# (written by the game when the player quits it: no start again)
|
||||
QUIT="$HOME/Documents/HaloCE/quit_requested.txt"
|
||||
rm -f "$QUIT"
|
||||
CONTEXT="steamlaunch-$SteamAppId"
|
||||
|
||||
log() { echo "$(date '+%F %T') $*" >> "$LOG"; }
|
||||
log "start (pid $$, parent $PPID)"
|
||||
|
||||
# Steam's "Exit game" ends the game's processes as Steam knows them (this
|
||||
# script, under Steam's reaper), not the container, which podman runs: a
|
||||
# signal here is passed on to Lepton, and a watcher of its own takes the
|
||||
# container down once this script is gone (killed)
|
||||
stopping=0
|
||||
child=0
|
||||
stop()
|
||||
{
|
||||
log "signal $1"
|
||||
stopping=1
|
||||
[ "$child" -gt 0 ] && kill -TERM "$child" 2>/dev/null
|
||||
}
|
||||
trap 'stop TERM' TERM
|
||||
trap 'stop INT' INT
|
||||
trap 'stop HUP' HUP
|
||||
|
||||
script=$$
|
||||
setsid bash -c '
|
||||
while kill -0 '"$script"' 2>/dev/null; do sleep 1; done
|
||||
echo "$(date "+%F %T") watcher: the game was ended, so its container" >> "'"$LOG"'"
|
||||
"'"$LEPTON"'" kill "'"$CONTEXT"'" >/dev/null 2>&1
|
||||
' </dev/null >/dev/null 2>&1 &
|
||||
|
||||
run()
|
||||
{
|
||||
"$LEPTON" waitforexitandrun -- "$APK" &
|
||||
child=$!
|
||||
# (wait returns early when a signal comes: until Lepton is gone)
|
||||
while kill -0 "$child" 2>/dev/null; do
|
||||
wait "$child"
|
||||
done
|
||||
child=0
|
||||
}
|
||||
|
||||
# (a new APK is only installed on its first start, not started; and a start
|
||||
# right after a game that was ended hard can fail in SteamVR, which then
|
||||
# needs a moment: again, up to three times, while it ends within 30 s)
|
||||
for attempt in 1 2 3; do
|
||||
started=$SECONDS
|
||||
run
|
||||
if (( stopping || SECONDS - started >= 30 )) || [ -e "$QUIT" ]; then
|
||||
break
|
||||
fi
|
||||
log "ended within $((SECONDS - started)) s: again"
|
||||
sleep 5
|
||||
done
|
||||
rm -f "$QUIT"
|
||||
log "end"
|
||||
@@ -201,6 +201,9 @@ driver's hands on it as drawn (world units, from the game's camera), or
|
||||
NULL; the player's own hands turn it. And whether they hold it. */
|
||||
void halo_vr_set_wheel(short local_player_index, const float left[3], const float right[3]);
|
||||
int halo_vr_wheel_held(void);
|
||||
/* the controllers' layout (HALO_XR_LAYOUT_*: 0 the Quest's, 1 the Steam
|
||||
Frame's), for what the game tells the player */
|
||||
int halo_vr_controller_layout(void);
|
||||
/* whether a seated player's view stays in first person (director.c) */
|
||||
int halo_vr_vehicle_first_person(void);
|
||||
/* VR test aid (debug.vehicle_spawn): both sticks held in for a moment asks
|
||||
|
||||
@@ -2863,7 +2863,15 @@ static struct program_entry *prepare_draw(BOOL immediate)
|
||||
key.count_samples = device.visibility_test_active && xgpu_capabilities.atomic_counters;
|
||||
#endif
|
||||
|
||||
entry = program_get(vertex_shader_get(program, immediate, targets_multiview), fragment_shader_get(&key));
|
||||
entry = program_get(vertex_shader_get(program, immediate,
|
||||
#ifdef HALO_ANDROID
|
||||
/* the bound targets are the layers (Android's GL_OVR_multiview2);
|
||||
on the desktop there are no layers to stand in for the back buffer */
|
||||
targets_multiview
|
||||
#else
|
||||
FALSE
|
||||
#endif
|
||||
), fragment_shader_get(&key));
|
||||
if (!entry)
|
||||
{
|
||||
stats.skipped_link++;
|
||||
@@ -4023,7 +4031,9 @@ void d3d8_clear_back_buffer(float red, float green, float blue, float alpha)
|
||||
glDepthMask(GL_TRUE);
|
||||
glStencilMask(0xff);
|
||||
glClearColor(red, green, blue, alpha);
|
||||
glClearDepthf(1.0f);
|
||||
/* (glClearDepth: on Android the define above maps it to glClearDepthf,
|
||||
ES's float entry point) */
|
||||
glClearDepth(1.0f);
|
||||
glClearStencil(0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | (depth_buffer ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : 0));
|
||||
xgpu_gl_state_invalidate();
|
||||
|
||||
@@ -925,8 +925,10 @@ static void platform_show_pending_message(void)
|
||||
/* ---------- events */
|
||||
|
||||
/* quits as closing the window does, when the events are next read (the
|
||||
menus' Quit: port/linux/game/menu_functions.c); Android's menus have none,
|
||||
as the system closes its apps */
|
||||
menus' Quit: port/linux/game/menu_functions.c); on Android, where the
|
||||
system closes its apps, the VR options' Quit (ui_widget.c) ends the process
|
||||
at once: on the Steam Frame, Steam cannot end the game, which runs in
|
||||
Lepton's container (port/android/STEAM_FRAME.md) */
|
||||
void platform_request_quit(void)
|
||||
{
|
||||
#ifndef HALO_ANDROID
|
||||
@@ -935,6 +937,20 @@ void platform_request_quit(void)
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
#else
|
||||
{
|
||||
/* (and says so beside the game's data: the Steam Frame's launcher
|
||||
does not then take it for a start that failed and start it again) */
|
||||
char path[512];
|
||||
FILE *file;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/quit_requested.txt", platform_data_root());
|
||||
file = fopen(path, "wb");
|
||||
if (file)
|
||||
fclose(file);
|
||||
}
|
||||
platform_log("quit: the player asked");
|
||||
exit(EXIT_SUCCESS);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -502,6 +502,11 @@ int halo_vr_active(void)
|
||||
return vr.active;
|
||||
}
|
||||
|
||||
int halo_vr_controller_layout(void)
|
||||
{
|
||||
return vr.active ? vr.frame.controller_layout : HALO_XR_LAYOUT_TOUCH;
|
||||
}
|
||||
|
||||
int halo_vr_eye_size(int *width, int *height)
|
||||
{
|
||||
const char *eye_width = getenv("HALO_VR_EYE_WIDTH");
|
||||
@@ -2479,6 +2484,7 @@ int halo_vr_controllers(struct halo_xr_hand hands[HALO_XR_HAND_COUNT], int *menu
|
||||
|
||||
#else
|
||||
|
||||
int halo_vr_controller_layout(void) { return 0; }
|
||||
int halo_vr_active(void) { return 0; }
|
||||
int halo_vr_stereo_frame(void) { return 0; }
|
||||
int halo_vr_stereo_player(short local_player_index, int cinematic) { (void)local_player_index; (void)cinematic; return 0; }
|
||||
|
||||
@@ -862,6 +862,12 @@ static void vr_gamepad_state(XINPUT_GAMEPAD *pad)
|
||||
value = vr_stick(left->thumbstick[1]);
|
||||
if (abs(value) > abs(pad->sThumbLY)) pad->sThumbLY = value;
|
||||
if (left->buttons & HALO_XR_BUTTON_MENU) pad->wButtons |= XINPUT_GAMEPAD_START;
|
||||
/* (the Steam Frame's controllers: the left one's View button and D-pad) */
|
||||
if (left->buttons & HALO_XR_BUTTON_VIEW) pad->wButtons |= XINPUT_GAMEPAD_BACK;
|
||||
if (left->buttons & HALO_XR_BUTTON_DPAD_UP) pad->wButtons |= XINPUT_GAMEPAD_DPAD_UP;
|
||||
if (left->buttons & HALO_XR_BUTTON_DPAD_DOWN) pad->wButtons |= XINPUT_GAMEPAD_DPAD_DOWN;
|
||||
if (left->buttons & HALO_XR_BUTTON_DPAD_LEFT) pad->wButtons |= XINPUT_GAMEPAD_DPAD_LEFT;
|
||||
if (left->buttons & HALO_XR_BUTTON_DPAD_RIGHT) pad->wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT;
|
||||
if (left->buttons & HALO_XR_BUTTON_THUMBSTICK) pad->wButtons |= XINPUT_GAMEPAD_LEFT_THUMB;
|
||||
if (right->buttons & HALO_XR_BUTTON_THUMBSTICK) pad->wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB;
|
||||
/* (in the menus the triggers click the pointer: halo_vr_menu_pointer) */
|
||||
@@ -897,6 +903,10 @@ static void vr_gamepad_state(XINPUT_GAMEPAD *pad)
|
||||
merge_button(pad, XINPUT_GAMEPAD_B,
|
||||
((right->buttons & HALO_XR_BUTTON_SQUEEZE) != 0 && !halo_vr_wheel_held()) || halo_vr_melee());
|
||||
merge_button(pad, XINPUT_GAMEPAD_Y, (left->buttons & HALO_XR_BUTTON_B) != 0);
|
||||
/* (the Steam Frame's bumpers: the left switches grenades, the right is
|
||||
the flashlight) */
|
||||
merge_button(pad, XINPUT_GAMEPAD_BLACK, (left->buttons & HALO_XR_BUTTON_BUMPER) != 0);
|
||||
merge_button(pad, XINPUT_GAMEPAD_WHITE, (right->buttons & HALO_XR_BUTTON_BUMPER) != 0);
|
||||
{
|
||||
/* B: a tap is the flashlight, holding it switches grenades; the
|
||||
game sees a short press of either */
|
||||
|
||||
@@ -6570,10 +6570,11 @@ enum
|
||||
_vr_option_crosshair,
|
||||
_vr_option_motion_tracker,
|
||||
_vr_option_controls,
|
||||
_vr_option_quit,
|
||||
NUMBER_OF_VR_OPTIONS,
|
||||
|
||||
VR_OPTIONS_TOP = 102,
|
||||
VR_OPTIONS_ROW_HEIGHT = 28,
|
||||
VR_OPTIONS_TOP = 98,
|
||||
VR_OPTIONS_ROW_HEIGHT = 26,
|
||||
VR_OPTIONS_FOOTER = 362,
|
||||
|
||||
_vr_controls_on_foot = 0,
|
||||
@@ -6588,8 +6589,12 @@ static struct
|
||||
boolean controls;
|
||||
short selection;
|
||||
short page;
|
||||
/* "Quit Halo" chosen once: a second time quits */
|
||||
boolean quit_armed;
|
||||
} ui_vr_options_globals;
|
||||
|
||||
void platform_request_quit(void);
|
||||
|
||||
static real const vr_snap_angles[] = { 30.0f, 45.0f, 60.0f, 90.0f };
|
||||
static real const vr_smooth_speeds[] = { 60.0f, 90.0f, 120.0f, 150.0f, 180.0f };
|
||||
static real const vr_vignettes[] = { 0.0f, 0.35f, 0.7f, 1.0f };
|
||||
@@ -6661,6 +6666,13 @@ static void ui_vr_options_change(
|
||||
ui_vr_options_globals.controls = TRUE;
|
||||
ui_vr_options_globals.page = _vr_controls_on_foot;
|
||||
break;
|
||||
case _vr_option_quit:
|
||||
/* (the Steam Frame's Steam cannot end the game, which runs in
|
||||
Lepton's container: it ends itself; asked twice) */
|
||||
if (ui_vr_options_globals.quit_armed)
|
||||
platform_request_quit();
|
||||
ui_vr_options_globals.quit_armed = TRUE;
|
||||
break;
|
||||
}
|
||||
ui_play_audio_feedback_sound(_ui_audio_feedback_forward);
|
||||
}
|
||||
@@ -6668,6 +6680,7 @@ static void ui_vr_options_change(
|
||||
static void ui_vr_options_close(
|
||||
void)
|
||||
{
|
||||
ui_vr_options_globals.quit_armed = FALSE;
|
||||
ui_vr_options_globals.open = FALSE;
|
||||
ui_vr_options_globals.controls = FALSE;
|
||||
}
|
||||
@@ -6754,6 +6767,7 @@ static boolean ui_vr_options_take_event(
|
||||
else if (move)
|
||||
{
|
||||
ui_vr_options_globals.selection = (short)PIN(ui_vr_options_globals.selection + move, 0, NUMBER_OF_VR_OPTIONS - 1);
|
||||
ui_vr_options_globals.quit_armed = FALSE;
|
||||
ui_play_audio_feedback_sound(_ui_audio_feedback_cursor);
|
||||
}
|
||||
else if (accept)
|
||||
@@ -6813,7 +6827,11 @@ static boolean ui_vr_options_take_pointer(
|
||||
if (x >= 118 && x < 522 && y >= VR_OPTIONS_TOP - 4 &&
|
||||
y < VR_OPTIONS_TOP - 4 + NUMBER_OF_VR_OPTIONS * VR_OPTIONS_ROW_HEIGHT)
|
||||
{
|
||||
ui_vr_options_globals.selection = (short)((y - (VR_OPTIONS_TOP - 4)) / VR_OPTIONS_ROW_HEIGHT);
|
||||
short row = (short)((y - (VR_OPTIONS_TOP - 4)) / VR_OPTIONS_ROW_HEIGHT);
|
||||
|
||||
if (row != ui_vr_options_globals.selection)
|
||||
ui_vr_options_globals.quit_armed = FALSE;
|
||||
ui_vr_options_globals.selection = row;
|
||||
if (pointer->left_clicks)
|
||||
ui_vr_options_change(1);
|
||||
}
|
||||
@@ -6875,6 +6893,63 @@ static void ui_vr_controls_render(
|
||||
ui_join_code_draw_text(left, (short)(left + 270), 86, "LEFT CONTROLLER", FALSE, 0, &title_color);
|
||||
ui_join_code_draw_text(right, (short)(right + 270), 86, "RIGHT CONTROLLER", FALSE, 0, &title_color);
|
||||
ui_join_code_draw_quad(320, 86, 321, 300, 0xFF3A6EA5);
|
||||
if (halo_vr_controller_layout() == 1)
|
||||
{
|
||||
/* the Steam Frame's controllers: A, B, X, Y and a menu button on the
|
||||
right one, a D-pad and a View button on the left one */
|
||||
switch (ui_vr_options_globals.page)
|
||||
{
|
||||
case _vr_controls_on_foot:
|
||||
ui_vr_controls_line(left, y, "Stick", "Move; click: crouch");
|
||||
ui_vr_controls_line(left, y + 22, "Trigger", "Grenade: hold it,");
|
||||
ui_vr_controls_line(left, y + 40, "", "let go as you throw");
|
||||
ui_vr_controls_line(left, y + 62, "Grip", "Weapon in both hands");
|
||||
ui_vr_controls_line(left, y + 84, "Bumper", "Switch grenades");
|
||||
ui_vr_controls_line(left, y + 106, "View", "Scores");
|
||||
ui_vr_controls_line(left, y + 128, "D-pad", "In the menus");
|
||||
ui_vr_controls_line(right, y, "Stick", "Turn; click: zoom");
|
||||
ui_vr_controls_line(right, y + 22, "Trigger", "Fire");
|
||||
ui_vr_controls_line(right, y + 44, "Grip", "Melee (or punch)");
|
||||
ui_vr_controls_line(right, y + 66, "A", "Jump");
|
||||
ui_vr_controls_line(right, y + 88, "B", "Light; held: grenades");
|
||||
ui_vr_controls_line(right, y + 110, "X", "Action, reload");
|
||||
ui_vr_controls_line(right, y + 132, "Y", "Switch weapon");
|
||||
ui_vr_controls_line(right, y + 154, "Menu", "Pause");
|
||||
ui_vr_controls_note(320, "Motion tracker: turn the back of your left wrist towards you.");
|
||||
ui_vr_controls_note(340, "Scope: zoom, and look through the disc above the weapon.");
|
||||
ui_vr_controls_note(360, "Right bumper: the flashlight too.");
|
||||
break;
|
||||
case _vr_controls_vehicles:
|
||||
ui_vr_controls_line(left, y, "Stick", "Drive");
|
||||
ui_vr_controls_line(left, y + 22, "Trigger", "Second weapon");
|
||||
ui_vr_controls_line(left, y + 40, "", "(Banshee, tank)");
|
||||
ui_vr_controls_line(right, y, "Stick", "Steer; up and down:");
|
||||
ui_vr_controls_line(right, y + 18, "", "climb, dive");
|
||||
ui_vr_controls_line(right, y + 40, "Trigger", "Fire");
|
||||
ui_vr_controls_line(right, y + 62, "X", "Get in, get out");
|
||||
ui_vr_controls_line(right, y + 84, "B", "Flashlight");
|
||||
ui_vr_controls_note(300, "Warthog: grip the wheel with one hand or both and turn it.");
|
||||
ui_vr_controls_note(320, "Holding it, the right trigger accelerates, the left one brakes");
|
||||
ui_vr_controls_note(338, "and reverses. Let go, and the right stick steers.");
|
||||
ui_vr_controls_note(360, "At a gun or as a passenger: aim with the right controller.");
|
||||
break;
|
||||
default:
|
||||
ui_vr_controls_line(left, y, "Stick", "Choose");
|
||||
ui_vr_controls_line(left, y + 22, "D-pad", "Choose");
|
||||
ui_vr_controls_line(left, y + 44, "Trigger", "Click where it points");
|
||||
ui_vr_controls_line(right, y, "Stick", "Choose, scroll");
|
||||
ui_vr_controls_line(right, y + 22, "A", "Accept");
|
||||
ui_vr_controls_line(right, y + 44, "B", "Back");
|
||||
ui_vr_controls_line(right, y + 66, "X, Y", "As the screen shows");
|
||||
ui_vr_controls_line(right, y + 88, "Trigger", "Click where it points");
|
||||
ui_vr_controls_note(300, "Point at the menus with either controller; its trigger clicks.");
|
||||
ui_vr_controls_note(320, "On the keyboard, BACK and ENTER under it can be clicked too.");
|
||||
ui_vr_controls_note(340, "Y on the main menu or the pause menu: these VR options.");
|
||||
break;
|
||||
}
|
||||
ui_join_code_draw_text(44, 596, 412, "%b-button Back Left, right: the other pages", TRUE, 0, &tab_color);
|
||||
return;
|
||||
}
|
||||
switch (ui_vr_options_globals.page)
|
||||
{
|
||||
case _vr_controls_on_foot:
|
||||
@@ -7009,6 +7084,10 @@ static void ui_vr_options_render(
|
||||
case _vr_option_controls:
|
||||
label = "Controller buttons...";
|
||||
break;
|
||||
case _vr_option_quit:
|
||||
label = ui_vr_options_globals.quit_armed && ui_vr_options_globals.selection == _vr_option_quit ?
|
||||
"Quit Halo? Once more to quit" : "Quit Halo";
|
||||
break;
|
||||
}
|
||||
if (row == ui_vr_options_globals.selection)
|
||||
ui_join_code_draw_quad(118, (short)(y - 4), 522, (short)(y + VR_OPTIONS_ROW_HEIGHT - 6), 0x803A6EA5);
|
||||
|
||||
@@ -231,6 +231,7 @@ symbols in this file:
|
||||
#include "units/units.h"
|
||||
#ifdef HALO_ANDROID
|
||||
#include "halo_vr.h"
|
||||
#include "physics/collisions.h"
|
||||
#endif
|
||||
|
||||
/* port/linux/game/pal_tags.c's */
|
||||
@@ -2205,6 +2206,10 @@ static void trigger_create_projectiles(
|
||||
long target_object_index= NONE;
|
||||
long projectile_definition_index;
|
||||
short projectile_count;
|
||||
#ifdef HALO_ANDROID
|
||||
/* VR: fired from the muzzle of the weapon in the hand */
|
||||
boolean vr_from_muzzle= FALSE;
|
||||
#endif
|
||||
|
||||
if (!TEST_FLAG(trigger_definition->flags, _weapon_trigger_projectiles_cannot_be_aimed_bit) &&
|
||||
unit &&
|
||||
@@ -2282,6 +2287,7 @@ static void trigger_create_projectiles(
|
||||
origin.x = vr_origin[0];
|
||||
origin.y = vr_origin[1];
|
||||
origin.z = vr_origin[2];
|
||||
vr_from_muzzle = TRUE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -2383,6 +2389,26 @@ static void trigger_create_projectiles(
|
||||
real_point3d camera_position;
|
||||
|
||||
unit_get_camera_position(owner_object_index, &camera_position);
|
||||
#ifdef HALO_ANDROID
|
||||
if (vr_from_muzzle)
|
||||
{
|
||||
/* VR: from the eyes down to the hand the line runs through
|
||||
the player's own body, which the game's test hits, moving the
|
||||
shot up towards the eyes (it then flies parallel to the aim,
|
||||
above it): only the level stops it, a hand through a wall */
|
||||
struct collision_result collision;
|
||||
struct object_datum *projectile_object= object_get(projectile_object_index);
|
||||
|
||||
if (collision_test_line(FLAG(_collision_test_structure_bit) | FLAG(_collision_test_front_facing_surfaces_bit),
|
||||
&camera_position, &projectile_object->object.position, owner_object_index, &collision) &&
|
||||
collision.location.cluster_index!=NONE)
|
||||
{
|
||||
object_translate(projectile_object_index, &collision.point, &collision.location);
|
||||
object_compute_node_matrices(projectile_object_index);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
object_force_inside_bsp(projectile_object_index, &camera_position);
|
||||
}
|
||||
if (target_object_index!=NONE)
|
||||
|
||||
@@ -703,9 +703,10 @@ typedef char network_game_server_countdown_state_offset_assert[
|
||||
offsetof(struct network_game_server, countdown_state) ==
|
||||
8 + HALO_PORT_NETWORK_GAME_SIZE + MAXIMUM_NETWORK_MACHINE_COUNT * 0x10 + 0xC ? 1 : -1];
|
||||
|
||||
#ifdef HALO_ANDROID
|
||||
/* bots' machines (below: the VR port's bots) */
|
||||
/* bots' machines (the VR port's bots, port/linux/game/bots.c; on the
|
||||
desktop none is ever marked, the writes live behind the Android guard) */
|
||||
static boolean network_game_server_bot_machines[MAXIMUM_NETWORK_MACHINE_COUNT];
|
||||
#ifdef HALO_ANDROID
|
||||
static short network_game_server_bot_machine_count(struct network_game_server *server);
|
||||
#endif
|
||||
|
||||
@@ -4340,6 +4341,19 @@ static boolean network_game_server_have_all_machines_have_precached(
|
||||
return all_machines_have_precached;
|
||||
}
|
||||
|
||||
/* whether the machine at this index is one of the host's bots (the VR
|
||||
port's bots.c asks, on every platform: on the desktop no bot machine is
|
||||
ever marked, so this is always false there) */
|
||||
boolean network_game_server_is_bot_machine(
|
||||
long machine_index)
|
||||
{
|
||||
struct network_game_server *server = global_network_game_server_get();
|
||||
|
||||
return server && VALID_INDEX(machine_index, MAXIMUM_NETWORK_MACHINE_COUNT) &&
|
||||
network_game_server_bot_machines[machine_index] &&
|
||||
server->game.machines[machine_index].machine_index == machine_index;
|
||||
}
|
||||
|
||||
#ifdef HALO_ANDROID
|
||||
/* ---------- bots (the VR port's: port/linux/game/bots.c)
|
||||
|
||||
@@ -4363,16 +4377,6 @@ static void network_game_server_bot_copy_name(
|
||||
destination[index] = 0;
|
||||
}
|
||||
|
||||
boolean network_game_server_is_bot_machine(
|
||||
long machine_index)
|
||||
{
|
||||
struct network_game_server *server = global_network_game_server_get();
|
||||
|
||||
return server && VALID_INDEX(machine_index, MAXIMUM_NETWORK_MACHINE_COUNT) &&
|
||||
network_game_server_bot_machines[machine_index] &&
|
||||
server->game.machines[machine_index].machine_index == machine_index;
|
||||
}
|
||||
|
||||
static short network_game_server_bot_machine_count(
|
||||
struct network_game_server *server)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user