HUD¶
A HUD is presentation drawn over the live scene. It is not a dock panel and it is not a document. HUDs answer questions about the running world: which body is in view, how fast the vessel is moving, whether a download is holding the simulation, or which lesson step comes next.
LunCoSim has three HUD families:
| Family | Visibility owner | Examples |
|---|---|---|
| User-controlled | A persisted setting or a Twin setting | Time HUD, surface-view switcher, performance HUD, input HUD, camera/status HUD |
| Automatic | Scene, possession, or lifecycle state | Rover HUD, lander control cards, terrain and download progress, guided and notification cards |
| Twin-authored | A runtime surface manifest plus its exposure | Mission-status cards, vehicle controls, program browsers, custom Twin HUDs |
Settings → HUD is the discoverable view over the user-controlled family. It does not become a second visibility registry: each row writes the same resource, Twin setting, or typed command that the owning feature already reads.
User-controlled HUDs¶
| Surface | Position | Default | Owner and switch |
|---|---|---|---|
| Time HUD | Top-left | Off | OverlaySettings.sky_clock; also in the Time menu |
| Surface view switcher | Top-centre | Off | OverlaySettings.view_switcher; also in the Camera menu |
| Performance HUD | Status bar | Off | PerfHudSettings.enabled; TogglePerfHud |
| Input HUD | Bottom-centre | Off | InputOverlaySettings.enabled; ToggleInputOverlay |
| Camera/status HUD | Top-right | On | Twin setting ui.camera_status, authored default true |
| Graphs HUDs | Measured view area | Per instance | GraphHuds; + Add in Settings → HUD |
| Camera views | Measured view area | Per instance | CameraFeeds; + Add camera view, limit eight |
The Time HUD shows the shared celestial epoch. The surface-view switcher is both a control and a readout: it selects Surface, Moon, or Earth and shows which body the camera is focused on. Both are opt-in; a fresh install shows the terrain and nothing on top of it.
The performance HUD occupies the status bar and reports rendering FPS, frame time, physics step time, and Modelica worker time per physics step. Its samples come from the shared engine-health publication, so the graph remains live before a Twin is active. When space is tight the metric text shortens and scales rather than wrapping.
The input HUD visualizes live keyboard and mouse input. It is intended for recording, teaching, and input debugging, not for normal operation.
The camera/status surface shows the active camera identity. It is a runtime
surface with a Twin-scoped boolean gate, so a Twin can hide it with
"ui.camera_status" = false without changing the user's global settings.
Graphs HUDs are independent native windows, each with its own grid, parameter selection, visibility, and measured geometry. A card can plot up to four measured channels; each quantity keeps its own unit and Y scale while the time axes stay linked. The title-bar × removes the instance; the Settings row only toggles visibility, so a hidden card keeps its selection. The picker closes on Done or an outside click. Scene teardown releases every HUD-owned grid and plot, including while hidden.
Camera views are independent render feeds. + Add camera view creates the first view without needing an existing camera window; the view's own toolbar then selects a live camera. The limit is eight views.
Automatic HUDs¶
These surfaces are listed in Settings → HUD under Automatic HUDs with their owner, but they are not user-toggled. Their visibility is a consequence of state:
| Surface | Owner |
|---|---|
| Rover HUD | Driven-vessel possession and capability state |
| Lander control cards | Authored USD control-HUD metadata on the active scene |
| Terrain and scenario-download progress | Transient terrain and network lifecycle state |
| Guided, notification, and blackout HUDs | Authored guidance and runtime session state |
The rover HUD appears only while a vessel is possessed, because it answers a question the user just asked by taking control. The lander cards are authored on the scene, not generated from a model class. Download and terrain progress are transient: dismissing the preparation window keeps the required-data hold, and the window reopens when a new runtime requirement appears.
Tutorial presentation is a separate guided layer. Its lesson HUD is a specific-HUD readout; its ring, coach card, and completion prompt use the guided overlay order. Menus and window controls remain in front of both.
Runtime surfaces¶
The shipped application manifest registers these Twin-facing surfaces:
| Surface | Placement | Gate |
|---|---|---|
vessel-hud |
Full viewport | View perspective |
vehicle-control-left |
Bottom-left window | ui.vehicle_controls, default on |
vehicle-control-right |
Bottom-right window | ui.vehicle_controls, default on |
camera-status |
Top-right window | View perspective; ui.camera_status, default on |
celestial-view |
Top-centre window | View perspective; view_switcher gate |
program-browser |
Top-left window | Always registered; content decides visibility |
lunica-schema |
Top-centre window | Modelica schema presentation |
terrain-progress |
Top-centre window | Terrain lifecycle |
scenario-download |
Top-centre window | Download lifecycle |
asset-consent |
Full viewport | Required-data consent; non-modal |
A Twin can replace any of these by stable ID or add its own surfaces. Closing the Twin restores the application surfaces. The full manifest contract is in Runtime UI.
The celestial-view surface also carries the lunar map and the mission-site
card. The map is gated by the Twin setting ui.lunar_map; omission means hidden.
The map marker is projected from the canonical surface pose, not from a render
transform. The mission-site card appears only while the Moon orbital pin is
active and the scene has a valid lunar site anchor.
Stacking and input¶
HUD layers are ordered. From front to back: menus and popups, floating windows and dialogs, the general HUD, the specific HUD, docked panels, then authored runtime HUI. A HUD area belongs inside the viewport rectangle. Docked panels are not above a HUD that overlaps them.
Pointer ownership follows the same explicitness:
- A full-window runtime surface is transparent to scene clicks. Only visible
controls that declare an HUI
on_presscallback enter the scene-pick gate. - A
windowsurface can be dragged only by a descendant taggedtag:ui_drag_handle="true". Dragging a button never moves the panel. - A
modalsurface must be interactive and use viewport placement. While visible, its root claims the full-window scene-pick gate. Escape emits its declareddismiss_action; Rhai owns what dismissal means. - A non-modal data window such as asset consent consumes clicks only inside its own panel. The rest of the viewport stays interactive while its work continues.
Hiding a surface destroys its retained tree. When the exposure returns, the runtime creates a fresh root from the manifest, so template-owned build state cannot survive a hidden transition.
Guided HUD cards¶
Rhai scripts can publish hint, objective, and action cards through
lunco-workbench-guided-ui. This is separate from the runtime surface manifest:
the cards are script-owned presentation, not HTML templates.
hud_perspectives(ids) sets a card-specific allowlist. The default is View
(sandbox_view); an empty list allows every perspective. Unknown perspective
ids reject before state changes. Hiding a card preserves its state; visible cards
stack by stable owner identity and measured height.
An implicit card belongs to the executing script's stable identity. Unscripted
API or REPL calls use a distinct command-context card. An explicit hud name
creates a scene-owned card that several producers can share:
hint("mission", "Align with the landing site.");
objectives_hud("mission", [
#{ id: "align", text: "Align with the site", done: false },
]);
guided_hud_actions("mission", "rover", "on_guided_action", [
#{ id: "take_control", label: "Take control" },
]);
Action sets carry a ToolHookBinding lease. Replacing or clearing the actions
despawns the previous lease, so a queued click cannot bind to a new same-name
card or a different Twin.
Layout persistence¶
A draggable window surface stores only a validated logical top-left override in
the active Twin's workspace state. The manifest remains the default geometry and
visibility authority. Overrides are pruned when the manifest no longer authors
their surface ID, clamped after a resize or DPI change, and cleared when the Twin
closes. A primary-button double click on the drag handle restores the authored
anchor.
Settings → HUD → Surface view switcher position → Reset position removes only
the celestial-view override. It does not reset other draggable surfaces or the
shared layout store.
Common tasks¶
To reduce clutter while presenting:
- Open Settings → HUD.
- Turn off the performance HUD, input HUD, and any graph or camera view you do not need.
- Leave automatic HUDs alone; they disappear with their scene state.
- If a Twin hides the camera/status surface, check
ui.camera_statusin the Twin settings rather than looking for a global toggle.
To diagnose a missing HUD:
| Symptom | Check |
|---|---|
| Surface absent | Namespace, visible, perspective filter, gate, and placement rectangle |
| Surface visible but empty | Declared template properties and binding source names |
| Button ignores clicks | Callback spelling in the template and manifest, then the Rhai action handler |
| Surface moved and will not return | Double-click its drag handle, or reset its position in Settings → HUD |
| Text renders as boxes | The stylesheet must import lunco://ui/runtime_fonts.css |
| Surface remains after its state ended | Check the exposure's visible value and the surface lifecycle; hidden surfaces are destroyed |
For the full authoring contract, see Runtime UI. For the walkthrough, see Compose a HUD.