desktop-avatar: TigerFaceViewer — head-tracked billboard + A/B probe (plugin #236) - #9
Merged
Merged
Conversation
…(plugin #236) The plugin's Face Viewer (Billboard) sample shipped in v2.8.3 reading the viewer head from Camera.GetStereoViewMatrix(Left/Right), which returns the same mono matrix for both eyes under the display provider — so it never tracked a head (DisplayXR/displayxr-unity#236, fixed by displayxr-unity#237). Nothing caught it because no sample exercised head-coupled effects. desktop-avatar is the sample that can: it runs on a real tracking display with a transparent overlay, so a head-coupled turn is visible at arm's length. - Press F to billboard the tiger at the tracked viewer, via the new DisplayXRProvider.TryGetViewerHead(). Turn is a yaw delta from the tiger's authored rotation, derived from the angle between (tiger -> rig camera) and (tiger -> head), so it assumes nothing about which local axis is the face. `gain` exaggerates it — real head travel in front of a desktop panel subtends a small angle. Press F again to restore the rotation. - The once-per-second log prints BOTH sources side by side — the provider head plus the GetStereoViewMatrix readback with an `eyesEqual` flag — so the fix and the original defect are captured in one line, with or without hardware eyes on the tiger. - Self-installs via RuntimeInitializeOnLoadMethod (the KooimaProbe pattern): no scene edit, no .unity diff, off by default. - CLAUDE.md documents the three correct viewer-position APIs and their spaces, plus verification step 9. Compile-checked against Unity 6000.4.0f1 + Unity.InputSystem. BLOCKED: needs a plugin release carrying displayxr-unity#237 and a repin of this sample before it compiles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
v2.9.1 adds DisplayXRProvider.TryGetViewerEyes / TryGetViewerHead — the world-space tracked-viewer position TigerFaceViewer needs — and fixes the Face Viewer sample that read the head from Camera.GetStereoViewMatrix, which returns the same mono matrix for both eyes in provider mode (DisplayXR/displayxr-unity#236, PR #237). packages-lock.json refreshed alongside the manifest — the lock caches the resolved commit SHA, so bumping the pin alone would silently keep serving v2.9.0. Lock now resolves to 416bf4e8 (= upm/v2.9.1, the signed re-injected commit). Verified against the published tarball before pinning: package.json 2.9.1, the new API present in Runtime/Provider/DisplayXRProvider.cs, FaceViewer's only remaining GetStereoViewMatrix mention is the warning comment, and displayxr_unity.dll reports Authenticode Status=Valid. The other three samples stay on v2.9.0 — this release changes nothing they use, and an all-four repin implies re-verifying all four on hardware. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
dfattal
marked this pull request as ready for review
July 30, 2026 13:50
…plugin #236)
Hardware-verified: the tiger now turns to face the tracked viewer at startup;
F turns it OFF and restores the authored rotation.
Yaw math is ported 1:1 from the viewer-confirmed native reference in
displayxr-demo-avatar (windows/main.cpp, "Face-the-viewer billboard"):
targetYaw = FACE_YAW_SIGN * atan2(hx, |hz|) FACE_YAW_SIGN = -1
with (hx, hz) the head centroid in RAW DISPLAY SPACE — metres, panel-centre
origin, viewer at +Z — from displayxr_get_eye_positions. Live values: hz ~0.5 m,
hx +/-0.12 m, yaw +/-14 deg. Smoothing is the reference's time-based exponential
(tau = 0.04 s) and the heading only chases while eye tracking is locked.
Physical space, not world space, is the point. Two world-space attempts failed on
hardware and both are recorded in CLAUDE.md so the next author skips them:
- FindAnyObjectByType<DragRotateCube>() silently rotated the INVISIBLE fallback
cube — TransparentAutoSetup wires a DragRotateCube onto every target root, the
tiger AND Cube. Target selection now prefers the SkinnedMeshRenderer.
- A (camera - tiger) yaw reference is degenerate here: the tiger's root sits at
the camera's x/z, so the component refused to start.
Neither was diagnosable from the old log line, so it now carries the applied yaw
and the target's name/renderer alongside every viewer-position source.
Also corrects the #236 story: Camera.GetStereoViewMatrix does NOT return a mono
matrix for both eyes in a built player here — the probe logs eyesEqual=False with
values matching the provider exactly. The failure is configuration-specific, not
universal, and the docs no longer claim otherwise.
Not ported: the reference's zone-canvas rebase (offsets hx by the window's centre
on the panel). Without it the heading references the panel centre rather than the
window centre — a constant bias when off-centre, not a tracking failure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Unblocked — plugin v2.9.1 carries the fix (displayxr-unity#237) and this branch repins desktop-avatar to
#upm/v2.9.1.Why
The plugin's Face Viewer (Billboard) sample shipped in v2.8.3 reading the viewer head from
Camera.GetStereoViewMatrix(Left/Right), which returns the same mono matrix for both eyes under the display provider — so it never tracked a head (displayxr-unity#236). Nothing caught it because no sample exercised a head-coupled effect. desktop-avatar is the one that can: real tracking display, transparent overlay, a turn is visible at arm's length.What
DisplayXRProvider.TryGetViewerHead()(new in v2.9.1). The turn is a yaw delta from the tiger's authored rotation, derived from the angle between (tiger → rig camera) and (tiger → head), so it assumes nothing about which local axis is the tiger's face.gain(default 2.5) exaggerates it — real head travel in front of a desktop panel subtends a small angle. F again restores the rotation.GetStereoViewMatrixreadback with aneyesEqualflag — so the fix and the original defect land in one line.[RuntimeInitializeOnLoadMethod](theKooimaProbepattern): no scene edit, no.unitydiff, off by default.CLAUDE.md: the three correct viewer-position APIs with their coordinate spaces, plus verification step 9.manifest.jsonandpackages-lock.json(hash→416bf4e8=upm/v2.9.1) — bumping the manifest alone would silently keep serving v2.9.0.Verification
TigerFaceViewer.cscompile-checked against Unity 6000.4.0f1 +Unity.InputSystem.package.json2.9.1, new API present,displayxr_unity.dllAuthenticodeValid.provider ok=Truewith L/R ~0.06 m apart and a trackinghead=x, next toeyesEqual=Trueon the broken half.The other three samples stay on v2.9.0 — this release changes nothing they use.
🤖 Generated with Claude Code