Cursor sinks into the surface after the player turns
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 90/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- three.js, typescript
- Domain
- computer-graphics, frontend
Research direction
Start in packages/xr-input/src/pointer/cursor-visual.ts, focusing on updateFromIntersection and its fallback branch. Reproduce the cursor behavior in a Meta Quest 3 WebXR session with snap turning, then verify that the cursor remains visible on object surfaces after rotation and that both intersection paths behave consistently.
Written by the indexing model from the issue text.
Description
Cursor sinks into the surface after the player turns
Environment
@iwsdk/xr-input0.5.3 (also reachable through@iwsdk/core0.5.3)- Meta Quest 3, browser-based WebXR session
- Locomotion enabled with turning (snap turn on the thumbstick)
Symptom
The ray cursor (the white dot drawn at the intersection point) disappears once the
player has turned.
- Enter XR and point at any
RayInteractable/DistanceGrabbableobject: the
cursor is drawn correctly on the surface. - Turn with the thumbstick, then point at the same object from the same distance:
the cursor is gone. It reappears only when the ray grazes an edge of the object,
which suggests it is being drawn behind the surface and depth-tested away.
Not reproducible before the first turn. In a desktop (non-immersive) session where
nothing rotates the player, it never happens.
Cause
packages/xr-input/src/pointer/cursor-visual.ts, in updateFromIntersection
(dist lines 78-86, and the same shape again in the fallback branch at 87-95):
// Build world-space orientation from +Z to world normal
this.cursor.quaternion.setFromUnitVectors(ZAxis, scratchNormal);
// Convert world orientation to xrOrigin local space
quaternionHelper.copy(this.xrOrigin.quaternion).invert();
this.cursor.quaternion.multiply(quaternionHelper);
// Offset slightly along the oriented normal to avoid z-fighting
offsetHelper.set(0, 0, this.zOffset);
offsetHelper.applyQuaternion(this.cursor.quaternion);
cursorPosition.add(offsetHelper);
Two problems, both invisible while xrOrigin.quaternion is identity:
-
The world-to-local conversion multiplies in the wrong order. Expressing a
world rotation in a parent frame isparent⁻¹ * world, so this needs
premultiply(quaternionHelper).multiply(quaternionHelper)computes
world * parent⁻¹. -
The offset is applied with a local-space quaternion but added to a
world-space position.cursorPositionstill holds
intersection.pointOnFace(world) at that point;xrOrigin.worldToLocalis
only called afterwards. So the offset has to be built from the world normal.
With the player rotated, the offset therefore points somewhere other than out of
the surface, and for most orientations it pushes the cursor behind the geometry.
zOffset is only 0.004, so any error of more than a few degrees is enough.
Suggested fix
// Offset along the world-space normal, while the position is still world-space
offsetHelper.copy(scratchNormal).multiplyScalar(this.zOffset);
cursorPosition.add(offsetHelper);
// Then express the orientation in xrOrigin local space
this.cursor.quaternion.setFromUnitVectors(ZAxis, scratchNormal);
quaternionHelper.copy(this.xrOrigin.quaternion).invert();
this.cursor.quaternion.premultiply(quaternionHelper);
The fallback branch needs the same treatment: derive the offset direction from
intersection.pointerQuaternion (world) before converting the orientation, and
use premultiply for the conversion.
- Dominant language
- TypeScript
- Stars
- 357
- Forks
- 71
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from facebook/immersive-web-sdk
-
Difficulty 3/5 1-2 days Newbie friendliness 35/100
facebook/immersive-web-sdk#51 · 1 comment ·
-
Difficulty 3/5 1-2 days Newbie friendliness 52/100
-
Difficulty 3/5 1-2 days Newbie friendliness 58/100
-
Difficulty 3/5 1-2 days Newbie friendliness 58/100
facebook/immersive-web-sdk#44 · 1 comment ·
-
Difficulty 5/5 Over a week Newbie friendliness 30/100
All issues in facebook/immersive-web-sdk
Similar issues
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 95/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/studio#4908 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100