In-VR menu: replace chunky prop-knob panel with Godot Control UI in a SubViewport

WorldPanel is now the project's one SubViewport screen: MainMenu (tabs Player /
World / System; Return to spawn, Recalibrate body, Reset props, Ping, click
volume slider, scrollable log, FPS, Close) rendered by MenuScreen into a
960x640 SubViewport on a thin 0.36x0.24 m panel.

Input = touch-first hybrid cursor injected as mouse events: both controller
rays are projected analytically onto the panel plane, converted to viewport
pixels and pushed via Viewport.PushInput (MouseMotion/MouseButton, wheel from
thumbstick). Stylus tip within 6 cm = touch cursor (press crossing 0.6 cm from
the front, release past 1.8 cm); farther = laser cursor with trigger edge. One
owning hand per viewport. Haptic tick + synthesized click on press.

Kept: ax_button summon/dismiss, XROrigin3D parenting, grip drag (direct/laser)
with head re-orient, IsBlocking/IsPanelCollider. Locomotion's beam now takes
its length from WorldPanel.TryGetLaserDistance (no beam while touching) and
starts at the stylus tip. Recalibrate calls Recalibrate() on the first node in
group "calibration" (disabled when absent).

New headless regression scenes/menu_spike.tscn ("MENU: overall PASS").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
FoxyzandClaude Opus 5.5 committed 2026-10-02 05:49:13 +03:00
1 parent 3a990c6dbe
commit 661fe1089a
11 files changed
+1281 -316

No files matched your search

+12 -5
View File
@@ -7,6 +7,7 @@ public partial class Locomotion : Node
private const float RayLength = 50f;
private const float LaserMinDistance = 0.35f;
private const float MarkerNearDistance = 1.2f;
private const float LaserStart = 0.12f;
private XROrigin3D _origin;
private XRCamera3D _camera;
@@ -78,7 +79,7 @@ public partial class Locomotion : Node
Node3D pivot = new() { Visible = false };
pivot.AddChild(beam);
Basis rot = Basis.FromEuler(new Vector3(-Mathf.Pi / 2f, 0, 0));
pivot.Transform = new Transform3D(rot, new Vector3(0, 0, -0.12f));
pivot.Transform = new Transform3D(rot, new Vector3(0, 0, -LaserStart));
controller.AddChild(pivot);
return pivot;
}
@@ -127,13 +128,19 @@ public partial class Locomotion : Node
bool show = false;
float dist = 0f;
if (hit.TryGetValue("collider", out Variant collider) && collider.As<Node>() is Node node)
if (WorldPanel.TryGetLaserDistance(controller, out float panelDist))
{
// The menu decides touch vs laser itself (tip near the surface = touch, no beam).
dist = panelDist;
show = dist > LaserMinDistance;
}
else if (hit.TryGetValue("collider", out Variant collider) && collider.As<Node>() is Node node)
{
bool interactive;
if (node.GetParent() is TeleportMarker marker)
interactive = !marker.IsNear;
else
interactive = WorldPanel.IsPanelCollider(node);
interactive = false; // panel hit but the menu isn't in laser mode for this hand
if (interactive && hit.TryGetValue("position", out Variant posVariant))
{
dist = from.DistanceTo(posVariant.As<Vector3>());
@@ -142,8 +149,8 @@ public partial class Locomotion : Node
}
laser.Visible = show;
if (show)
laser.Scale = new Vector3(1, dist, 1);
if (show) // beam starts at the stylus tip (LaserStart ahead of the controller)
laser.Scale = new Vector3(1, Mathf.Max(0.01f, dist - LaserStart), 1);
}
private void CheckTeleport(XRController3D controller, ref bool wasPressed)