Skip to content

desktop-avatar: TigerFaceViewer — head-tracked billboard + A/B probe (plugin #236) - #9

Merged
dfattal merged 3 commits into
mainfrom
feat/tiger-face-viewer-236
Jul 30, 2026
Merged

dfattal merged 3 commits into
mainfrom
feat/tiger-face-viewer-236

Conversation

@dfattal

@dfattal dfattal commented Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

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

  • F toggles head-tracked billboarding of the tiger via 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.
  • The once-per-second log prints both sources side by side — provider head/eyes/IPD and the GetStereoViewMatrix readback with an eyesEqual flag — so the fix and the original defect land in one line.
  • Self-installs via [RuntimeInitializeOnLoadMethod] (the KooimaProbe pattern): no scene edit, no .unity diff, off by default.
  • CLAUDE.md: the three correct viewer-position APIs with their coordinate spaces, plus verification step 9.
  • Repin: manifest.json and packages-lock.json (hash → 416bf4e8 = upm/v2.9.1) — bumping the manifest alone would silently keep serving v2.9.0.

Verification

  • TigerFaceViewer.cs compile-checked against Unity 6000.4.0f1 + Unity.InputSystem.
  • Published v2.9.1 tarball checked before pinning: package.json 2.9.1, new API present, displayxr_unity.dll Authenticode Valid.
  • Hardware run still pending — press F, move head left/right: expect the tiger to follow, and the log to show provider ok=True with L/R ~0.06 m apart and a tracking head= x, next to eyesEqual=True on the broken half.

The other three samples stay on v2.9.0 — this release changes nothing they use.

🤖 Generated with Claude Code

dfattal and others added 2 commits July 30, 2026 19:11
…(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
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>
@dfattal
dfattal merged commit 4ec7e94 into main Jul 30, 2026
1 check passed
@dfattal
dfattal deleted the feat/tiger-face-viewer-236 branch July 30, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant