Fix strafing on foot, the invisible laser, script text and glassy windshields (headset-unverified)

From the 2026-10-05 wear (round 2):
- The drive throttle zeroed the stick's sideways on foot too (whenever
  vr.vehicle_steering was "drive"): now only for a driver.
- The pointer's beam pose wasn't a unit quaternion (look_rotation from an
  unnormalised direction), so SteamVR refused every frame that drew it
  (XR_ERROR_POSE_INVALID, 564 frames in the wear): look_rotation now
  normalises, and the host normalises every layer pose. The beam ends
  2 cm short of the panel (a quad through another hides in it).
- Level scripts' print (green over cutscenes as a map loads) goes to
  debug.txt only in the headset, unless game.console_log is "all".
- Glass reflects vr.glass_reflection (0.3) of the game's own in the
  headset: the Warthog's windshield was too bright to see through.
This commit is contained in:
Madison committed 2026-10-05 10:51:54 +09:00
1 parent 6853839b85
commit 5a88349558
8 files changed
+118 -3

No files matched your search

+19
View File
@@ -478,3 +478,22 @@ the Frame's display at 72 Hz, which Halo held before.
| 10 | The image isn't sharp | Eye images at the render size, anisotropic filtering, and `vr.resolution_scale` 1.25 by default (2160 square, about the panels; settings version 3 moves an untouched 1.5 to it), which should also give open levels the frame time for 90 Hz. **Not yet worn** |
| Wish | An immersive main menu around the ring | Not started. The menu scene has no player window, so it needs its own stereo path; the world-locked menu panel above is the piece it was missing |
## Wear feedback, 2026-10-05 (round 2, on 69b76356)
"Definitely better than before." The play log
(`lepton-steamlaunch-3667167125.log`) shows `xrEndFrame failed: XR_ERROR_POSE_INVALID` on
564 frames.
| # | Report | Status |
| --- | --- | --- |
| 1 | Can't strafe on foot (VR controls and room-scale off didn't help) | A regression from the driving work: the drive throttle zeroed the stick's sideways whenever `vr.vehicle_steering` was "drive", on foot too. Now only for a driver. **Not yet worn** |
| 2 | The Warthog's windshield too reflective to see through | Glass reflects `vr.glass_reflection` (0.3) of the game's own in the headset (`rasterizer_xbox_transparent_geometry.c`, all glass shaders). **Not yet worn** |
| 3 | No laser on the main or pause menu | The beam's pose wasn't a unit quaternion (`look_rotation` from an unnormalised direction), so SteamVR refused every frame that drew it. `look_rotation` normalises, and the host normalises every layer pose. The beam ends 2 cm short of the panel and the dot lies 1 cm off it (a quad through another hides in it). **Not yet worn** |
| 4 | Green debug text over cutscenes as a map loads | The level scripts' `print` (the beta's debugging, `hs_print`): in the headset it goes to debug.txt only, unless `game.console_log` is "all". **Not yet worn** |
| 5 | Silent Cartographer's opening cutscenes drop frames | Not yet measured |
| 6 | Wish: anti-aliasing | Not started (MSAA on the eye targets would cost frame time that 90 Hz doesn't have yet) |
**120 Hz standard** (cross-port, 2026-10-05: 120 Hz at about 2160² an eye, falling back to 90
then 72). Halo hasn't been measured against it. Silent Cartographer ran at about 46 fps on
90 Hz at 1.5× before the 1.25× default.
+19
View File
@@ -214,6 +214,25 @@ static void local_pose(const struct halo_xr_pose *pose, XrPosef *out)
rotate_y(xr.recentre_yaw, pose->position, position);
quaternion_multiply(yaw, pose->orientation, orientation);
/* a unit quaternion whatever the guest sent: one that is not makes
xrEndFrame refuse the whole frame (XR_ERROR_POSE_INVALID) */
{
float length = sqrtf(orientation[0] * orientation[0] + orientation[1] * orientation[1] +
orientation[2] * orientation[2] + orientation[3] * orientation[3]);
if (length > 1e-6f)
{
orientation[0] /= length;
orientation[1] /= length;
orientation[2] /= length;
orientation[3] /= length;
}
else
{
orientation[0] = orientation[1] = orientation[2] = 0.0f;
orientation[3] = 1.0f;
}
}
out->position.x = position[0] + xr.recentre_position[0];
out->position.y = position[1] + xr.recentre_position[1];
out->position.z = position[2] + xr.recentre_position[2];
+23 -2
View File
@@ -153,7 +153,8 @@ enum
_vr_vehicle_turret,
};
/* how a driver is steered this frame (vr.vehicle_steering and the vehicle):
/* how a driver is steered this frame (vr.vehicle_steering and the vehicle;
the setting itself for anyone but a driver, so callers check the seat):
drive and wheel apply to the Warthog and the Ghost; a flier (the Banshee)
flies where the hand points; the Scorpion keeps the game's own driving, its
cannon aimed by the head */
@@ -219,8 +220,12 @@ void vr_render_drive_throttle(
float steer, pedals;
int wheel;
if (local_player_index != local_player_get_next(NONE) || !vr_active() || cinematic_in_progress())
/* (only a driver: on foot the stick's sideways is the player's strafe) */
if (local_player_index != local_player_get_next(NONE) || !vr_active() || cinematic_in_progress() ||
!vr_render.seat.seated || !vr_render.seat.driver)
{
return;
}
steering = vr_driver_steering();
if (steering != _vr_steering_drive && steering != _vr_steering_wheel)
return;
@@ -868,6 +873,22 @@ void vr_render_room_scale(
*position = clipped;
}
real vr_render_glass_reflection_scale(
void)
{
static real scale = -1.0f;
static int generation = -1;
if (!vr_active())
return 1.0f;
if (scale < 0.0f || generation != vr_settings_generation())
{
scale = (real)config_real("vr.glass_reflection");
generation = vr_settings_generation();
}
return scale;
}
int vr_render_motion_sensor_yaw(
short local_player_index,
real *yaw)
+3
View File
@@ -120,6 +120,9 @@ void vr_render_room_scale(long biped_index, union real_point3d *position, real h
"drive" or "wheel" (no strafing; with the hands on the wheel, the triggers) */
union real_vector2d;
void vr_render_drive_throttle(short local_player_index, union real_vector2d *throttle);
/* glass's reflection in the headset, a scale (vr.glass_reflection; 1 off
the headset) */
real vr_render_glass_reflection_scale(void);
/* motion_sensor_update: the yaw (radians) the first local player's motion
sensor turns with, the head's, not the hand's aim; 0 to leave the
facing's */
+4
View File
@@ -272,6 +272,9 @@ static const struct config_setting config_settings[] =
"older file up to date (1: vr.hud_distance and vr.hud_width default to 0,\n"
"the HUD mode's own; 2: vr.turn_mode and vr.snap_angle replace\n"
"vr.snap_turn). Leave it be." },
{ "vr.glass_reflection", _config_real, "0.3", "HALO_VR_GLASS_REFLECTION", _environment_value, _platform_vr,
"How much glass reflects in the headset, 0 to 1 of the game's own: the\n"
"Warthog's windshield at the game's own is too bright to see through." },
{ "vr.texture_anisotropy", _config_integer, "8", "HALO_VR_TEXTURE_ANISOTROPY", _environment_value, _platform_vr,
"Anisotropic filtering for the game's textures in the headset, 1 (off)\n"
"to 16: keeps floors and walls sharp seen at a slant." },
@@ -915,6 +918,7 @@ static const struct config_range
{
{ "vr.resolution_scale", 0.75, 1.5, 0 }, /* sfvr */
{ "vr.texture_anisotropy", 1.0, 16.0, 0 },
{ "vr.glass_reflection", 0.0, 1.0, 0 },
{ "vr.screen_distance", 0.8, 5.0, 0 },
{ "vr.screen_width", 0.5, 6.0, 0 },
{ "vr.world_scale", 0.1, 1.0, 0 },
+15 -1
View File
@@ -1591,6 +1591,18 @@ static void look_rotation(const float forward[3], const float up[3], float out[4
{
float z[3] = { -forward[0], -forward[1], -forward[2] }, x[3], y[3], length, trace;
/* (any length of `forward`: a basis from an unnormalised one makes a
quaternion that is not a unit, and the runtime refuses the whole frame
for one such layer pose with XR_ERROR_POSE_INVALID) */
length = sqrtf(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
if (length < 1e-6f)
{
out[0] = out[1] = out[2] = 0.0f;
out[3] = 1.0f;
return;
}
z[0] /= length; z[1] /= length; z[2] /= length;
x[0] = up[1] * z[2] - up[2] * z[1];
x[1] = up[2] * z[0] - up[0] * z[2];
x[2] = up[0] * z[1] - up[1] * z[0];
@@ -2542,8 +2554,10 @@ static void place_pointer(struct halo_xr_layers *layers)
layers->flags |= HALO_XR_LAYER_POINTER;
if (!(vr.frame.hand_valid[vr.weapon_hand] & 2))
return;
/* (the beam ends 2 cm short of the panel, the dot 1 cm off it: a quad
through another is depth-composited by SteamVR, and hides in it) */
for (axis = 0; axis < 3; axis++)
along[axis] = layers->pointer_pose.position[axis] - aim->position[axis];
along[axis] = vr.pointer_hit[axis] + out[axis] * 0.02f - aim->position[axis];
length = sqrtf(along[0] * along[0] + along[1] * along[1] + along[2] * along[2]);
if (length < 0.05f)
return;
+10
View File
@@ -170,6 +170,16 @@ void hs_print(
* text, from a scenario script or typed at this build's console, reads arguments
* that were never passed. A corrected build should print it through "%s".
* Source-policy approval pending (2026-09-27 audit). */
#ifdef HALO_VR
/* port: in the headset a level script's prints (the beta's debugging,
green over cutscenes as a level loads) go to debug.txt only, unless
game.console_log is "all" */
if (!terminal_shows(_terminal_message_chatter))
{
error(_error_log, "%s", message);
return;
}
#endif
terminal_printf(global_real_argb_green, message);
return;
@@ -166,6 +166,9 @@ symbols in this file:
#include "rasterizer/xbox/rasterizer_xbox_plasma_energy.h"
#include "rasterizer/xbox/rasterizer_xbox_water.h"
#include "rasterizer/xbox/shader_transparent_chicago_preprocessor.h"
#ifdef HALO_VR
#include "halo_vr.h"
#endif
/* ---------- constants */
@@ -3020,10 +3023,32 @@ void rasterizer_transparent_geometry_group_draw(
}
pixel_shader.rgb_inputs[2] = 0x0C0C0D0D;
pixel_shader.rgb_outputs[2] = 0xD;
#ifdef HALO_VR
/* port: in the headset glass reflects less
(vr.glass_reflection): the Warthog's windshield
was too bright to see through */
{
real scale = vr_render_glass_reflection_scale();
real_argb_color perpendicular = glass->reflection_view_perpendicular_color;
real_argb_color parallel = glass->reflection_view_parallel_color;
perpendicular.alpha *= scale;
perpendicular.red *= scale;
perpendicular.green *= scale;
perpendicular.blue *= scale;
parallel.alpha *= scale;
parallel.red *= scale;
parallel.green *= scale;
parallel.blue *= scale;
pixel_shader.constant_0[3] = real_argb_color_to_pixel32(&perpendicular);
pixel_shader.constant_1[3] = real_argb_color_to_pixel32(&parallel);
}
#else
pixel_shader.constant_0[3] = real_argb_color_to_pixel32(
&glass->reflection_view_perpendicular_color);
pixel_shader.constant_1[3] = real_argb_color_to_pixel32(
&glass->reflection_view_parallel_color);
#endif
pixel_shader.alpha_outputs[3] = 0xC00;
pixel_shader.rgb_outputs[3] = 0xC00;
pixel_shader.rgb_outputs[4] = 0xC00;