Component that visualizes VR world input. On each update it gets the hovered UI panel from WorldInput, computes a 3D hit point from a world-space ray against the panel or its root, then draws a cyan line and a sphere at the hit point.
using Sandbox;
public sealed class VrWorldInput : Component
{
[RequireComponent] private WorldInput WorldInput { get; set; }
private const float HitSphereRadius = 0.5f;
protected override void OnUpdate()
{
var hovered = WorldInput.Hovered;
if ( !hovered.IsValid() )
return;
var origin = WorldPosition;
var ray = new Ray( origin, WorldRotation.Forward );
if ( !TryGetHitPoint( hovered, ray, out var hitPoint ) )
return;
Gizmo.Draw.Color = Color.Cyan;
Gizmo.Draw.Line( origin, hitPoint );
Gizmo.Draw.SolidSphere( hitPoint, HitSphereRadius );
}
/// <summary>
/// Nested hovered panels have no 3D transform; hit-test the root, then fall back to the panel plane.
/// </summary>
private static bool TryGetHitPoint( Sandbox.UI.Panel panel, Ray ray, out Vector3 hitPoint )
{
hitPoint = default;
var root = panel.FindRootPanel();
if ( root.IsValid() && root.RayToLocalPosition( ray, out _, out var distance ) && distance > 0f )
{
hitPoint = ray.Position + ray.Forward * distance;
return true;
}
var go = panel.GameObject;
if ( !go.IsValid() )
return false;
// LookAtCamera panels face the viewer; use a plane through the panel toward the ray origin.
var planePos = go.WorldPosition;
var planeNormal = (ray.Position - planePos).Normal;
var denom = Vector3.Dot( ray.Forward, planeNormal );
if ( denom.AlmostEqual( 0f ) )
return false;
var t = Vector3.Dot( planePos - ray.Position, planeNormal ) / denom;
if ( t < 0f )
return false;
hitPoint = ray.Position + ray.Forward * t;
return true;
}
}