Removes r_flatColor, r_traceSurf, r_forceGenericStage, r_noCull, r_noDepth,
r_noFog, r_invertCull, vr_debugPanel and its magenta panel clear, along with
R_TraceSurface, R_DriverGet, R_FlatColorPush/Pop and the GetFloatv/IsEnabled
entries added to qgl.h for them. Around 570 lines.
Everything they were built to find is fixed, and the fixes are all still here:
the cull face forced past gl4es's cache, RB_SurfaceFace's index type, the window
resize vid_restart, the frustum built from the runtime's tangents, the terrain
cone sized to the headset, and the VR layer's state calls routed through gl4es.
Verified rendering on the device after the strip, with no overrides at all.
vr_captureEye stays, defaulted off. It writes the eye buffer to a TGA and it is
the one probe that consistently earned its keep - a picture settled in one look
what rounds of description could not. Inert unless switched on.
misc/android/autoexec.cfg is down to that single line. Nothing in it is load
bearing any more.
Also records a third instance of the session's recurring mistake in the gl4es
doc: r_noDepth was declared, registered and tested for several device runs while
the edit meant to read it had silently failed to apply. Every result attributed
to it was a no-op, and it sat in the "known good" configuration taking credit for
what disabling culling was actually doing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two bugs, both found on the device, both with the evidence recorded in
VR_RENDERER_HANDOFF.md sections 8 and 9.
The menu was a renderer restart. SDL reports a window resize; the handler
compares it against glconfig.vidWidth/Height, which in VR is the eye buffer and
never the Android surface, so it can never match. It set r_customwidth,
r_customheight and r_mode -1 - cvars the renderer then ignores, because the VR
resolution comes from GetVRRenderResolution - and scheduled a vid_restart that
destroyed and rebuilt the GL context and the OpenXR session under a running
frame loop. The panel drew a full 256/256 samples up to CL_Vid_Restart_f and
nothing ever after. VR_PORT_STATUS called that restart harmless; it was the
whole bug.
RB_SurfaceFace declared its output pointer as unsigned* and assigned
tess.indexes to it, which is glIndex_t* - unsigned short under gl4es. Every
index went in four bytes apart in a two byte array, so each triangle picked up
whichever vertex the zero high halves pointed at. That was the world smeared
into streaks that dragged its textures with it; models never come through that
function, which is why they were always correct. ProjectDlightTexture had the
same fault in hitIndexes. The compiler had been reporting it all along, under
sixty other warnings in that file.
Also: renderergl1 culled against the game's symmetric fov rather than the
runtime's asymmetric per-eye frustum; the VR layer set viewport, cull, depth,
blend, colour mask and scissor state natively while gl4es caches all of them and
drops the engine's next request for a value its stale copy already claims; and
GfxInfo_f reported a different draw route than R_DrawElements took.
The remaining diagnostics are CVAR_TEMP and default off. vr_captureEye is worth
keeping: it writes the eye buffer to a TGA, and one image settled questions that
six rounds of verbal description could not.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two problems the first device run found, one hiding the other.
GLimp_SetMode's fixedFunction branch offers exactly one context: desktop OpenGL
1.1. There is no such thing on Android, so window creation failed three times
over and R_Init gave up with "Couldn't get a visual". renderergl2 never met this
because it takes the other branch, which already knows how to ask for ES.
gl4es is what makes the request answerable, and it wants the opposite of what
the renderer believes it is getting: desktop GL 1.x presented upwards, OpenGL ES
underneath. So the fixed function branch now asks for ES 3.0 and then ES 2.0,
and the renderer is never told - it reads its version through gl4es, which
reports desktop GL, and takes the desktop fixed function path on the strength of
it.
That got the renderer up, and revealed the second problem: the OpenXR session
found no current EGL context. Which is where gl4es having its own opinion about
glGetString stops being convenient. The engine's proof that a context is live is
GL_VERSION coming back non-NULL, and gl4es answers VERSION, VENDOR and RENDERER
out of constants without asking the driver - so a context that was created but
never bound passes that test and is only noticed much later, somewhere else
entirely. GLimp_SetMode now asks SDL directly whether the context it just made
is current, restoring the check gl4es defeated.
The session no longer assumes one is bound either. Being current is per thread,
and a window that has not been shown yet has no surface to be current against,
so VR_CreateSession asks for the binding through GLimp_MakeCurrent rather than
requiring it to already be there, and reports the EGL handles when it cannot get
one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Everything the VR work had built so far lived in renderergl2. This carries it
across to renderergl1, which is the renderer the port is moving to.
Most of it transfers unchanged: the head pose composed onto whatever camera the
game asked for (R_ApplyVRView, in RE_RenderScene so it follows cutscenes and
ladders as well as the player), the asymmetric per-eye frustum and vr_fovZoom in
R_SetupProjection, the eye render resolution asked for through
GetVRRenderResolution rather than r_mode, and the sprite paths that face the
viewer's position rather than the view plane.
Two places came out simpler than rend2. Everything 2D in this renderer goes
through Set2DWindow - RB_SetGL2D is a one line call to it - so the HUD's
convergence has a single place to be applied rather than two that had to be
kept in agreement. And RE_SetDefaultFramebuffer is nearly nothing: renderergl1
predates framebuffer objects, has none of its own and never touches the
binding, so redirecting an eye is one bind with no FBO_Bind to intercept and
nothing to restore.
glBindFramebuffer is resolved on first use rather than declared with the rest
of qgl, because renderergl1 has no extension probe of its own - it goes through
GLimp_GetProcAddress so it reaches gl4es like everything else the renderer
calls, and gl4es stays aware of which framebuffer is bound.
renderergl2 still builds and is unchanged in behaviour.
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>
GLimp_GetProcAddresses() failed on every OpenGL ES context, so the GL2
renderer could not start on any ES target. Two independent causes:
QGL_1_1_PROCS carried five entry points that exist in no version of
OpenGL ES - glCallList, glDrawPixels, glFogi, glLineStipple and
glPixelZoom. That list is shared by the desktop and ES paths, so
requiring them there failed the whole load. Upstream keeps ES-incompatible
functions in QGL_DESKTOP_1_1_PROCS; these are moved there to match. Only
renderergl1 calls them - every use in renderergl2 sits inside an
"#if 0 // FIXME: unimplemented (GL2)" - so nothing loses a function it
actually invokes.
The ES 3.0 context was also only ever requested under __EMSCRIPTEN__.
Everywhere else the highest ES context asked for was 2.0, and since EGL
returns a genuine ES 2.0 context when that is what was requested, ES 3
was never reached even on hardware that supports it. It is now requested
first on all platforms.
Verified on a Meta Quest 3 (Adreno 740): renderergl2 now comes up on an
ES 3.2 context and compiles all 115 GLSL shaders. Desktop builds of both
renderers are unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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
renderercommon is now used, which will appropriately use opengl1 when building static libraries, or compile both opengl1/opengl2 modules when building shared libraries
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