nightshade-api 0.46.0

Procedural high level API for the nightshade game engine
Documentation
//! Exercises the facade capabilities added to close app-coverage gaps: the
//! advanced retained UI widgets, decals rendering in the default graph, a
//! configurable particle emitter, runtime material and texture registration with
//! instanced draws, and navmesh baking with an explicit Recast config. Every
//! call here is a `nightshade_api` function, no engine re-export.

use nightshade_api::nightshade::prelude::nalgebra_glm::Quat;
use nightshade_api::prelude::*;

struct Coverage {
    instances: Entity,
    phase: f32,
}

fn main() {
    run(
        |world| {
            set_title(world, "Facade Coverage");
            set_background(world, Background::Nebula);
            set_bloom(world, true);
            orbit_camera(world, vec3(0.0, 0.5, 0.0), 14.0);
            set_orbit_view(world, vec3(0.0, 0.5, 0.0), 14.0, 0.7, 0.5);
            spawn_floor(world, 30.0);

            build_widgets(world);
            let instances = build_instanced_city(world);
            scatter_decals(world);

            spawn_particle_emitter(
                world,
                ParticleEmitter::firework_explosion(vec3(0.0, 4.0, 0.0), vec3(1.0, 0.6, 0.2), 200),
            );

            bake_navmesh_with(
                world,
                &RecastNavMeshConfig {
                    agent_radius: 0.6,
                    walkable_slope_angle: 50.0,
                    ..Default::default()
                },
            );

            Coverage {
                instances,
                phase: 0.0,
            }
        },
        |world, state| {
            state.phase += delta_time(world);
            let transforms = city_transforms(state.phase);
            set_instances(world, state.instances, transforms);
        },
    )
    .unwrap();
}

fn build_widgets(world: &mut World) {
    let panel = spawn_panel(world, ScreenAnchor::TopLeft, 320.0, 560.0);
    panel_heading(world, panel, "Facade widgets");
    panel_separator(world, panel);

    panel_label(world, panel, "Tree");
    let tree = panel_tree_view(world, panel, false);
    let content = tree_content(world, tree);
    let root = tree_node(world, tree, content, "World", 0, 0);
    set_tree_node_expanded(world, root, true);
    let children = tree_node_children(world, root);
    tree_node(world, tree, children, "Buildings", 1, 1);
    tree_node(world, tree, children, "Lights", 1, 2);

    panel_label(world, panel, "Table");
    let grid = panel_data_grid(world, panel, &[("Name", 120.0), ("Value", 80.0)], 8);
    set_data_grid_rows(world, grid, 3);
    for row in 0..3 {
        set_data_grid_cell(world, grid, row, 0, &format!("item {row}"));
        set_data_grid_cell(world, grid, row, 1, &format!("{}", row * 10));
    }

    panel_label(world, panel, "Inputs");
    panel_color_picker_hsv(world, panel, [0.2, 0.6, 1.0, 1.0]);
    panel_drag_value(world, panel, 0.0, 100.0, 25.0);
    panel_multi_select(world, panel, &["alpha", "beta", "gamma"]);
    panel_date_picker(world, panel, 2026, 6, 15);
    panel_text_area(world, panel, "notes...", 3);
    panel_breadcrumb(world, panel, &["home", "scene", "node"]);
    panel_spinner(world, panel);
}

fn build_instanced_city(world: &mut World) -> Entity {
    let pixels = checker_pixels(8, [60, 70, 110, 255], [200, 210, 255, 255]);
    register_texture(world, "coverage::windows", 8, 8, &pixels);
    register_material(
        world,
        "coverage::glass",
        Material {
            base_color: [0.4, 0.55, 0.8, 1.0],
            metallic: 0.6,
            roughness: 0.25,
            ..Default::default()
        },
    );
    spawn_instanced_with_material(world, Shape::Cube, city_transforms(0.0), "coverage::glass")
}

fn city_transforms(phase: f32) -> Vec<InstanceTransform> {
    let mut transforms = Vec::new();
    for column in 0..6 {
        for row in 0..6 {
            let x = column as f32 * 1.6 - 4.0;
            let z = row as f32 * 1.6 - 4.0;
            let height = 1.5 + ((column + row) as f32 + phase).sin().abs() * 2.5;
            transforms.push(InstanceTransform::new(
                vec3(x, height * 0.5, z),
                Quat::identity(),
                vec3(0.6, height, 0.6),
            ));
        }
    }
    transforms
}

fn scatter_decals(world: &mut World) {
    let pixels = checker_pixels(4, [255, 120, 40, 255], [255, 200, 60, 255]);
    register_texture(world, "coverage::decal", 4, 4, &pixels);
    for index in 0..4 {
        let offset = index as f32 * 2.0 - 3.0;
        spawn_decal(
            world,
            "coverage::decal",
            vec3(offset, 0.02, 5.0),
            vec3(0.0, 1.0, 0.0),
            1.5,
        );
    }
}

fn checker_pixels(size: usize, a: [u8; 4], b: [u8; 4]) -> Vec<u8> {
    let mut pixels = Vec::with_capacity(size * size * 4);
    for row in 0..size {
        for column in 0..size {
            let color = if (row + column) % 2 == 0 { a } else { b };
            pixels.extend_from_slice(&color);
        }
    }
    pixels
}