Compare commits

...
Author SHA1 Message Date
Stefan Maier 719563a48c Merge branch 'feature/release-install' into test/comfort-release 2026-10-06 22:08:41 +02:00
Stefan MaierandClaude Opus 5.5 32cc1cb206 VR roadmap: step 2's second part built on feature/vr-comfort
Buttons remapped, the flashlight by tapping the head, finer turning and
vignette steps, the transparent geometry groups raised: what they do, the
settings, what the emulator showed and the headset tests for the Quest 3
and the Steam Frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 22:07:28 +02:00
Stefan MaierandClaude Opus 5.5 adf220d344 VR: finer turning steps, an on-foot vignette, a COMFORT page
The VR options get a fourth page, COMFORT (PLAY, COMFORT, BUTTONS, MORE;
the tabs beside the title share its width): Turning, Snap turn, Smooth
turn speed and the vehicle vignette move there from PLAY, joined by the
on-foot vignette and its strength. PLAY keeps the hands, laser,
crosshair, motion tracker, body, fingers and the Warthog's wheel.

- Snap turns step through 10 to 45 degrees by 5, then 60, 75 and 90 (were
  30, 45, 60, 90); smooth turning through 30 to 150 degrees a second by
  15, then to 300 by 30 (were 60 to 180 by 30); the vehicle vignette
  through Off, 15, 25, 35, 50, 60, 70, 85 and 100% (were Off, Light 35%,
  Medium 70%, Strong). The old values are among the steps; the defaults
  (snap 30, 120 a second, vignette 70%) are unchanged.
- vr.foot_vignette ("off" default, "turning", "moving") and
  vr.foot_vignette_strength (0.7): on foot the eye images darken from
  their edges while the turning stick turns smoothly and, with "moving",
  while the move stick moves, as far as the sticks are pushed beyond
  their dead zone (fully at three quarters), eased in and out as the
  vehicle vignette is. Not in a vehicle's seat (whether or not its view
  is first person), not in the menus; snap turns have none. The stronger
  of the two vignettes is shown. Off by default: nothing changes.

Checked in the emulator (debug.vr_options): the four tabs and the rows
without overlaps, each row's steps, the values kept over a restart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 22:05:10 +02:00
Stefan MaierandClaude Opus 5.5 32ad9ed6b0 VR: the flashlight by tapping the side or top of the head
Either hand brought to the side or the top of the head and away again, as
a tap, toggles the flashlight (vr.flashlight_gesture, default true; a row
on the VR options' BUTTONS page); the flashlight's button keeps working.
After HaloCE-Quest-VR (CC0), whose off hand near the head's middle
toggles it, made stricter against false toggles:

- The zone is in the head's axes: beside the head (7 to 24 cm out, from
  10 cm below the eyes to 18 cm above) or over it (within 12 cm of its
  middle line, 8 to 30 cm up), never more than 6 cm in front of the eyes,
  so a scope held to the eye, a two-handed weapon, a punch and the wrist's
  motion tracker, all in front of the face, are outside it.
- A tap: the hand comes from 35 cm or more from the head's middle, enters
  slower than 3 m/s (a throw past the ear is faster), stays 40 to 600 ms
  (adjusting the headset takes longer), alone (both hands at the head is
  the headset adjusted) and leaves 4 cm past the zone's edges; toggles are
  at least a second apart.
- None while the menus are up, while recentring, while a grenade is held
  or for 0.6 s after one is thrown, within 0.7 s of a melee swing, with
  both hands on the weapon, with the weapon hand while the scope is drawn,
  or with a hand on a Warthog's wheel; such a hand must leave the head
  before it taps.
- A toggle presses the flashlight's button (white) for 100 ms, as B's tap
  does, and gives the tapping hand a short rumble.

The tap's state machine was run off the headset in a small harness over
taps beside and over the head, a turned head, a second tap within the
cooldown, a long dwell, both hands, a scope at the eye, the scope flag, a
fast throw past the ear, a held grenade and a two-handed grip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 22:00:53 +02:00
Stefan MaierandClaude Opus 5.5 cf19bcfdfa VR: the game's actions moved between the controllers' buttons
A BUTTONS page in the VR options (between PLAY and MORE) puts jump, the
flashlight (held: switch grenades), action/reload, switch weapon, crouch,
zoom and melee on any of seven buttons: A, B, X, Y, the move and turning
sticks' clicks and the weapon hand's grip. Moving an action to a button
swaps it with the action there, so every action always has a button;
"Reset buttons to defaults" puts them back. The triggers, the free hand's
grip (both hands on the weapon), the menu button, the Steam Frame's
bumpers, D-pad and View button keep what they do, and the menus keep
their buttons.

Settings vr.button_jump, _light, _action, _weapon, _crouch, _zoom and
_melee ("a", "b", "x", "y", "move_stick", "turn_stick", "grip"; the
defaults are today's layout), named as right-handed. vr.c reads them with
the live settings and, after the left-handed mirroring
(controllers_mirrored), reads each action's button and hands it on as the
button that action has by default (controls_remapped), so xinput_sdl.c's
table is unchanged and the defaults change nothing. Because the mapping
sits after the mirroring it works the same on the Quest's Touch and the
Steam Frame's controllers (mirrored on the Frame the left D-pad is A, B,
X, Y), and a mirrored player's swaps are the mirror image of the
right-handed ones. The guest/host ABI is unchanged.

- The melee's aim and the weapon's jab (melee_update) follow the melee's
  button, not the grip as such; the grips holding a Warthog's wheel press
  nothing whatever action the grip has (moved from xinput_sdl.c).
- The controller buttons page (now on the BUTTONS page) shows each action
  under the controller and name of its button, for both layouts and the
  mirrored hands; its ON FOOT tab no longer draws the MENUS tab's notes
  over its own.
- debug.vr_options (Android, false): the VR options panel off the
  headset, for checking its pages in the emulator.

Checked in the emulator (debug.vr_options): the rows, swaps, reset, the
controls page right- and left-handed, the settings kept over a restart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:57:15 +02:00
Stefan MaierandClaude Opus 5.5 3b5796f1a0 VR roadmap: the keyboard's password in the clear is no open point
Nobody else sees the headset's view (decided 2026-10-06): no masking.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:47:26 +02:00
Stefan MaierandClaude Opus 5.5 68135a712d Transparent geometry: room for 2048 groups a window (was the Xbox's 384)
Every transparent thing a window draws is a group of its own: a model's
transparent part (shields, plasma, glass), each particle system, contrail
and light volume, a window's or water's surface. The Xbox had room for 384
(rasterizer_transparent_geometry_new_group); past that new_group returns
NULL and the submitter drops the geometry, logging "### ERROR too many
transparent geometry groups" once (rasterizer_xbox_models.c,
rasterizer_xbox_widgets.c; environment and dynavobgeom drop it silently).
render_window submits the objects first, then the structure's transparent
geometry, weather, particles, particle systems and contrails, so in games
of 30 and more players the late ones vanish: water and glass surfaces,
explosions, muzzle flashes, smoke, plasma glows, contrails.

The groups are a heap array (0xA0 each), a short array of sorted indices
and a bit vector of pending flags, all sized by the constant; indices are
shorts (sorted_index, presorted indices, the draw loop), good to 32767.
Nothing else depends on the number (the debug view's "all groups" index
1000 only colours groups in a debug mode). Upstream (halo-ce-universal)
still has 384.

2048 groups take 320 KB (61 KB before). A one-line log tells when a window
passes 384 and every 128 beyond, for checking big games in the headset.
In the emulator, a 36-player bot game on Blood Gulch had up to 134 groups
a window from a standing player, using under 8 KB of the rasterizer's
768 KB memory pool (the models' queued matrices and lighting), so the
pool is not the next limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 21:47:22 +02:00
8 changed files with 906 additions and 87 deletions

No files matched your search

+101 -6
View File
@@ -35,7 +35,7 @@ named where code is taken over.
| --- | --- | --- |
| 1 | Steam Frame CPU time: Mesa's trace markers off (after astromaddie/HaloCE-VR), no `glGet` stalls in the visibility counters' copies | **done** (in `main` 2026-10-06): markers quieted, no problems; the frame's work about unchanged (11-12.5 ms of 13.9 in a bot game) |
| 3a | Visible arms and hands: the arms by IK to the controllers, fingers by trigger and grip | **done** (in `main` 2026-10-06; Quest 3 and Steam Frame good; 0.02 ms a frame) |
| 2 | Controls and comfort: left-handed mode, buttons remapped, flashlight by tapping the head, finer turning and vignette choices, graphics presets and refresh rate in the VR options | first part **done** (in `main` 2026-10-06; Quest 3 and Steam Frame good): left-handed mode, graphics presets, refresh rate and resolution in the VR options. Left: buttons remapped, flashlight by tapping the head, finer turning and vignette choices |
| 2 | Controls and comfort: left-handed mode, buttons remapped, flashlight by tapping the head, finer turning and vignette choices, graphics presets and refresh rate in the VR options | first part **done** (in `main` 2026-10-06; Quest 3 and Steam Frame good): left-handed mode, graphics presets, refresh rate and resolution in the VR options. Second part **built** (branch `feature/vr-comfort`, headset test pending): buttons remapped, flashlight by tapping the head, finer turning and vignette choices |
| 3b | The whole body: torso from head and hands, legs and feet following in the room; full / arms only / hands only | |
| 3c | Weapons held physically, holsters at shoulder and hip (the buttons stay) | |
| 3d | Other VR players shown with their heads, hands and bodies (today their bodies move as flat players' do: only their aim and throws come from their hands) | |
@@ -106,7 +106,7 @@ the GPU's fill.
VR options (Y): two pages, PLAY (the comfort rows, "Weapon hand",
"Mirrored controls") and MORE ("Graphics", "Refresh rate", "Resolution",
the controller buttons page, Quit). The refresh rate steps through what
the controller buttons page, Quit); four since the second part (below). The refresh rate steps through what
the runtime offers and changes at once; on the Steam Frame the row shows
SteamOS's rate. The resolution (`vr.resolution_scale`, 70-120%) takes a
restart. The controller buttons page shows the layout as set.
@@ -136,6 +136,100 @@ To try in the headset (Quest 3 and Steam Frame):
in a bot game; Max on the Quest at 90 Hz; refresh rates 72/80/90/120
on the Quest; the Frame's row.
## Step 2, second part (built)
On branch `feature/vr-comfort`, not yet tried in the headset. The VR
options (Y) have four pages: PLAY ("Weapon hand", "Mirrored controls",
laser, crosshair, motion tracker, body, fingers, the Warthog's wheel),
COMFORT (turning, the vignettes), BUTTONS (the actions' buttons, the
flashlight's tap, "Reset buttons to defaults", the controller buttons
page) and MORE (graphics, refresh rate, resolution, Quit).
Buttons remapped (BUTTONS page; `vr.c` `controls_remapped`):
- Jump, the flashlight (held: switch grenades), action/reload, switch
weapon, crouch, zoom and melee each go on one of seven buttons: A, B,
X, Y, the move stick's click, the turning stick's, the weapon hand's
grip. Moving an action to a button swaps it with the action there, so
none is ever left without one.
- The triggers, the free hand's grip, the menu button, the Steam Frame's
bumpers, D-pad and View button keep theirs; the menus keep the buttons
as they are.
- Applied after the left-handed mirroring, on the gamepad's sides: the
same on the Touch and the Frame controllers, and a left-handed player's
buttons are the mirror image of the right-handed ones (on the Frame,
mirrored, the left D-pad's). The defaults are the old layout, the
host's bindings and the ABI unchanged.
- The melee's aim and jab follow its button; the grips on a Warthog's
wheel press nothing, whatever the grip does.
- The controller buttons page shows each action where its button is.
Flashlight by tapping the head (`vr.c` `flashlight_gesture_update`,
after HaloCE-Quest-VR, CC0): either hand to the side or the top of the
head and away, as a tap, toggles it; the button still does. A zone in
the head's axes beside or over the head, never more than 6 cm in front
of the eyes (a scope at the eye, a two-handed weapon, a punch and the
wrist's tracker are in front of the face); the hand comes from 35 cm
away, slower than 3 m/s, stays 40 to 600 ms, alone, and leaves; a second
between toggles. None in the menus, with a grenade held or just thrown,
just after a melee, with both hands on the weapon, the weapon hand while
the scope is drawn, a hand on the wheel. A short rumble in the tapping
hand. Its logic was run in a harness off the headset; the feel is the
headset's to judge.
Comfort (COMFORT page): snap turns 10 to 45 degrees by 5, 60, 75, 90;
smooth turning 30 to 150 degrees a second by 15, then to 300 by 30; the
vehicle vignette Off, 15 to 100%; the on-foot vignette (off by default)
while the stick turns smoothly, or also while the move stick moves, as
far as the sticks are pushed, with its own strength. The old values are
among the steps; defaults unchanged.
Transparent geometry: room for 2048 groups a window (the Xbox's 384,
below).
| Setting | Values |
| --- | --- |
| `vr.button_jump` | `"a"` (default), `"b"`, `"x"`, `"y"`, `"move_stick"`, `"turn_stick"`, `"grip"`: as right-handed, mirrored with `vr.mirror_controls` |
| `vr.button_light` | the flashlight, held switch grenades; `"b"` (default) |
| `vr.button_action` | action, reload; `"x"` (default) |
| `vr.button_weapon` | switch weapon; `"y"` (default) |
| `vr.button_crouch` | `"move_stick"` (default) |
| `vr.button_zoom` | `"turn_stick"` (default) |
| `vr.button_melee` | `"grip"` (default) |
| `vr.flashlight_gesture` | `true` (default): a tap of the head toggles the flashlight |
| `vr.foot_vignette` | `"off"` (default), `"turning"` (smooth turns), `"moving"` (and moving) |
| `vr.foot_vignette_strength` | 0.15 to 1, `0.7` (default) |
| `debug.vr_options` | `false` (default); `true` shows the VR options off the headset (the emulator) |
Checked in the emulator (`debug.vr_options`): the four pages without
overlaps, each row's values, swaps and reset, the controller buttons page
right- and left-handed after swaps, the settings kept over a restart, a
15-bot game as before.
To try in the headset (Quest 3 and Steam Frame):
1. Defaults: every button as before (jump, light/held grenades, action,
switch weapon, crouch, zoom, melee by grip and by swing), right- and
left-handed, mirrored and not; the menus' A and B.
2. BUTTONS: swap jump and melee (jump on the grip, melee on A): both work,
the melee jabs and aims where the head looks; light on a stick click
(tap: light, hold: grenades); the controller buttons page says so.
At the Warthog's wheel with jump on the grip: no jump while holding
the wheel. Reset: all back.
3. The same left-handed with mirrored controls (on the Frame the left
D-pad), and with `Mirrored controls: Off`.
4. Flashlight tap with either hand, beside the head and over it; the
rumble; the flashlight button still works. No toggle while: aiming
through the sniper's and the pistol's scope, holding the weapon with
both hands, holding and throwing a grenade (overhand past the ear),
punching and swinging, putting on or adjusting the headset with both
hands or one, looking at the wrist's tracker, at the wheel.
5. COMFORT: snap 10 and 90, smooth 30 and 300; vehicle vignette 15% and
100%; on-foot vignette "Smooth turns" (nothing with snap turns),
"Moving, turning", strength 15% and 100%; no vignette in a vehicle
from the on-foot one, none in the menus.
6. A game of 30 and more players: effects, glass and water stay; the log
(`adb logcat -s halo`) says `rasterizer: N transparent geometry groups
in a window` once a window passes 384; frame time as before.
## Step 3a (done)
Tried on the Quest 3 and the Steam Frame, in `main`. The arm IK,
@@ -194,10 +288,11 @@ the left hand animated through reloads.
- Multiview on the Steam Frame: why the eyes do not fuse there (suspected
Mesa zink/Turnip); tools in branch `frame/doubled-images` (86335828),
not merged.
- Transparent geometry groups: the Xbox's 384 a window overflow in games
of 30 and more players.
- The VR on-screen keyboard shows a password in the clear while it is
typed.
- Transparent geometry groups: the Xbox's 384 a window overflowed in
games of 30 and more players (the last submitted, glass, water,
particles, contrails, vanished). Raised to 2048 (320 KB) on
`feature/vr-comfort`, to check in a large game (step 2, second part,
test 6); upstream still has 384.
## Headset tests (2026-10-06, all three in `main`)
+36
View File
@@ -162,6 +162,9 @@ enum
int halo_vr_body_mode(void);
/* a hand has just been thrust forward: a melee attack (xinput_sdl.c) */
int halo_vr_melee(void);
/* a hand has just tapped the head (vr.flashlight_gesture): the flashlight's
button, for a moment (xinput_sdl.c) */
int halo_vr_flashlight_tap(void);
/* the weight of the melee attack under way, from the swing's speed (0..1;
0 with none): the game scales melee damage by the player's run instead,
and in VR a player punches standing (units.c) */
@@ -252,6 +255,39 @@ int halo_vr_controller_layout(void);
/* the hand that holds the weapon (HALO_XR_HAND_*: 1 the right, 0 the left,
vr.handedness) */
int halo_vr_weapon_hand(void);
/* The game's actions on the controllers' buttons (vr.button_*; the VR
options' BUTTONS page): each action on one of seven buttons, named as
right-handed (A and B on the right controller, X and Y, the Quest's, on
the left; the move stick's click, the turning stick's, the weapon hand's
grip); left-handed with vr.mirror_controls, their mirror images. The
actions are numbered by the button each has by default. The triggers, the
free hand's grip, the menu button and the Steam Frame's bumpers, D-pad and
View button keep theirs; the menus keep the buttons as they are. */
enum
{
HALO_VR_INPUT_A = 0,
HALO_VR_INPUT_B,
HALO_VR_INPUT_X,
HALO_VR_INPUT_Y,
HALO_VR_INPUT_MOVE_STICK,
HALO_VR_INPUT_TURN_STICK,
HALO_VR_INPUT_GRIP,
HALO_VR_INPUT_COUNT,
HALO_VR_ACTION_JUMP = HALO_VR_INPUT_A,
HALO_VR_ACTION_LIGHT = HALO_VR_INPUT_B, /* a tap the flashlight; held, switch grenades */
HALO_VR_ACTION_ACTION = HALO_VR_INPUT_X, /* action, reload */
HALO_VR_ACTION_WEAPON = HALO_VR_INPUT_Y, /* switch weapon */
HALO_VR_ACTION_CROUCH = HALO_VR_INPUT_MOVE_STICK,
HALO_VR_ACTION_ZOOM = HALO_VR_INPUT_TURN_STICK,
HALO_VR_ACTION_MELEE = HALO_VR_INPUT_GRIP,
HALO_VR_ACTION_COUNT = HALO_VR_INPUT_COUNT,
};
/* an action's setting ("vr.button_jump"), a button's value in it ("a"),
and the button an action is on, as set */
const char *halo_vr_action_setting(int action);
const char *halo_vr_input_value(int input);
int halo_vr_action_input(int action);
/* the graphics preset (vr.graphics; HALO_VR_GRAPHICS_HIGH outside VR): which
effects are drawn (port/linux/game/vr_graphics.c), and with "max" the
models lit for each pixel and larger shadows (d3d8_gl.c) */
+43 -2
View File
@@ -201,6 +201,28 @@ static const struct config_setting config_settings[] =
"and the left one turns, X jumps and A reloads (on the Steam Frame the\n"
"D-pad takes A, B, X and Y as their mirror image); false keeps them as\n"
"right-handed. The menus keep their buttons either way." },
{ "vr.button_jump", _config_string, "\"a\"", "HALO_VR_BUTTON_JUMP", _environment_value, _platform_android,
"The button each of the game's actions is on (the VR options' BUTTONS\n"
"page swaps them): \"a\", \"b\", \"x\", \"y\" (A and B on the right\n"
"controller, X and Y the Quest's on the left; the Steam Frame has all\n"
"four on the right), \"move_stick\" and \"turn_stick\" (their clicks),\n"
"\"grip\" (the weapon hand's), named as right-handed: left-handed with\n"
"mirror_controls, their mirror images. Jump," },
{ "vr.button_light", _config_string, "\"b\"", "HALO_VR_BUTTON_LIGHT", _environment_value, _platform_android,
"the flashlight (held: switch grenades)," },
{ "vr.button_action", _config_string, "\"x\"", "HALO_VR_BUTTON_ACTION", _environment_value, _platform_android,
"action and reload," },
{ "vr.button_weapon", _config_string, "\"y\"", "HALO_VR_BUTTON_WEAPON", _environment_value, _platform_android,
"switch weapon," },
{ "vr.button_crouch", _config_string, "\"move_stick\"", "HALO_VR_BUTTON_CROUCH", _environment_value,
_platform_android,
"crouch," },
{ "vr.button_zoom", _config_string, "\"turn_stick\"", "HALO_VR_BUTTON_ZOOM", _environment_value,
_platform_android,
"zoom" },
{ "vr.button_melee", _config_string, "\"grip\"", "HALO_VR_BUTTON_MELEE", _environment_value, _platform_android,
"and melee. The triggers, the free hand's grip and the menu button keep\n"
"theirs, as do the menus." },
{ "vr.laser", _config_boolean, "false", "HALO_VR_LASER", _environment_value, _platform_android,
"A laser from the weapon to what it aims at (with aim_with = \"controller\")." },
{ "vr.crosshair", _config_boolean, "true", "HALO_VR_CROSSHAIR", _environment_value, _platform_android,
@@ -229,6 +251,13 @@ static const struct config_setting config_settings[] =
{ "vr.arm_ik", _config_boolean, "true", "HALO_VR_ARM_IK", _environment_value, _platform_android,
"The free hand's arm bends at the elbow to reach it from the shoulder;\n"
"false keeps the arm's shape as on the weapon, pointed at the shoulder." },
{ "vr.flashlight_gesture", _config_boolean, "true", "HALO_VR_FLASHLIGHT_GESTURE", _environment_value,
_platform_android,
"Tapping the side or the top of your head with either hand (to it and\n"
"away again) turns the flashlight on or off; the flashlight's button\n"
"still does. Not while aiming through a scope, holding the weapon with\n"
"both hands, holding or throwing a grenade, just after a melee, or with\n"
"both hands at the head." },
{ "vr.melee_swing_speed", _config_real, "2.0", "HALO_VR_MELEE_SWING", _environment_value, _platform_android,
"Thrusting a hand forward faster than this, in metres per second, is a\n"
"melee attack (the weapon hand's grip is too); 0 turns it off." },
@@ -304,6 +333,13 @@ static const struct config_setting config_settings[] =
{ "vr.comfort_vignette", _config_real, "0.7", "HALO_VR_COMFORT_VIGNETTE", _environment_value, _platform_android,
"How far the view darkens from its edges while a vehicle turns you\n"
"(0 never, 1 to a small window)." },
{ "vr.foot_vignette", _config_string, "\"off\"", "HALO_VR_FOOT_VIGNETTE", _environment_value, _platform_android,
"On foot, the view darkens from its edges too: \"turning\" while the stick\n"
"turns you smoothly (smooth_turn), \"moving\" while a stick turns or moves\n"
"you, as far as it is pushed; \"off\" never." },
{ "vr.foot_vignette_strength", _config_real, "0.7", "HALO_VR_FOOT_VIGNETTE_STRENGTH", _environment_value,
_platform_android,
"How far it darkens then (as comfort_vignette)." },
{ "vr.motion_tracker_on_wrist", _config_boolean, "true", "HALO_VR_MOTION_TRACKER_WRIST", _environment_value,
_platform_android,
"Show the motion tracker on the back of the left wrist, like a watch (turn\n"
@@ -324,9 +360,11 @@ static const struct config_setting config_settings[] =
{ "vr.smooth_turn", _config_boolean, "false", "HALO_VR_SMOOTH_TURN", _environment_value, _platform_android,
"The right stick turns smoothly; false turns in steps (snap_turn_degrees)." },
{ "vr.snap_turn_degrees", _config_real, "30.0", "HALO_VR_SNAP_TURN", _environment_value, _platform_android,
"How far a flick of the right stick turns." },
"How far a flick of the right stick turns, in degrees (the VR options step\n"
"through 10 to 45 by 5, 60, 75 and 90)." },
{ "vr.smooth_turn_speed", _config_real, "120.0", "HALO_VR_SMOOTH_TURN_SPEED", _environment_value, _platform_android,
"Degrees per second the right stick turns at, fully pushed, with smooth_turn." },
"Degrees per second the right stick turns at, fully pushed, with smooth_turn\n"
"(the VR options step through 30 to 150 by 15, then to 300 by 30)." },
{ "vr.cinematics_in_3d", _config_boolean, "false", "HALO_VR_CINEMATICS_3D", _environment_value, _platform_android,
"Show cinematics around you, from the cinematic camera; false shows them\n"
"on the screen in front of you (easier on the stomach)." },
@@ -573,6 +611,9 @@ static const struct config_setting config_settings[] =
"The headset's layer copies: 1 deferred (no waits for the GL thread or the\n"
"GPU in the middle of the frame), 0 each one restored and released at once,\n"
"-1 as the runtime needs (deferred on SteamVR). Read when the app starts." },
{ "debug.vr_options", _config_boolean, "false", "HALO_DEBUG_VR_OPTIONS", _environment_value, _platform_android,
"Off the headset (the emulator), show the VR options panel on the main\n"
"and pause menus all the same, for checking its pages." },
{ "debug.mirror_first_person", _config_boolean, "false", "HALO_MIRROR_FIRST_PERSON", _environment_value,
_platform_android,
"Off the headset (flat), the first-person weapon drawn mirrored, as the\n"
+334 -21
View File
@@ -17,6 +17,28 @@ yaw plus the head's.
#include "halo_vr.h"
/* the actions' settings and the buttons' values in them (halo_vr.h's
HALO_VR_ACTION_* and HALO_VR_INPUT_*), by number */
static const char *const vr_action_settings[HALO_VR_ACTION_COUNT] =
{
"vr.button_jump", "vr.button_light", "vr.button_action", "vr.button_weapon", "vr.button_crouch",
"vr.button_zoom", "vr.button_melee",
};
static const char *const vr_input_values[HALO_VR_INPUT_COUNT] =
{
"a", "b", "x", "y", "move_stick", "turn_stick", "grip",
};
const char *halo_vr_action_setting(int action)
{
return action >= 0 && action < HALO_VR_ACTION_COUNT ? vr_action_settings[action] : "";
}
const char *halo_vr_input_value(int input)
{
return input >= 0 && input < HALO_VR_INPUT_COUNT ? vr_input_values[input] : "";
}
#ifdef HALO_ANDROID
#include "platform.h"
@@ -36,6 +58,14 @@ yaw plus the head's.
/* the facing the game changes itself by more than this (a respawn, a
teleport, a vehicle's seat) turns the body with it */
#define FACING_JUMP (10.0f * PI / 180.0f)
/* vr.foot_vignette: none on foot, while the stick turns smoothly, and
while a stick turns smoothly or moves */
enum
{
FOOT_VIGNETTE_OFF = 0,
FOOT_VIGNETTE_TURNING,
FOOT_VIGNETTE_MOVING,
};
static struct
{
@@ -133,9 +163,13 @@ static struct
float vehicle_steer_smoothing; /* seconds */
float steer[2]; /* the steering as smoothed: aside, up (-1..1) */
unsigned long long steer_last_us;
/* the comfort vignette: how strong at most, now, and the heading it
last saw */
/* the comfort vignette: how strong at most in a vehicle, on foot
(vr.foot_vignette: FOOT_VIGNETTE_*) and how strong there, now, and the
heading it last saw */
float comfort_vignette;
int foot_vignette;
float foot_vignette_strength;
int seated; /* in a vehicle's seat, whatever the view */
float vignette;
float vignette_last_yaw;
int vignette_yaw_valid;
@@ -178,6 +212,7 @@ static struct
captured, and where its disc is in LOCAL space */
int scope_pass;
int scope_captured;
int scope_last_frame; /* the scope was drawn the frame before */
struct halo_xr_pose scope_pose;
int motion_tracker_on_wrist;
float motion_tracker_size;
@@ -220,9 +255,23 @@ static struct
/* until when a melee attack aims where the head looks: its damage is
dealt some ticks into the attack, along the unit's aim */
unsigned long long melee_aim_until_us;
int melee_grip_down;
int melee_button_down;
float melee_strength; /* 0..1, the last attack's swing */
unsigned long long melee_jab_start_us; /* the weapon hand's grip's attack: the weapon jabs */
/* the flashlight by tapping the head (flashlight_gesture_update,
vr.flashlight_gesture): each hand where it was (the head's axes) and
whether it has been away from the head since its last tap; the hand at
the head now and since when (-1: none); when the next toggle may come,
and until when the game sees the flashlight's button */
int flashlight_gesture;
float tap_last[HALO_XR_HAND_COUNT][3];
int tap_last_valid[HALO_XR_HAND_COUNT];
int tap_armed[HALO_XR_HAND_COUNT];
int tap_hand;
unsigned long long tap_entered_us;
unsigned long long tap_last_us;
unsigned long long tap_ready_us;
unsigned long long flashlight_until_us;
unsigned long long melee_jab_start_us; /* the melee button's attack: the weapon jabs */
/* the laser along the barrel, for the frame it was set in */
unsigned long frame_number;
unsigned long laser_frame;
@@ -250,6 +299,9 @@ static struct
vr.mirror_controls the sticks and face buttons mirrored too */
int left_handed;
int mirror_controls;
/* the button each action is on (vr.button_*: HALO_VR_INPUT_* by
HALO_VR_ACTION_*) */
int action_inputs[HALO_VR_ACTION_COUNT];
/* vr.refresh_rate as last asked for (the VR options change it), and
vr.resolution_scale as the app started with it */
float refresh_rate_wanted;
@@ -307,6 +359,7 @@ static int name_pose(const float point[3], struct halo_xr_pose *pose, float *wid
static int weapon_hand_tracked(short local_player_index);
static void controller_axes(float forward[3], float up[3], float right[3]);
static void hand_offset(float out[3]);
static void controls_in_play(struct halo_xr_hand hands[HALO_XR_HAND_COUNT]);
/* the hand that holds the weapon, the free one (the grenade, the
foregrip), and the one whose stick turns the body and steers */
@@ -494,6 +547,14 @@ static void vr_read_live_settings(void)
vr.laser = config_boolean("vr.laser");
vr.vehicle_wheel = config_boolean("vr.vehicle_wheel");
vr.comfort_vignette = (float)config_real("vr.comfort_vignette");
{
const char *foot = config_string("vr.foot_vignette");
vr.foot_vignette = !strcmp(foot, "moving") ? FOOT_VIGNETTE_MOVING :
!strcmp(foot, "turning") ? FOOT_VIGNETTE_TURNING : FOOT_VIGNETTE_OFF;
}
vr.foot_vignette_strength = (float)config_real("vr.foot_vignette_strength");
vr.flashlight_gesture = config_boolean("vr.flashlight_gesture");
vr.fingers = config_boolean("vr.fingers");
vr.arm_ik = config_boolean("vr.arm_ik");
{
@@ -517,6 +578,12 @@ static void vr_read_live_settings(void)
vr.left_handed = left_handed;
vr.mirror_controls = config_boolean("vr.mirror_controls");
}
{
int action;
for (action = 0; action < HALO_VR_ACTION_COUNT; action++)
vr.action_inputs[action] = halo_vr_action_input(action);
}
{
/* (the refresh rate changes at once where the runtime lets it) */
float refresh_rate = (float)config_real("vr.refresh_rate");
@@ -535,6 +602,7 @@ void halo_vr_initialize(void)
if (vr.active || !getenv("HALO_VR"))
return;
vr_read_live_settings();
vr.tap_hand = -1;
vr.cinematics_3d = config_boolean("vr.cinematics_in_3d");
vr.hud_distance = (float)config_real("vr.hud_distance");
vr.hud_width = 2.0f * vr.hud_distance * tanf((float)config_real("vr.hud_field_of_view") * PI / 360.0f);
@@ -619,6 +687,22 @@ int halo_vr_weapon_hand(void)
return weapon_hand();
}
int halo_vr_action_input(int action)
{
const char *value;
int input;
if (action < 0 || action >= HALO_VR_ACTION_COUNT)
return HALO_VR_INPUT_A;
value = config_string(vr_action_settings[action]);
for (input = 0; input < HALO_VR_INPUT_COUNT; input++)
{
if (!strcmp(value, vr_input_values[input]))
return input;
}
return action;
}
int halo_vr_refresh_rates(float *rates, int maximum, float *current)
{
*current = 0.0f;
@@ -980,18 +1064,21 @@ static void melee_update(void)
vr.hand_last_us = now;
{
/* the weapon hand's grip's melee attack (xinput_sdl.c) aims the
same way */
int grip_down = vr.frame.hands[weapon_hand()].active &&
(vr.frame.hands[weapon_hand()].buttons & HALO_XR_BUTTON_SQUEEZE);
/* the melee button's attack (xinput_sdl.c; the weapon hand's grip
unless vr.button_melee moves it) aims the same way */
struct halo_xr_hand hands[HALO_XR_HAND_COUNT];
int button_down;
if (grip_down && !vr.melee_grip_down)
memcpy(hands, vr.frame.hands, sizeof(hands));
controls_in_play(hands);
button_down = (hands[HALO_XR_HAND_RIGHT].buttons & HALO_XR_BUTTON_SQUEEZE) != 0;
if (button_down && !vr.melee_button_down)
{
vr.melee_aim_until_us = now + 700000;
vr.melee_strength = 0.75f;
vr.melee_jab_start_us = now;
}
vr.melee_grip_down = grip_down;
vr.melee_button_down = button_down;
}
quaternion_rotate(vr.frame.head.orientation, look, facing);
facing[1] = 0.0f;
@@ -1038,6 +1125,148 @@ static void melee_update(void)
}
}
/* The flashlight by tapping the head (vr.flashlight_gesture; after
HaloCE-Quest-VR, CC0, whose off hand brought near the head's middle
toggles it): either hand brought to the side or the top of the head and
away again, as a tap, toggles the flashlight; its button still does.
The zone is in the head's axes (metres from between the eyes; x right,
y up, z back): beside the head, from 7 to 24 cm out, 10 cm below the eyes
to 18 cm above them; or over it, within 12 cm of its middle line, 8 to
30 cm up; either way no further forward than 6 cm before the eyes, so a
scope held to the eye, a two-handed weapon and a punch, all in front of
the face, are not in it. A tap is a hand coming from 35 cm or more from
the head's middle into the zone, slower than 3 m/s (a grenade's throw
past the ear is faster), staying 40 to 600 ms (adjusting the headset
takes longer) alone (both hands at the head adjust it), and leaving it 4
cm past its edges; toggles are a second apart. No taps while the menus
are up, while a grenade is held or just thrown, after a melee, with both
hands on the weapon, with the weapon hand at the scope, or with a hand on
a Warthog's wheel. */
#define TAP_SPEED 3.0f
#define TAP_AWAY 0.35f
#define TAP_MARGIN 0.04f
#define TAP_DWELL_SHORTEST 40000ULL
#define TAP_DWELL_LONGEST 600000ULL
#define TAP_COOLDOWN 1000000ULL
static int tap_zone(const float local[3], float margin)
{
float x = fabsf(local[0]), y = local[1], z = local[2];
if (z < -0.06f - margin || z > 0.18f + margin)
return 0;
if (x >= 0.07f - margin && x <= 0.24f + margin && y >= -0.10f - margin && y <= 0.18f + margin)
return 1;
return x <= 0.12f + margin && y >= 0.08f - margin && y <= 0.30f + margin;
}
static void flashlight_gesture_update(void)
{
unsigned long long now = now_us();
float seconds = vr.tap_last_us ? (float)(now - vr.tap_last_us) * 1e-6f : 0.0f;
float inverse[4];
int blocked, hand;
vr.tap_last_us = now;
inverse[0] = -vr.frame.head.orientation[0];
inverse[1] = -vr.frame.head.orientation[1];
inverse[2] = -vr.frame.head.orientation[2];
inverse[3] = vr.frame.head.orientation[3];
blocked = !vr.flashlight_gesture || !vr.frame.focused || vr.last_overlay_mode == HALO_XR_OVERLAY_SCREEN ||
vr.recentre_frames > 0 || vr.grenade_held || now - vr.grenade_released_us < 600000 ||
now < vr.melee_ready_us || vr.two_hand_latched;
for (hand = 0; hand < HALO_XR_HAND_COUNT; hand++)
{
const struct halo_xr_hand *state = &vr.frame.hands[hand];
float relative[3], local[3], away[3], speed = 0.0f;
int axis, inside, outside;
if (!state->active || !(state->flags & HALO_XR_POSE_VALID))
{
vr.tap_last_valid[hand] = 0;
vr.tap_armed[hand] = 0;
if (vr.tap_hand == hand)
vr.tap_hand = -1;
continue;
}
for (axis = 0; axis < 3; axis++)
relative[axis] = state->grip.position[axis] - vr.frame.head.position[axis];
quaternion_rotate(inverse, relative, local);
if (vr.tap_last_valid[hand] && seconds > 0.0f && seconds < 0.1f)
{
float moved[3];
for (axis = 0; axis < 3; axis++)
moved[axis] = local[axis] - vr.tap_last[hand][axis];
speed = sqrtf(moved[0] * moved[0] + moved[1] * moved[1] + moved[2] * moved[2]) / seconds;
}
memcpy(vr.tap_last[hand], local, sizeof(vr.tap_last[hand]));
vr.tap_last_valid[hand] = 1;
away[0] = local[0];
away[1] = local[1];
away[2] = local[2] - 0.10f;
if (sqrtf(away[0] * away[0] + away[1] * away[1] + away[2] * away[2]) > TAP_AWAY)
vr.tap_armed[hand] = 1;
inside = tap_zone(local, 0.0f);
outside = !tap_zone(local, TAP_MARGIN);
if (blocked || (vr.wheel_hands & (1 << hand)) || (hand == weapon_hand() && vr.scope_last_frame))
{
/* (a hand that may be at the head for another reason taps
only once it has been away again) */
if (inside)
vr.tap_armed[hand] = 0;
if (vr.tap_hand == hand)
vr.tap_hand = -1;
continue;
}
if (vr.tap_hand == hand)
{
unsigned long long dwell = now - vr.tap_entered_us;
if (dwell > TAP_DWELL_LONGEST)
{
vr.tap_hand = -1;
}
else if (outside)
{
vr.tap_hand = -1;
if (dwell >= TAP_DWELL_SHORTEST && now >= vr.tap_ready_us)
{
vr.flashlight_until_us = now + 100000;
vr.tap_ready_us = now + TAP_COOLDOWN;
host_xr_haptic(hand, 0.5f, 0.05f);
platform_log("VR: flashlight, %s hand at the head for %llu ms", hand ? "right" : "left",
dwell / 1000);
}
}
}
else if (inside && vr.tap_armed[hand])
{
vr.tap_armed[hand] = 0;
if (vr.tap_hand >= 0)
{
/* (both hands at the head: the headset adjusted) */
vr.tap_hand = -1;
}
else if (speed <= TAP_SPEED)
{
vr.tap_hand = hand;
vr.tap_entered_us = now;
}
}
else if (inside && vr.tap_hand >= 0)
{
vr.tap_hand = -1;
}
}
}
int halo_vr_flashlight_tap(void)
{
return vr.active && now_us() < vr.flashlight_until_us;
}
/* The fingers, from where they rest on the controller (after
HaloCE-Quest-VR, CC0, its vr_frame.c): the thumb curled while it touches
the stick, a button or the rest, half curled where the controller does not
@@ -1378,13 +1607,45 @@ int halo_vr_wheel_held(void)
}
/* The comfort vignette: while a vehicle turns the player (from 15 degrees a
second, fully at 60), the eye images darken from their edges; it comes in
quickly and goes slowly. */
second, fully at 60), the eye images darken from their edges; on foot
(vr.foot_vignette), while the turning stick turns smoothly and, with
"moving", the move stick moves, as far as they are pushed (fully at three
quarters). It comes in quickly and goes slowly. */
static float foot_vignette_target(void)
{
const struct halo_xr_hand *turn = &vr.frame.hands[turn_hand()];
const struct halo_xr_hand *move = &vr.frame.hands[turn_hand() == HALO_XR_HAND_RIGHT ? HALO_XR_HAND_LEFT :
HALO_XR_HAND_RIGHT];
float push = 0.0f;
if (vr.foot_vignette == FOOT_VIGNETTE_OFF || vr.foot_vignette_strength <= 0.0f || vr.seated ||
!vr.frame.focused || vr.last_overlay_mode == HALO_XR_OVERLAY_SCREEN)
{
return 0.0f;
}
if (vr.smooth_turn && turn->active)
push = fabsf(turn->thumbstick[0]);
if (vr.foot_vignette == FOOT_VIGNETTE_MOVING && move->active)
{
float moving = sqrtf(move->thumbstick[0] * move->thumbstick[0] + move->thumbstick[1] * move->thumbstick[1]);
if (moving > push)
push = moving;
}
/* (beyond the sticks' dead zone, 0.15) */
push = (push - 0.15f) / 0.6f;
if (push < 0.0f)
push = 0.0f;
if (push > 1.0f)
push = 1.0f;
return push * (vr.foot_vignette_strength > 1.0f ? 1.0f : vr.foot_vignette_strength);
}
static void vignette_update(void)
{
unsigned long long now = now_us();
float seconds = vr.vignette_last_us ? (float)(now - vr.vignette_last_us) * 1e-6f : 0.0f;
float target = 0.0f, rate;
float target = foot_vignette_target(), rate;
vr.vignette_last_us = now;
if (seconds > 0.1f)
@@ -1396,13 +1657,15 @@ static void vignette_update(void)
if (vr.vignette_yaw_valid && seconds > 0.0f)
{
float turning = fabsf(wrap_angle(yaw - vr.vignette_last_yaw)) / seconds;
float vehicle = (turning - 15.0f * PI / 180.0f) / (45.0f * PI / 180.0f);
target = (turning - 15.0f * PI / 180.0f) / (45.0f * PI / 180.0f);
if (target < 0.0f)
target = 0.0f;
if (target > 1.0f)
target = 1.0f;
target *= vr.comfort_vignette;
if (vehicle < 0.0f)
vehicle = 0.0f;
if (vehicle > 1.0f)
vehicle = 1.0f;
vehicle *= vr.comfort_vignette;
if (vehicle > target)
target = vehicle;
}
vr.vignette_last_yaw = yaw;
vr.vignette_yaw_valid = 1;
@@ -1423,6 +1686,7 @@ void halo_vr_set_seat(short local_player_index, int seat, int armed, float vehic
{
if (!vr.active || local_player_index != 0)
return;
vr.seated = seat != HALO_VR_SEAT_NONE;
if (!vr.vehicle_first_person)
seat = HALO_VR_SEAT_NONE;
vr.seat_yaw = vehicle_yaw;
@@ -1581,6 +1845,7 @@ void halo_vr_frame_begin(void)
vr.frame_begun = 1;
vr.frame_number++;
vr.eyes_captured = 0;
vr.scope_last_frame = vr.scope_captured;
vr.scope_captured = 0;
vr.crosshair_captured = 0;
vr.name_count = 0;
@@ -1607,6 +1872,7 @@ void halo_vr_frame_begin(void)
look_update();
grenade_update();
melee_update();
flashlight_gesture_update();
fingers_update();
torso_update();
}
@@ -3046,6 +3312,51 @@ static void controllers_mirrored(struct halo_xr_hand hands[HALO_XR_HAND_COUNT])
}
}
/* The buttons the actions are on (vr.button_*), as the gamepad's sides
have them once mirrored: A and B the right hand's, X and Y the left's
A and B, the move stick's click the left's, the turning stick's the
right's, the grip the right's (the weapon hand's). Each action's button
is read and handed on as the button the action has by default, which
xinput_sdl.c turns into it; with the defaults nothing changes. The grips
holding a Warthog's wheel press nothing. */
static const int input_hands[HALO_VR_INPUT_COUNT] =
{
HALO_XR_HAND_RIGHT, HALO_XR_HAND_RIGHT, HALO_XR_HAND_LEFT, HALO_XR_HAND_LEFT, HALO_XR_HAND_LEFT,
HALO_XR_HAND_RIGHT, HALO_XR_HAND_RIGHT,
};
static const int input_buttons[HALO_VR_INPUT_COUNT] =
{
HALO_XR_BUTTON_A, HALO_XR_BUTTON_B, HALO_XR_BUTTON_A, HALO_XR_BUTTON_B, HALO_XR_BUTTON_THUMBSTICK,
HALO_XR_BUTTON_THUMBSTICK, HALO_XR_BUTTON_SQUEEZE,
};
static void controls_remapped(struct halo_xr_hand hands[HALO_XR_HAND_COUNT])
{
int down[HALO_VR_INPUT_COUNT];
int input, action;
for (input = 0; input < HALO_VR_INPUT_COUNT; input++)
down[input] = (hands[input_hands[input]].buttons & input_buttons[input]) != 0;
if (vr.wheel_hands)
down[HALO_VR_INPUT_GRIP] = 0;
for (input = 0; input < HALO_VR_INPUT_COUNT; input++)
hands[input_hands[input]].buttons &= ~input_buttons[input];
for (action = 0; action < HALO_VR_ACTION_COUNT; action++)
{
if (down[vr.action_inputs[action]])
hands[input_hands[action]].buttons |= input_buttons[action];
}
}
/* the controllers as the game is played with them: mirrored (left-handed)
and the actions on their buttons */
static void controls_in_play(struct halo_xr_hand hands[HALO_XR_HAND_COUNT])
{
if (vr.left_handed)
controllers_mirrored(hands);
controls_remapped(hands);
}
/* the controllers, for the gamepad the game reads (xinput_sdl.c) */
int halo_vr_controllers(struct halo_xr_hand hands[HALO_XR_HAND_COUNT], int *menus)
{
@@ -3053,8 +3364,8 @@ int halo_vr_controllers(struct halo_xr_hand hands[HALO_XR_HAND_COUNT], int *menu
return 0;
memcpy(hands, vr.frame.hands, sizeof(vr.frame.hands));
*menus = vr.last_overlay_mode == HALO_XR_OVERLAY_SCREEN;
if (vr.left_handed && !*menus)
controllers_mirrored(hands);
if (!*menus)
controls_in_play(hands);
return 1;
}
@@ -3062,6 +3373,7 @@ int halo_vr_controllers(struct halo_xr_hand hands[HALO_XR_HAND_COUNT], int *menu
int halo_vr_controller_layout(void) { return 0; }
int halo_vr_weapon_hand(void) { return 1; }
int halo_vr_action_input(int action) { return action; }
int halo_vr_graphics(void) { return HALO_VR_GRAPHICS_HIGH; }
int halo_vr_refresh_rates(float *rates, int maximum, float *current)
{
@@ -3128,6 +3440,7 @@ void halo_vr_debug_look(short local_player_index, const float unit_looking[3], i
(void)local_player_index; (void)unit_looking; (void)in_vehicle;
}
int halo_vr_melee(void) { return 0; }
int halo_vr_flashlight_tap(void) { return 0; }
int halo_vr_finger_curls(short local_player_index, int hand, float curls[4])
{
(void)local_player_index; (void)hand; (void)curls;
+14 -5
View File
@@ -840,7 +840,9 @@ In play, laid out for VR shooters rather than by position:
right trigger fire left trigger grenade: held in the hand, thrown as it lets go (vr.c)
right grip melee (B) left grip hold the weapon with both hands (vr.c)
(a hand thrust forward, vr.melee_swing_speed, is a melee attack too)
(a hand thrust forward, vr.melee_swing_speed, is a melee attack too;
a hand tapped on the side or top of the head, vr.flashlight_gesture,
the flashlight)
A jump X action, reload (X), as the game's prompts say
B flashlight (white); held, switch grenade (black)
Y switch weapon (Y)
@@ -859,6 +861,11 @@ face buttons too (the right stick moves, X jumps, A reloads; on the Steam
Frame the left D-pad takes A, B, X and Y). Here "right" and "left" are the
sides so handed over.
The VR options' BUTTONS page (vr.button_*) moves jump, the flashlight,
action, switch weapon, crouch, zoom and melee between A, B, X, Y, the
stick clicks and the weapon hand's grip: vr.c hands each action's button
over as the one above, so "A" here is the jump wherever it is.
In the menus the buttons are the Xbox's by position: A accepts, B goes back. */
#include "guest_host.h"
@@ -866,6 +873,7 @@ In the menus the buttons are the Xbox's by position: A accepts, B goes back. */
int halo_vr_controllers(struct halo_xr_hand hands[HALO_XR_HAND_COUNT], int *menus);
int halo_vr_wheel_held(void);
int halo_vr_flashlight_tap(void);
/* a click that stands for the pad's A (ui_widget.c: the screen where
controllers join by pressing it), held a moment */
@@ -937,14 +945,15 @@ static void vr_gamepad_state(XINPUT_GAMEPAD *pad)
}
merge_button(pad, XINPUT_GAMEPAD_A, (right->buttons & HALO_XR_BUTTON_A) != 0);
merge_button(pad, XINPUT_GAMEPAD_X, (left->buttons & HALO_XR_BUTTON_A) != 0);
/* (the grips holding a Warthog's wheel are not a blow) */
merge_button(pad, XINPUT_GAMEPAD_B,
((right->buttons & HALO_XR_BUTTON_SQUEEZE) != 0 && !halo_vr_wheel_held()) || halo_vr_melee());
/* (the grips holding a Warthog's wheel are not a blow: vr.c) */
merge_button(pad, XINPUT_GAMEPAD_B, (right->buttons & HALO_XR_BUTTON_SQUEEZE) != 0 || 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);
/* (and a tap of the head, vr.flashlight_gesture: vr.c) */
merge_button(pad, XINPUT_GAMEPAD_WHITE,
(right->buttons & HALO_XR_BUTTON_BUMPER) != 0 || halo_vr_flashlight_tap());
{
/* B: a tap is the flashlight, holding it switches grenades; the
game sees a short press of either */
+360 -52
View File
@@ -6648,6 +6648,7 @@ double config_real(char const *name);
char const *config_string(char const *name);
int config_write(char const *name, char const *value);
int config_write_boolean(char const *name, int value);
int config_default(char const *name, char *text, size_t size);
enum
{
@@ -6655,6 +6656,8 @@ enum
_vr_option_snap_angle,
_vr_option_smooth_speed,
_vr_option_vignette,
_vr_option_foot_vignette,
_vr_option_foot_vignette_strength,
_vr_option_wheel,
_vr_option_handedness,
_vr_option_mirror_controls,
@@ -6663,6 +6666,11 @@ enum
_vr_option_motion_tracker,
_vr_option_body,
_vr_option_fingers,
/* (the game's actions on the buttons, HALO_VR_ACTION_*'s order) */
_vr_option_button_first,
_vr_option_button_last = _vr_option_button_first + HALO_VR_ACTION_COUNT - 1,
_vr_option_flashlight_gesture,
_vr_option_buttons_reset,
_vr_option_graphics,
_vr_option_refresh_rate,
_vr_option_resolution,
@@ -6675,7 +6683,17 @@ enum
VR_OPTIONS_ROW_HEIGHT = 22,
VR_OPTIONS_FOOTER = 362,
VR_OPTIONS_PER_PAGE = 12,
NUMBER_OF_VR_OPTION_PAGES = 2,
_vr_options_page_play = 0,
_vr_options_page_comfort,
_vr_options_page_buttons,
_vr_options_page_more,
NUMBER_OF_VR_OPTION_PAGES,
/* (the pages' tabs, beside the title, as wide as there is room) */
VR_OPTIONS_TABS_LEFT = 216,
VR_OPTIONS_TABS_RIGHT = 528,
VR_OPTIONS_TAB_WIDTH = (VR_OPTIONS_TABS_RIGHT - VR_OPTIONS_TABS_LEFT) / NUMBER_OF_VR_OPTION_PAGES,
_vr_controls_on_foot = 0,
_vr_controls_vehicles,
@@ -6694,14 +6712,22 @@ static struct
} ui_vr_options_globals;
/* the panel's pages (its tabs), their rows in order (NONE past the last) */
static char const *const vr_option_page_names[NUMBER_OF_VR_OPTION_PAGES] = { "PLAY", "MORE" };
static char const *const vr_option_page_names[NUMBER_OF_VR_OPTION_PAGES] = { "PLAY", "COMFORT", "BUTTONS", "MORE" };
static short const vr_option_pages[NUMBER_OF_VR_OPTION_PAGES][VR_OPTIONS_PER_PAGE] =
{
{ _vr_option_turning, _vr_option_snap_angle, _vr_option_smooth_speed, _vr_option_vignette, _vr_option_wheel,
_vr_option_handedness, _vr_option_mirror_controls, _vr_option_laser, _vr_option_crosshair,
_vr_option_motion_tracker, _vr_option_body, _vr_option_fingers },
{ _vr_option_graphics, _vr_option_refresh_rate, _vr_option_resolution, _vr_option_controls, _vr_option_quit,
NONE, NONE, NONE, NONE, NONE, NONE, NONE },
{ _vr_option_handedness, _vr_option_mirror_controls, _vr_option_laser, _vr_option_crosshair,
_vr_option_motion_tracker, _vr_option_body, _vr_option_fingers, _vr_option_wheel,
NONE, NONE, NONE, NONE },
{ _vr_option_turning, _vr_option_snap_angle, _vr_option_smooth_speed, _vr_option_vignette,
_vr_option_foot_vignette, _vr_option_foot_vignette_strength,
NONE, NONE, NONE, NONE, NONE, NONE },
{ _vr_option_button_first + HALO_VR_ACTION_JUMP, _vr_option_button_first + HALO_VR_ACTION_LIGHT,
_vr_option_button_first + HALO_VR_ACTION_ACTION, _vr_option_button_first + HALO_VR_ACTION_WEAPON,
_vr_option_button_first + HALO_VR_ACTION_CROUCH, _vr_option_button_first + HALO_VR_ACTION_ZOOM,
_vr_option_button_first + HALO_VR_ACTION_MELEE, _vr_option_flashlight_gesture, _vr_option_buttons_reset,
_vr_option_controls, NONE, NONE },
{ _vr_option_graphics, _vr_option_refresh_rate, _vr_option_resolution, _vr_option_quit,
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE },
};
/* the page a row is on, and its place there */
@@ -6754,12 +6780,151 @@ static boolean ui_vr_controls_mirrored(
return ui_vr_left_handed() && config_boolean("vr.mirror_controls");
}
/* Where a button the game's actions go on (HALO_VR_INPUT_*) is, as the
hands are set: its controller (0 the left, 1 the right) and its name,
short under its controller on the controls page, or full in the
options' rows. On the Quest A and B are on the right, X and Y on the
left, and mirrored the other way round; on the Steam Frame all four are
on the right, and mirrored the left D-pad takes them (down, left, right,
up). The sticks' clicks change sides with them; the grip is the weapon
hand's. */
static char const *ui_vr_input_name(
short input,
boolean full,
short *column)
{
static char const *const buttons[] = { "A", "B", "X", "Y" };
static char const *const quest_mirrored[] = { "X", "Y", "A", "B" };
static char const *const frame_mirrored[] = { "D-pad down", "D-pad left", "D-pad right", "D-pad up" };
boolean frame = halo_vr_controller_layout() == 1;
boolean mirrored = ui_vr_controls_mirrored();
short side;
char const *name;
switch (input)
{
case HALO_VR_INPUT_MOVE_STICK:
case HALO_VR_INPUT_TURN_STICK:
side = (short)((input == HALO_VR_INPUT_TURN_STICK) != (mirrored != 0));
name = full ? (side ? "Right stick click" : "Left stick click") : "Stick click";
break;
case HALO_VR_INPUT_GRIP:
side = (short)(ui_vr_left_handed() ? 0 : 1);
name = full ? (side ? "Right grip" : "Left grip") : "Grip";
break;
default:
input = (short)PIN(input, HALO_VR_INPUT_A, HALO_VR_INPUT_Y);
if (frame)
{
side = (short)(mirrored ? 0 : 1);
name = mirrored ? frame_mirrored[input] : buttons[input];
}
else
{
side = (short)((input <= HALO_VR_INPUT_B) != (mirrored != 0));
name = mirrored ? quest_mirrored[input] : buttons[input];
}
break;
}
if (column)
*column = side;
return name;
}
/* the game's actions on the buttons (HALO_VR_ACTION_*'s order): as the
options' rows name them, and in short (the Steam Frame's stick lines) */
static char const *const vr_action_names[HALO_VR_ACTION_COUNT] =
{
"Jump", "Light; held: grenades", "Action, reload", "Switch weapon", "Crouch", "Zoom", "Melee",
};
static char const *const vr_action_short_names[HALO_VR_ACTION_COUNT] =
{
"jump", "light", "action", "weapon", "crouch", "zoom", "melee",
};
/* an action moved to the next button or the one before: the action that
was there takes its old one (so that every action keeps a button) */
static void ui_vr_options_button_change(
short action,
short change)
{
short input = (short)halo_vr_action_input(action);
short wanted = (short)((input + change + HALO_VR_INPUT_COUNT) % HALO_VR_INPUT_COUNT);
short other;
for (other = 0; other < HALO_VR_ACTION_COUNT; other++)
{
if (other != action && halo_vr_action_input(other) == wanted)
config_write(halo_vr_action_setting(other), halo_vr_input_value(input));
}
config_write(halo_vr_action_setting(action), halo_vr_input_value(wanted));
}
static boolean ui_vr_options_buttons_default(
void)
{
short action;
for (action = 0; action < HALO_VR_ACTION_COUNT; action++)
{
if (halo_vr_action_input(action) != action)
return FALSE;
}
return TRUE;
}
static void ui_vr_options_buttons_reset(
void)
{
short action;
for (action = 0; action < HALO_VR_ACTION_COUNT; action++)
{
char text[32];
if (config_default(halo_vr_action_setting(action), text, sizeof(text)))
config_write(halo_vr_action_setting(action), text);
}
}
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 };
static char const *const vr_vignette_names[] = { "Off", "Light", "Medium", "Strong" };
/* (in finer steps than they had: the old values among them) */
static real const vr_snap_angles[] = { 10.0f, 15.0f, 20.0f, 25.0f, 30.0f, 35.0f, 40.0f, 45.0f, 60.0f, 75.0f, 90.0f };
static real const vr_smooth_speeds[] = { 30.0f, 45.0f, 60.0f, 75.0f, 90.0f, 105.0f, 120.0f, 135.0f, 150.0f, 180.0f,
210.0f, 240.0f, 270.0f, 300.0f };
static real const vr_vignettes[] = { 0.0f, 0.15f, 0.25f, 0.35f, 0.5f, 0.6f, 0.7f, 0.85f, 1.0f };
/* the on-foot vignette's strengths (vr.foot_vignette_strength: the
vignettes' without Off), and vr.foot_vignette's values */
static real const vr_foot_vignette_strengths[] = { 0.15f, 0.25f, 0.35f, 0.5f, 0.6f, 0.7f, 0.85f, 1.0f };
static char const *const vr_foot_vignettes[] = { "off", "turning", "moving" };
static char const *const vr_foot_vignette_names[] = { "Off", "Smooth turns", "Moving, turning" };
static short ui_vr_options_foot_vignette(
void)
{
char const *mode = config_string("vr.foot_vignette");
short index;
for (index = 0; index < (short)NUMBEROF(vr_foot_vignettes); index++)
{
if (mode && strcmp(mode, vr_foot_vignettes[index]) == 0)
return index;
}
return 0;
}
/* a vignette's strength as the rows show it: Off, or how far it darkens */
static void ui_vr_options_vignette_text(
char *text,
size_t size,
real strength)
{
if (strength <= 0.0f)
snprintf(text, size, "Off");
else
snprintf(text, size, "%.0f%%", strength * 100.0f);
}
/* vr.body's values, and as the panel shows them */
static char const *const vr_bodies[] = { "arms", "hands", "off" };
static char const *const vr_body_names[] = { "Arms and hands", "Hands only", "Weapon only" };
@@ -6838,8 +7003,9 @@ static boolean ui_vr_options_screen(
{
struct widget_instance *screen = widget_globals.active_widgets[0];
/* (the Xbox's main menu, or the PC version's: vr.menus) */
return halo_vr_active() && screen && screen->name &&
/* (the Xbox's main menu, or the PC version's: vr.menus; off the
headset with debug.vr_options, to check the pages) */
return (halo_vr_active() || config_boolean("debug.vr_options")) && screen && screen->name &&
(main_menu_screen_is_active() || strstr(screen->name, "pause_game") != NULL ||
strcmp(screen->name, "main_menu") == 0);
}
@@ -6891,6 +7057,14 @@ static void ui_vr_options_change(
case _vr_option_vignette:
ui_vr_options_step("vr.comfort_vignette", vr_vignettes, NUMBEROF(vr_vignettes), change);
break;
case _vr_option_foot_vignette:
config_write("vr.foot_vignette", vr_foot_vignettes[(ui_vr_options_foot_vignette() + change +
(short)NUMBEROF(vr_foot_vignettes)) % (short)NUMBEROF(vr_foot_vignettes)]);
break;
case _vr_option_foot_vignette_strength:
ui_vr_options_step("vr.foot_vignette_strength", vr_foot_vignette_strengths,
NUMBEROF(vr_foot_vignette_strengths), change);
break;
case _vr_option_wheel: config_write_boolean("vr.vehicle_wheel", !config_boolean("vr.vehicle_wheel")); break;
case _vr_option_handedness: config_write("vr.handedness", ui_vr_left_handed() ? "right" : "left"); break;
case _vr_option_mirror_controls:
@@ -6928,6 +7102,12 @@ static void ui_vr_options_change(
case _vr_option_resolution:
ui_vr_options_step("vr.resolution_scale", vr_resolution_scales, NUMBEROF(vr_resolution_scales), change);
break;
case _vr_option_flashlight_gesture:
config_write_boolean("vr.flashlight_gesture", !config_boolean("vr.flashlight_gesture"));
break;
case _vr_option_buttons_reset:
ui_vr_options_buttons_reset();
break;
case _vr_option_controls:
ui_vr_options_globals.controls = TRUE;
ui_vr_options_globals.page = _vr_controls_on_foot;
@@ -6940,6 +7120,11 @@ static void ui_vr_options_change(
ui_vr_options_globals.quit_armed = TRUE;
break;
}
if (ui_vr_options_globals.selection >= _vr_option_button_first &&
ui_vr_options_globals.selection <= _vr_option_button_last)
{
ui_vr_options_button_change((short)(ui_vr_options_globals.selection - _vr_option_button_first), change);
}
ui_play_audio_feedback_sound(_ui_audio_feedback_forward);
}
@@ -7056,7 +7241,8 @@ static boolean ui_vr_options_take_event(
{
ui_vr_options_change(1);
}
else if (change && ui_vr_options_globals.selection != _vr_option_controls)
else if (change && ui_vr_options_globals.selection != _vr_option_controls &&
ui_vr_options_globals.selection != _vr_option_buttons_reset)
{
ui_vr_options_change(change);
}
@@ -7113,10 +7299,11 @@ static boolean ui_vr_options_take_pointer(
{
short page = ui_vr_options_page_of(ui_vr_options_globals.selection, NULL);
if (pointer->left_clicks && y >= 68 && y < 88 && x >= 330 && x < 514)
if (pointer->left_clicks && y >= 68 && y < 88 && x >= VR_OPTIONS_TABS_LEFT && x < VR_OPTIONS_TABS_RIGHT)
{
/* (a tab: its page) */
short tab = (short)PIN((x - 330) / 92, 0, NUMBER_OF_VR_OPTION_PAGES - 1);
short tab = (short)PIN((x - VR_OPTIONS_TABS_LEFT) / VR_OPTIONS_TAB_WIDTH, 0,
NUMBER_OF_VR_OPTION_PAGES - 1);
if (tab != page)
{
@@ -7196,6 +7383,77 @@ static void ui_vr_controls_add(
}
}
/* the action on a button (vr.button_*; the swaps keep one on each) */
static short ui_vr_input_action(
short input)
{
short action;
for (action = 0; action < HALO_VR_ACTION_COUNT; action++)
{
if (halo_vr_action_input(action) == input)
return action;
}
return input;
}
/* in a vehicle: the action button gets in and out, the light's is the
flashlight */
static void ui_vr_controls_add_vehicle_actions(
struct ui_vr_controls_column columns[2])
{
short column;
char const *name = ui_vr_input_name((short)halo_vr_action_input(HALO_VR_ACTION_ACTION), FALSE, &column);
ui_vr_controls_add(&columns[column], name, "Get in, get out");
name = ui_vr_input_name((short)halo_vr_action_input(HALO_VR_ACTION_LIGHT), FALSE, &column);
ui_vr_controls_add(&columns[column], name, "Flashlight");
}
/* an action's line under the controller its button is on (vr.button_*), as
the Quest's page words it or, shorter, the Steam Frame's (whose page has
more lines) */
static void ui_vr_controls_add_action(
struct ui_vr_controls_column columns[2],
short action,
boolean frame)
{
/* (one action is held: its name lives on with the column) */
static char held[24];
short column;
char const *name = ui_vr_input_name((short)halo_vr_action_input(action), FALSE, &column);
switch (action)
{
case HALO_VR_ACTION_LIGHT:
if (frame)
{
ui_vr_controls_add(&columns[column], name, "Light; held: grenades");
}
else
{
snprintf(held, sizeof(held), "%s held", strcmp(name, "Stick click") == 0 ? "Click" : name);
ui_vr_controls_add(&columns[column], name, "Flashlight");
ui_vr_controls_add(&columns[column], held, "Switch grenades");
}
break;
case HALO_VR_ACTION_MELEE:
if (frame)
{
ui_vr_controls_add(&columns[column], name, "Melee (or punch)");
}
else
{
ui_vr_controls_add(&columns[column], name, "Melee (or punch");
ui_vr_controls_add(&columns[column], "", "forward)");
}
break;
default:
ui_vr_controls_add(&columns[column], name, vr_action_names[action]);
break;
}
}
static void ui_vr_controls_column_render(
short x,
short y,
@@ -7264,26 +7522,27 @@ static void ui_vr_controls_render(
{
/* 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; mirrored, the
D-pad does what A, B, X and Y do (down, left, right, up) */
char const *jump = mirrored ? "D-pad down" : "A";
char const *light = mirrored ? "D-pad left" : "B";
char const *act = mirrored ? "D-pad right" : "X";
char const *weapon = mirrored ? "D-pad up" : "Y";
short buttons = mirrored ? 0 : 1;
D-pad does what A, B, X and Y do (down, left, right, up). The sticks'
clicks on the sticks' lines, to keep to the page's lines */
static char move_line[32], turn_line[32];
switch (ui_vr_options_globals.page)
{
case _vr_controls_on_foot:
ui_vr_controls_add(&columns[move], "Stick", "Move; click: crouch");
ui_vr_controls_add(&columns[turn], "Stick", "Turn; click: zoom");
snprintf(move_line, sizeof(move_line), "Move; click: %s",
vr_action_short_names[ui_vr_input_action(HALO_VR_INPUT_MOVE_STICK)]);
snprintf(turn_line, sizeof(turn_line), "Turn; click: %s",
vr_action_short_names[ui_vr_input_action(HALO_VR_INPUT_TURN_STICK)]);
ui_vr_controls_add(&columns[move], "Stick", move_line);
ui_vr_controls_add(&columns[turn], "Stick", turn_line);
ui_vr_controls_add(&columns[gun], "Trigger", "Fire");
ui_vr_controls_add(&columns[free_column], "Trigger", "Grenade: hold, throw");
ui_vr_controls_add(&columns[gun], "Grip", "Melee (or punch)");
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_GRIP), TRUE);
ui_vr_controls_add(&columns[free_column], "Grip", "Weapon in both hands");
ui_vr_controls_add(&columns[buttons], jump, "Jump");
ui_vr_controls_add(&columns[buttons], light, "Light; held: grenades");
ui_vr_controls_add(&columns[buttons], act, "Action, reload");
ui_vr_controls_add(&columns[buttons], weapon, "Switch weapon");
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_A), TRUE);
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_B), TRUE);
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_X), TRUE);
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_Y), TRUE);
ui_vr_controls_add(&columns[mirrored ? 0 : 1], "Bumper", "Flashlight");
ui_vr_controls_add(&columns[mirrored ? 1 : 0], "Bumper", "Switch grenades");
ui_vr_controls_add(&columns[0], "View", "Scores");
@@ -7294,6 +7553,8 @@ static void ui_vr_controls_render(
ui_vr_controls_add(&columns[1], "Menu", "Pause");
ui_vr_controls_note(320, wrist_note);
ui_vr_controls_note(340, "Scope: zoom, and look through the disc above the weapon.");
if (config_boolean("vr.flashlight_gesture"))
ui_vr_controls_note(360, "Tap the side or top of your head: the flashlight.");
break;
case _vr_controls_vehicles:
ui_vr_controls_add(&columns[move], "Stick", "Drive");
@@ -7302,8 +7563,7 @@ static void ui_vr_controls_render(
ui_vr_controls_add(&columns[gun], "Trigger", "Fire");
ui_vr_controls_add(&columns[free_column], "Trigger", "Second weapon");
ui_vr_controls_add(&columns[free_column], "", "(Banshee, tank)");
ui_vr_controls_add(&columns[buttons], act, "Get in, get out");
ui_vr_controls_add(&columns[buttons], light, "Flashlight");
ui_vr_controls_add_vehicle_actions(columns);
break;
default:
ui_vr_controls_add(&columns[0], "Stick", "Choose");
@@ -7322,31 +7582,28 @@ static void ui_vr_controls_render(
/* the Quest's: A and B on the right controller, X, Y and the menu
button on the left; mirrored, X and Y do what A and B do, and the
other way round */
short jumps = mirrored ? 0 : 1;
short acts = (short)(1 - jumps);
switch (ui_vr_options_globals.page)
{
case _vr_controls_on_foot:
ui_vr_controls_add(&columns[move], "Stick", "Move");
ui_vr_controls_add(&columns[move], "Stick click", "Crouch");
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_MOVE_STICK), FALSE);
ui_vr_controls_add(&columns[turn], "Stick", "Turn");
ui_vr_controls_add(&columns[turn], "Stick click", "Zoom");
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_TURN_STICK), FALSE);
ui_vr_controls_add(&columns[gun], "Trigger", "Fire");
ui_vr_controls_add(&columns[gun], "Grip", "Melee (or punch");
ui_vr_controls_add(&columns[gun], "", "forward)");
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_GRIP), FALSE);
ui_vr_controls_add(&columns[free_column], "Trigger", "Grenade: hold it,");
ui_vr_controls_add(&columns[free_column], "", "let go as you throw");
ui_vr_controls_add(&columns[free_column], "Grip", "Weapon in both hands");
ui_vr_controls_add(&columns[jumps], jumps ? "A" : "X", "Jump");
ui_vr_controls_add(&columns[jumps], jumps ? "B" : "Y", "Flashlight");
ui_vr_controls_add(&columns[jumps], jumps ? "B held" : "Y held", "Switch grenades");
ui_vr_controls_add(&columns[acts], acts ? "A" : "X", "Action, reload");
ui_vr_controls_add(&columns[acts], acts ? "B" : "Y", "Switch weapon");
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_A), FALSE);
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_B), FALSE);
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_X), FALSE);
ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_Y), FALSE);
ui_vr_controls_add(&columns[0], "Menu", "Pause");
ui_vr_controls_note(320, wrist_note);
ui_vr_controls_note(340, "Scope: zoom, and look through the disc above the weapon.");
ui_vr_controls_note(360, "Hold the Meta button to recentre the view.");
if (config_boolean("vr.flashlight_gesture"))
ui_vr_controls_note(380, "Tap the side or top of your head: the flashlight.");
break;
case _vr_controls_vehicles:
ui_vr_controls_add(&columns[move], "Stick", "Drive");
@@ -7355,8 +7612,7 @@ static void ui_vr_controls_render(
ui_vr_controls_add(&columns[gun], "Trigger", "Fire");
ui_vr_controls_add(&columns[free_column], "Trigger", "Second weapon");
ui_vr_controls_add(&columns[free_column], "", "(Banshee, tank)");
ui_vr_controls_add(&columns[acts], acts ? "A" : "X", "Get in, get out");
ui_vr_controls_add(&columns[jumps], jumps ? "B" : "Y", "Flashlight");
ui_vr_controls_add_vehicle_actions(columns);
break;
default:
ui_vr_controls_add(&columns[0], "Stick", "Choose");
@@ -7373,6 +7629,9 @@ static void ui_vr_controls_render(
ui_vr_controls_column_render(right, y, &columns[1]);
switch (ui_vr_options_globals.page)
{
case _vr_controls_on_foot:
/* (its notes are with its lines, above) */
break;
case _vr_controls_vehicles:
ui_vr_controls_note(300, "Warthog: grip the wheel with one hand or both and turn it.");
ui_vr_controls_note(320, left_handed ? "Holding it, the left trigger accelerates, the right one brakes" :
@@ -7429,15 +7688,15 @@ static void ui_vr_options_render(
ui_join_code_draw_quad(110, 62, 530, 396, 0xFF050A18);
ui_join_code_draw_quad(110, 62, 530, 64, 0xFF3A6EA5);
ui_join_code_draw_quad(110, 394, 530, 396, 0xFF3A6EA5);
ui_join_code_draw_text(126, 330, 72, "VR OPTIONS", FALSE, 0, &title_color);
ui_join_code_draw_text(126, VR_OPTIONS_TABS_LEFT, 72, "VR OPTIONS", FALSE, 0, &title_color);
/* (the pages' tabs, beside the title) */
for (tab = 0; tab < NUMBER_OF_VR_OPTION_PAGES; tab++)
{
short x0 = (short)(330 + tab * 92);
short x0 = (short)(VR_OPTIONS_TABS_LEFT + tab * VR_OPTIONS_TAB_WIDTH);
if (tab == page)
ui_join_code_draw_quad(x0, 68, (short)(x0 + 88), 88, 0x803A6EA5);
ui_join_code_draw_text(x0, (short)(x0 + 88), 72, vr_option_page_names[tab], FALSE, 2,
ui_join_code_draw_quad(x0, 68, (short)(x0 + VR_OPTIONS_TAB_WIDTH - 4), 88, 0x803A6EA5);
ui_join_code_draw_text(x0, (short)(x0 + VR_OPTIONS_TAB_WIDTH - 4), 72, vr_option_page_names[tab], FALSE, 2,
tab == page ? &text_color : &dim_color);
}
for (row = 0; row < ui_vr_options_page_rows(page); row++)
@@ -7467,10 +7726,23 @@ static void ui_vr_options_render(
unused = !smooth;
break;
case _vr_option_vignette:
label = "Vehicle comfort vignette";
snprintf(value, sizeof(value), "%s", vr_vignette_names[ui_vr_options_nearest(
label = "Vehicle vignette";
ui_vr_options_vignette_text(value, sizeof(value), vr_vignettes[ui_vr_options_nearest(
(real)config_real("vr.comfort_vignette"), vr_vignettes, NUMBEROF(vr_vignettes))]);
break;
case _vr_option_foot_vignette:
label = "On-foot vignette";
snprintf(value, sizeof(value), "%s", vr_foot_vignette_names[ui_vr_options_foot_vignette()]);
/* (smooth turns alone, with snap turning: nothing to darken) */
unused = ui_vr_options_foot_vignette() == 1 && !smooth;
break;
case _vr_option_foot_vignette_strength:
label = "On-foot vignette strength";
ui_vr_options_vignette_text(value, sizeof(value), vr_foot_vignette_strengths[ui_vr_options_nearest(
(real)config_real("vr.foot_vignette_strength"), vr_foot_vignette_strengths,
NUMBEROF(vr_foot_vignette_strengths))]);
unused = ui_vr_options_foot_vignette() == 0;
break;
case _vr_option_wheel:
label = "Warthog steering wheel";
snprintf(value, sizeof(value), "%s", config_boolean("vr.vehicle_wheel") ? "On" : "Off (stick)");
@@ -7537,9 +7809,27 @@ static void ui_vr_options_render(
snprintf(value, sizeof(value), "%.0f%%", scale * 100.0f);
break;
}
case _vr_option_flashlight_gesture:
label = "Light by tapping the head";
snprintf(value, sizeof(value), "%s", config_boolean("vr.flashlight_gesture") ? "On" : "Off");
break;
case _vr_option_buttons_reset:
label = "Reset buttons to defaults";
unused = ui_vr_options_buttons_default();
break;
case _vr_option_controls:
label = "Controller buttons...";
break;
default:
if (option >= _vr_option_button_first && option <= _vr_option_button_last)
{
short action = (short)(option - _vr_option_button_first);
label = vr_action_names[action];
snprintf(value, sizeof(value), "%s", ui_vr_input_name((short)halo_vr_action_input(action), TRUE,
NULL));
}
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";
@@ -7556,7 +7846,25 @@ static void ui_vr_options_render(
ui_join_code_draw_text(330, 512, y, shown, FALSE, 1, unused ? &dim_color : &text_color);
}
}
if (page == 1)
if (page == _vr_options_page_comfort)
{
/* (what the vignettes do) */
ui_join_code_draw_text(128, 512, 248, "The vignette darkens the view from its edges while the", FALSE, 0,
&dim_color);
ui_join_code_draw_text(128, 512, 266, "game moves you: in a vehicle as it turns you; on foot, if", FALSE, 0,
&dim_color);
ui_join_code_draw_text(128, 512, 284, "on, as the sticks turn you smoothly (and move you).", FALSE, 0,
&dim_color);
}
if (page == _vr_options_page_buttons)
{
/* (what the buttons' rows do) */
ui_join_code_draw_text(128, 512, 320, "An action moved to a button swaps with the one there;", FALSE, 0,
&dim_color);
ui_join_code_draw_text(128, 512, 338, "left-handed and mirrored, the buttons are mirrored too.", FALSE, 0,
&dim_color);
}
if (page == _vr_options_page_more)
{
/* (what the graphics rows do) */
ui_join_code_draw_text(128, 512, 230, "Graphics: Medium and Low leave effects out (shadows,", FALSE, 0,
+8 -1
View File
@@ -31,7 +31,14 @@ enum
RASTERIZER_MAXIMUM_TRIANGLES_PER_TRIANGLE_BUFFER = 24576,
RASTERIZER_MAXIMUM_DEBUG_PRIMITIVES = 131072,
RASTERIZER_MAXIMUM_DEBUG_VERTICES = 393216,
RASTERIZER_MAXIMUM_TRANSPARENT_GEOMETRY_GROUPS = 384,
/* port: the Xbox's 384 groups a window (each a model's transparent part,
a particle system, a contrail, a light volume, a window's or water's
surface) are spent in games of 30 and more players, and whatever is
submitted after the last is dropped, the structure's glass and water
and the particles first (render_window submits the objects before
them). Their indices are shorts, the pending flags a bit vector of
this size: 2048 groups take 320 KB. */
RASTERIZER_MAXIMUM_TRANSPARENT_GEOMETRY_GROUPS = 2048,
RASTERIZER_MAXIMUM_TRANSPARENT_GEOMETRY_GROUPS2 = 32,
RASTERIZER_MAXIMUM_DYNAMIC_TRIANGLES = 32768,
RASTERIZER_MAXIMUM_DYNAMIC_TRIANGLE_BUFFERS = 1024,
@@ -150,6 +150,7 @@ typedef char transparent_geometry_group_cortana_hack_offset_assert[
/* ---------- prototypes */
static void rasterizer_sort_external(
void);
void platform_log(char const *format, ...);
/* ---------- globals */
@@ -162,6 +163,9 @@ static long transparent_geometry_group_count = 0;
static long transparent_geometry_group_count2 = 0;
static short *transparent_geometry_group_sorted_indices = NULL;
static short transparent_geometry_next_group_sorted_index = 0;
/* port: the most groups a window has had, logged as it passes the Xbox's
384 and every 128 beyond (rasterizer.h) */
static long transparent_geometry_group_count_logged = 384;
/* ---------- public code */
@@ -200,6 +204,12 @@ boolean rasterizer_transparent_geometry_initialize(
void rasterizer_transparent_geometry_begin(
void)
{
if (transparent_geometry_group_count>transparent_geometry_group_count_logged)
{
platform_log("rasterizer: %ld transparent geometry groups in a window (the Xbox had room for 384, the port %d)",
transparent_geometry_group_count, RASTERIZER_MAXIMUM_TRANSPARENT_GEOMETRY_GROUPS);
transparent_geometry_group_count_logged = (transparent_geometry_group_count/128 + 1)*128;
}
transparent_geometry_group_count = 0;
transparent_geometry_next_group_sorted_index = 0;
memset(transparent_geometry_group_pending_flags, 0,