vr_startMap waits for the client to sit idle before loading, because a map
started from autoexec.cfg runs during Com_Init and the game's own startup tears
it straight back down again. It counted only CA_DISCONNECTED as idle.
MOHAA's main menu is drawn over a static pic, and the enum says what that means:
CA_CINEMATIC // playing a cinematic or a static pic, not connected to a server
So the client is CA_CINEMATIC at the menu and never CA_DISCONNECTED. idleSince
was cleared every frame the menu was up, the four second wait could never
accumulate, and the map simply never loaded. Eight seconds sitting at the menu
was plenty; the timer just kept resetting. Confirmed by the state it now prints:
9, which is CA_CINEMATIC.
The reason this took a round trip to find is worth more than the fix. The
vr_testStart branch three lines below prints either way, and says why:
// Printed either way. A hook that silently does nothing is indistinguishable
// from one that never ran, and telling those apart has cost enough already.
vr_startMap printed only on success, so its silence could not be told from "ran
with an empty cvar". It now prints unconditionally and names the state that let
it through, so the next time this misbehaves the log says which.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three things, all found by running it.
The mission briefing was drawn onto the player's face. VR_UseScreenLayer only
asked whether clc.state was CA_ACTIVE, so a fullscreen menu that comes up while
the player is still in the world skipped the quad layer and was drawn as screen
space 2D into both eye buffers - at the near plane, a hand's width away, with no
disparity to fuse and each eye warping it through its own asymmetric frustum.
That is exactly what the panel exists to prevent; nothing in-game was reaching
for it. It now checks UI_FullscreenMenuUp as well.
The log said so before the headset did: "view3d 0" with the eye passes still
running means the 3D scene is not being drawn at all, which only happens when a
fullscreen menu is covering it.
map in autoexec.cfg never had a chance. That file is exec'd during Com_Init and
the game's own startup - the intro stages, then the menu - runs afterwards and
tears down anything loaded there. vr_startMap now waits until the client is
sitting idle at the menu and loads it then. vr_testStart also prints
unconditionally: a hook that silently does nothing is indistinguishable from one
that never ran, and telling those apart has cost enough already.
vr_captureEye is a plain archived cvar rather than a VR_TuningCvar. Those force
their default at every start, which is right for a value still being argued with
and wrong for a switch - it made the config's own setting impossible to honour
and cost a device run to notice. The handoff warns about this exact trap.
vr_traceFrame is off in the shipped config: three lines a second rolls anything
that prints once out of logcat within a minute.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The view model is authored to sit at the camera with the arms reaching forward,
so anchoring its origin at the controller leaves that built-in offset in place
and the gun lands near the player's elbow. Rotation was already correct; only
the position was wrong, which is exactly what this fixes and exactly why no
amount of getting the hand pose right removes the need for it.
Same mechanism as the reference's vr_weapon_adjustment_<id> (rtcw
cg_weapons.c, VrInputWeaponAlign.c), as one tunable cvar:
vr_weaponAdjust "scale,right,up,forward,pitch,yaw,roll"
Two details from theirs that are easy to get wrong and are kept here. The
rotation correction is composed in the weapon's own frame rather than added to
the angles, which is only right for one axis at a time. And the offset is
applied along the corrected axes, after the rotation - that ordering is why a
model can rotate correctly and still sit in the wrong place. It is a cvar so it
can be dialled in by pushing a config and relaunching, with no rebuild in the
loop.
CL_RunStartupCommand runs vr_testStart once, a second and a half after the
player is in the world. Setting up a weapon test otherwise means playing through
to wherever the game hands one over on every single build; there is no console
in a headset to type "give" into, and autoexec runs before there is a map, let
alone a player to give anything to. The delay is because CA_ACTIVE arrives
before the player has finished spawning and a give aimed at nothing is dropped.
It re-arms on every map load rather than firing only once per run.
The reference keeps an adjustment per weapon. This is a single global one until
the numbers for each are known - which is what the harness is for.
Not yet run: the headset dropped its connection before this could be installed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Runs on a Meta Quest 3 in stereo at the headset's native 1680x1760 per
eye, with the world tracking head movement in six degrees of freedom.
The session binds to the EGL context SDL already created rather than a
private one, so there is a single GL context and the renderer needs no
thread or context juggling. Each eye is rendered into an OpenXR swapchain
image by substituting the framebuffer the renderer treats as the screen,
which leaves the whole render path untouched. Exactly two eye passes per
frame; there is deliberately no third pass to produce a flat image.
The session and its swapchains belong to the GL context they were made
with, so they are torn down and rebuilt around the renderer restarts the
engine performs.
Menus, the loading screen and cinematics go on a quad layer fixed in the
room, placed once in front of wherever the player is facing. Drawing
screen space content into both eyes instead gives it no stereo disparity
while each eye still warps it through its own asymmetric frustum, and the
result cannot be fused. The panel is drawn into one persistent buffer
that is then copied into the swapchain image: a swapchain rotates through
several images, and the engine's 2D path expects the screen to still hold
what it drew last frame.
Head tracking:
- The per-eye asymmetric frustum from xrLocateViews feeds
R_SetupProjection directly. A symmetric approximation misaligns the two
images by a few degrees, which the eyes cannot fuse.
- The pose is applied in RE_RenderScene, so it follows every camera the
game has - the player's, cutscenes, ladders, death animations - without
each needing to know about VR.
- Orientation is carried as axes rather than pitch/yaw/roll, and composed
with the game's heading as a rotation. Adding Euler angles only works
while pitch and roll are near zero and turns every head movement into a
twist once they are not.
- Position is measured from where the head was when tracking started, so
the game keeps supplying eye height and the headset contributes only
leaning, ducking and stepping. The initial heading is subtracted too,
or the play space's arbitrary forward is added to the character's and
the player spawns facing the wrong way.
The controllers point at the panel and click: the aim ray is intersected
with the quad and the hit becomes the engine's cursor position, so the
menus behave as they always did.
The renderer's own framebuffer objects stay off on Android. Each eye is
already a swapchain image, so routing through tr.renderFbo first would
cost a second full resolution pass and resolve per eye for nothing.
Known gaps: head orientation does not yet reach the player's view angles,
so the game does not know where the player is looking; and there is no
controller model or pointer ray.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Use `allow` rather than `filter`, as it checks for allowed commands
rather than prohibited commands.
- Allow userinfo variables to be created by the server.
Userinfo variables are now removed when disconnecting. Some servers use
userinfo variables for custom settings or to store temporary
information, which makes them irrelevant on other servers and uses space.
- Avoids conflicts with third-party DLLs (like SDL, OpenAL) which have no suffix.
- Easier to build an installer and document.
- More clarity as the architecture is already indicated in the archive name.
- Simplifies launchers & automation (no need to guess the architecture).
- The architecture is still visible when typing `version` in the console or when the executable starts
Rework the version string.
- Make the original game version appear first in the `version` variable.
- Use `com_target_shortversion` for the short version number.
- Use `com_target_version` for the original game version.
Some software, such as HLSW, as well as certain mods, check if the version string begins with "Medal of Honor ....". This commit makes these changes to ensure most software properly handles the game version.
In windowed mode, the mouse is no longer grabbed when one of the UI/console catcher is active. When a UI menu is active, the system's hardware cursor is shown, using the game's custom cursor texture, instead of simulating and rendering the cursor in-game.
* Add exports to load a raw image from the renderer
* Set the hardware cursor to the game's custom mouse cursor texture
* When mouse is not grabbed, the mouse gets updated with absolute mouse position, relative to the window position
This removes coupling between the renderer and UI/client functions. An option USE_RENDERER_DLOPEN was added to specify whether a renderer module should be compiled and loaded, instead of integrating the renderer into the executable directly. This opens the door for a new renderer
Large lightmap can be disabled for very low-end hardware. A BSP file with a smaller lightmap (_sml.bsp) will be used instead of the standard one if the map supports it
Profiling can be enabled with `cl_netprofile` and `sv_netprofile`, the overlay can be shown using `cl_netprofileoverlay` and `sv_netprofileoverlay` and the server can dump network profile using the `netprofiledump` command
The non-dedicated server version of MOH: Breakthrough (standalone executable) rejects clients that have a version of 2.30 or below, or a version of 2.50 or above (0.1 difference), which prevents playing LAN sessions of Breakthrough. To solve this issue, the client now sends a version value that is within the allowed version range for the target game