Repository navigation
Scenix v1.10.0 — Editor And UI Tooling
Scenix 1.10.0 ships the two new optional tooling crates: scenix-editor
(owning editor sessions with exact inverse-command undo/redo, gizmo drag
math, scene-tree rows, the node inspector surface, material field editing,
asset-browser rows, timeline previews, and a versioned project metadata
exchange) and scenix-ui (a renderer-agnostic layer of docked panel stacks
solved by a space allocator, rolling renderer stats with budget policies,
tool widgets with hit testing, and bounded per-frame overlay composition).
Both build on the v1.5.0 interaction backbone instead of duplicating it —
selection stays in SceneGraph, editor metadata stays in
NodeEditorMetadata, and the inspector vocabulary stays in the core
inspector snapshot types — and neither renders a single pixel: host faces
(egui, HTML tools, native windows) draw the solved output.
Highlights
scenix-editor (new, std-only tooling crate)
- Owning
EditorSession: the scene graph plus the standard five-panel
stack (scene tree, inspector, assets, timeline, material), the edit
history, gizmo state, and a session tick in one struct. Panels live in
scenix-ui'sPanelStack; the session layout reuses the same space
solver hosts already call. - Exact history: every mutation is a concrete
EditCommand
(RenameNode,SetVisibility,SetLayer,SetTransform,
ReparentNode) whose inverse is computed from the pre-apply graph, so
undo restores byte-exact state and redo replays the stored forward
command. The history is capped (HISTORY_DEFAULT_CAP= 128) and new
work clears redo, like every editor users know. - Coalesced gizmo drags: continuous
SetTransformupdates coalesce by
kind key + tick window (COALESCE_WINDOW_TICKS= 30) while keeping the
first inverse — one drag is one undo step that restores the pre-drag
pose. Drag amounts re-solve absolutely from the captured start
transform, so repeated inputs replay identically, and rotation gizmos
compose about world axes (delta * current) as on-screen gizmos promise. - Gizmo vocabulary:
GizmoMode/GizmoAxis/GizmoSettings::SNAPPED
with half-unit translates, 15° rotations, and 0.1 scale steps
(apply_axis_delta,snap_translation), plus line-closest-approach
axis_pick(GIZMO_HALF_LENGTH= 1.0,
GIZMO_PICK_THRESHOLD= 0.08) for pointer rays. - Panel models:
ScenePanelRow+scene_rowsflatten the graph
depth-first (capped atSCENE_PANEL_ROW_LIMIT= 256) with depth,
payload tags, and selected/hidden/locked flags;InspectorEditroutes
rename/visible/layer/position/rotation/scale edits and refuses locked
nodes;inspect_selectedbuilds scalar rows over the v1.5
Inspectablesnapshot implementation. - Material editing:
PbrField/PhysicalFieldwrites with editor
clamps (unit channels0..=1, IOR1.0..=2.5),assign_textureover
TextureSlot(Option<TextureId>sets or clears), and a bounded
MaterialEditorLog. Materials are host-owned resources by design, so
material edits are optimistic and log-based rather than history-undone. - Asset browser (
assetsfeature):AssetBrowserModelover loader
AssetLoadStatus— capped atBROWSER_ROW_LIMIT= 256 rows with a
64-char label cap, case-insensitive substring filter, optional status
filter, and per-status counts. - Timeline previews (
timelinefeature):TimelinePreviewdelegates
to animato's ownLoopMode::advance— the same solver runtime actions
run — with rate clamping0.05..=8and 60 FPS snapping for scrubs. - Project metadata:
ProjectMetadata/SceneRouteschema v1 with
validation,upgrade_to_current, andserde_jsonround-trip
persistence under theserdefeature.
scenix-ui (new, no_std-capable crate)
- Layout:
Rect/Edges/Anchor/UiScalewithsafe_rectfor
device insets. - Docked panels:
PanelStack/PanelSpec/PanelEvent/DockKind+
the first-fit tab-orderpanel_placementspace solver
(DOCK_MAX_FRACTION,PANEL_MIN_THICKNESS,
PANEL_MAX_THICKNESS). A panel is skipped, never silently shrunk away,
when remaining space falls under the floor. - Renderer stats:
RendererStatsrolling window (STATS_HISTORY_FRAMES
= 120) withFrameSample::sanitized,BudgetPolicy::{THIRTY_HZ, SIXTY_HZ}, andBudgetWarnings; serializes oldest-first under
serdeso hosts replay identical windows. - Tool widgets:
Buttonwith edge-onlypress,Toggle,Slider
with truncatingsnap(SLIDER_DEFAULT_STEP= 0.01),SmallTextBuffer
(TEXT_BUFFER_CAP= 64),FocusChain(FOCUS_CHAIN_MAX= 8),
ColorSwatch, andwidget_hitpoint testing with exclusive right/bottom
edges. - Bounded overlays:
OverlayFrame::push_rect/push_textstop at
MAX_OVERLAY_RECTS= 256 /MAX_OVERLAY_TEXTS= 64, and
compose_panel_overlayis deterministic — the same panel stack, stats,
and viewport always produce the same draw list, which makes overlay
tests exact instead of approximate. Compiles for
wasm32-unknown-unknownwith--no-default-features.
Facade and integrations
- The facade
uifeature exposes curated re-exports of the whole UI
surface; theeditorfeature drivesscenix-editor(with its
inspector+materialfeatures) and impliesui, because
sessions own the standard panel stack returned bystandard_panels. - Cross-crate surfaces stay feature-gated: asset-browser rows re-export
undereditor,loader, timeline previews undereditor,animato, and the
project JSON exchange undereditor,serde. loaderforwardsscenix-editor/assets,animatoforwards
scenix-editor/timeline, andserdeforwards both tooling crates'
serialization surfaces.
Try It
use scenix::{DockKind, EditorSession, GizmoAxis, GizmoMode, InspectorEdit,
PanelId, PanelSpec, SceneNode, SelectionMode, Vec3, scene_rows,
standard_panels};
let mut session = EditorSession::new("Shell");
let root = session.graph_mut().add(SceneNode::group("Hero"));
*session.panels_mut() = standard_panels();
session.panels_mut().upsert(PanelSpec::docked(
PanelId(5), "Assets".into(), DockKind::Bottom, 240.0,
));
session.select(root, SelectionMode::Replace).unwrap();
session
.apply_inspector_edit(root, InspectorEdit::SetPosition(Vec3::new(2.0, 0.0, 1.0)))
.unwrap();
// One drag, one undo step: the second update re-solves from the start.
session.begin_drag(root, GizmoMode::Translate, GizmoAxis::X).unwrap();
session.drag_gizmo(0.4).unwrap();
session.drag_gizmo(0.9).unwrap();
session.end_drag();
session.undo().unwrap(); // back to the pre-drag transform, exactly
for row in scene_rows(session.graph()) {
println!("{}{}", " ".repeat(row.depth), row.label);
}
println!("layout: {:?}", session.layout((1600.0, 900.0).into()));Run the headless demos without any window or GPU:
cargo run -p scenix --features editor --example editor_shell and
cargo run -p scenix --features ui --example ui_overlay.
Upgrade
Nothing breaks. Add the features you need:
[dependencies]
scenix = { version = "1.10", features = ["editor"] } # implies ui
# or UI tooling only:
scenix = { version = "1.10", features = ["ui"] }The existing editor meaning did not change for scenix-core's inspector
snapshot types — they re-export exactly as in v1.9.x. Code that enabled
editor without wanting panel types can keep compiling: the added
re-exports are additive names.