use nightshade_api::prelude::*;
use std::collections::HashSet;
struct Inspector {
shapes: Vec<Entity>,
selected: Option<Entity>,
name_cell: Entity,
id_cell: Entity,
position_cell: Entity,
mesh_cell: Entity,
children_cell: Entity,
bounds_cell: Entity,
hud: Entity,
stack: UndoStack,
}
fn main() {
run(setup, update).unwrap();
}
fn setup(world: &mut World) -> Inspector {
set_window_title(world, "Inspector");
set_background(world, Background::Sky);
show_grid(world, true);
spawn_floor(world, 12.0);
let mut shapes = Vec::new();
let crate_box = spawn_cube(world, vec3(-3.0, 0.6, 0.0));
set_color(world, crate_box, ORANGE);
set_name(world, crate_box, "Crate");
shapes.push(crate_box);
let ball = spawn_sphere(world, vec3(0.0, 0.6, 0.0));
set_color(world, ball, TEAL);
set_name(world, ball, "Ball");
shapes.push(ball);
let pillar = spawn_cylinder(world, vec3(3.0, 0.6, 0.0));
set_color(world, pillar, PURPLE);
set_name(world, pillar, "Pillar");
shapes.push(pillar);
for &shape in &shapes {
set_visible(world, shape, true);
}
let panel = spawn_panel(world, ScreenAnchor::TopRight, 320.0, 280.0);
panel_heading(world, panel, "Selection");
let grid = panel_property_grid(world, panel, 90.0);
let name_cell = property_value(world, grid, "Name");
let id_cell = property_value(world, grid, "Id");
let position_cell = property_value(world, grid, "Position");
let mesh_cell = property_value(world, grid, "Mesh");
let children_cell = property_value(world, grid, "Children");
let bounds_cell = property_value(world, grid, "Bounds");
let tree_panel = spawn_panel(world, ScreenAnchor::BottomRight, 320.0, 220.0);
panel_heading(world, tree_panel, "Scene");
build_tree(world, tree_panel);
spawn_text(
world,
"L click select R click pick-behind F frame V visible N rename Z undo Y redo",
ScreenAnchor::BottomLeft,
);
let hud = spawn_text(world, "No selection", ScreenAnchor::TopLeft);
Inspector {
shapes,
selected: None,
name_cell,
id_cell,
position_cell,
mesh_cell,
children_cell,
bounds_cell,
hud,
stack: UndoStack::new(),
}
}
fn property_value(world: &mut World, grid: Entity, label: &str) -> Entity {
let cell = panel_property_row(world, grid, label);
panel_label(world, cell, "-")
}
fn build_tree(world: &mut World, panel: Entity) {
let tree_view = panel_tree_view(world, panel, false);
let content = tree_content(world, tree_view);
let mut first_root: Option<Entity> = None;
for row in scene_tree(world) {
let node = tree_node(
world,
tree_view,
content,
&row.name,
row.depth as usize,
row.id as u64,
);
if row.depth == 0 && first_root.is_none() {
first_root = Some(node);
}
}
if let Some(node) = first_root {
set_tree_node_expanded(world, node, true);
}
}
fn update(world: &mut World, inspector: &mut Inspector) {
if let Some(entity) = clicked_entity(world)
&& inspector.shapes.contains(&entity)
{
inspector.selected = Some(entity);
}
if mouse_clicked(world, MouseButton::Right) {
let mut exclude = HashSet::new();
if let Some(current) = inspector.selected {
exclude.insert(current);
}
if let Some(behind) = pick_excluding(world, mouse_position(world), &exclude)
&& inspector.shapes.contains(&behind)
{
inspector.selected = Some(behind);
}
}
let Some(selected) = inspector.selected else {
set_text(world, inspector.hud, "No selection");
return;
};
if key_pressed(world, KeyCode::KeyF) {
frame_entities(world, &[selected]);
}
if key_pressed(world, KeyCode::KeyN) {
let renamed = format!("{} (edited)", name(world, selected));
set_name(world, selected, &renamed);
}
if key_pressed(world, KeyCode::KeyV) {
let visible = describe_entity(world, selected)
.and_then(|view| view.visibility)
.unwrap_or(true);
inspector
.stack
.capture(world, selected, SnapshotKind::Visibility);
set_visible(world, selected, !visible);
inspector.stack.commit();
}
if key_pressed(world, KeyCode::KeyZ) {
inspector.stack.undo(world);
}
if key_pressed(world, KeyCode::KeyY) {
inspector.stack.redo(world);
}
refresh_grid(world, inspector, selected);
let visible = describe_entity(world, selected)
.and_then(|view| view.visibility)
.unwrap_or(true);
let root_count = roots(world).len();
set_text(
world,
inspector.hud,
&format!(
"{} | visible {} | roots {} | undo {} redo {}",
name(world, selected),
visible,
root_count,
inspector.stack.can_undo(),
inspector.stack.can_redo(),
),
);
}
fn refresh_grid(world: &mut World, inspector: &Inspector, selected: Entity) {
let Some(view) = describe_entity(world, selected) else {
return;
};
set_panel_text(world, inspector.name_cell, &view.name);
set_panel_text(world, inspector.id_cell, &format!("{}", view.id));
set_panel_text(
world,
inspector.position_cell,
&format!(
"{:.1}, {:.1}, {:.1}",
view.translation[0], view.translation[1], view.translation[2]
),
);
set_panel_text(
world,
inspector.mesh_cell,
view.mesh.as_deref().unwrap_or("none"),
);
set_panel_text(
world,
inspector.children_cell,
&format!("{}", children(world, selected).len()),
);
let radius = bounds(world, selected)
.map(|bound| bound.radius)
.unwrap_or(0.0);
set_panel_text(world, inspector.bounds_cell, &format!("r {radius:.2}"));
}