Skip to content

Inactive button zone meshes (visible=false) block raycasting to other Interactable entities #36

@rbaileyquintar

Description

@rbaileyquintar

Summary

When an entity has invisible child meshes (mesh.visible = false) that are not Interactable, those meshes still intercept rays fired by InputSystem's BVH raycaster. If those invisible meshes protrude closer to the viewer than a separate nearby Interactable entity's mesh, the nearby entity never receives Hovered.

Root Cause

Three.js Raycaster.intersectObject() does not check mesh.visible — invisible meshes are still tested. IWSDK's computeBoundsTreeForEntity uses object3D.traverse() (also no visibility check) to build the BVH for all descendant meshes of an Interactable entity.

A concrete example: a panel entity (entity A, Interactable) has inactive game-screen button zones as children (mesh.visible = false, zoneD = 0.10, front face at localZ = 0.11). A separate menu button entity (entity B, Interactable) is also a child of the panel, with its zone mesh at localZ = 0.08 (front face, zoneD = 0.04). When the ray is cast:

  1. Entity A's BVH is traversed recursively — hits the invisible child zone mesh at localZ = 0.11 (closer to the camera than entity B's mesh at localZ = 0.08)
  2. Entity A receives Hovered via that invisible mesh hit; pointer event propagation stops
  3. Entity B never receives Hovered, even though its interactive zone mesh is unobstructed

Steps to Reproduce

  1. Create a panel entity with Interactable + DistanceGrabbable
  2. Attach several button zone meshes as children — some Interactable (menu buttons), some not (game-screen buttons, mesh.visible = false)
  3. The inactive zones use a deeper BoxGeometry (zoneD = 0.10) than the active menu zones (zoneD = 0.04), so their front face protrudes further toward the viewer
  4. Point a controller at one of the menu button zones that overlaps in Y with an inactive zone
  5. The menu button entity does not receive Hovered; the panel entity does instead

Expected Behaviour

mesh.visible = false should be sufficient to exclude a mesh from raycasting consideration, OR the BVH traversal in computeBoundsTreeForEntity / updateDescendantArrays should skip invisible meshes.

Actual Behaviour

Invisible meshes are tested by the raycaster and can block Hovered from being delivered to the correct Interactable entity.

Workaround

Park inactive zone meshes behind the panel (localZ = -0.5) rather than in front of it. This ensures they are always at a greater ray distance than active zones, so they never win the closest-hit test. Update localZ to the correct active value when enabling zones, and back to -0.5 when disabling.

// On deactivate — park behind panel so raycaster can't intercept
(entity.getVectorView(Transform, 'position') as Float32Array)[2] = -0.5;
entity.object3D!.visible = false;

// On activate — move in front of panel face
(entity.getVectorView(Transform, 'position') as Float32Array)[2] = 0.06;
entity.object3D!.visible = true;

Environment

  • IWSDK @iwsdk/core (latest)
  • Three.js BVH raycasting via three-mesh-bvh
  • Emulated via IWER (Meta Quest 3 emulation)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions