nightshade-api 0.43.0

Procedural high level API for the nightshade game engine
Documentation
use nightshade_api::prelude::*;
use std::time::Duration;
use web_time::Instant;

const CUBE_COUNT: usize = 500;
const PHASE_FRAMES: u32 = 300;

struct Parity {
    entities: Vec<Entity>,
    positions: Vec<Vec3>,
    label: Entity,
    frame_index: u32,
    api_total: Duration,
    api_frames: u32,
    longhand_total: Duration,
    longhand_frames: u32,
    reported: bool,
}

fn main() {
    run(
        |world| {
            show_grid(world, false);
            orbit_camera(world, vec3(0.0, 0.5, 0.0), 45.0);

            let positions = grid_positions();
            let entities = spawn_objects(
                world,
                Object {
                    color: TEAL,
                    ..Object::default()
                },
                &positions,
            );
            let label = spawn_text(world, "phase 1: api spelling", ScreenAnchor::TopLeft);

            Parity {
                entities,
                positions,
                label,
                frame_index: 0,
                api_total: Duration::ZERO,
                api_frames: 0,
                longhand_total: Duration::ZERO,
                longhand_frames: 0,
                reported: false,
            }
        },
        |world, state| {
            let step = delta_time(world);
            if state.frame_index < PHASE_FRAMES {
                let start = Instant::now();
                for &entity in &state.entities {
                    rotate(world, entity, Vec3::y(), step);
                }
                state.api_total += start.elapsed();
                state.api_frames += 1;
                if state.frame_index + 1 == PHASE_FRAMES {
                    for &entity in &state.entities {
                        despawn(world, entity);
                    }
                    state.entities = longhand::spawn_grid(world, &state.positions, ORANGE);
                    world.resources.mesh_render_state.request_full_rebuild();
                    set_text(world, state.label, "phase 2: longhand spelling");
                }
            } else if state.frame_index < PHASE_FRAMES * 2 {
                let start = Instant::now();
                for &entity in &state.entities {
                    longhand::rotate(world, entity, Vec3::y(), step);
                }
                state.longhand_total += start.elapsed();
                state.longhand_frames += 1;
            } else if !state.reported {
                state.reported = true;
                report("api", state.api_total, state.api_frames);
                report("longhand", state.longhand_total, state.longhand_frames);
            }
            state.frame_index += 1;
        },
    )
    .unwrap();
}

fn report(name: &str, total: Duration, frames: u32) {
    if frames == 0 {
        println!("{name}: no frames measured");
        return;
    }
    let milliseconds = total.as_secs_f64() * 1000.0 / frames as f64;
    println!(
        "{name}: {CUBE_COUNT} cubes rotated in {milliseconds:.4} ms per frame average over {frames} frames"
    );
}

fn grid_positions() -> Vec<Vec3> {
    let mut positions = Vec::with_capacity(CUBE_COUNT);
    for row in 0..20 {
        for column in 0..25 {
            positions.push(vec3(
                column as f32 * 2.0 - 24.0,
                0.5,
                row as f32 * 2.0 - 19.0,
            ));
        }
    }
    positions
}

mod longhand {
    use nightshade_api::nightshade;
    use nightshade_api::nightshade::prelude::*;

    pub fn spawn_grid(world: &mut World, positions: &[Vec3], color: [f32; 4]) -> Vec<Entity> {
        let material_name = nightshade::ecs::material::resources::material_registry_find_or_insert(
            &mut world.resources.assets.material_registry,
            "parity::longhand".to_string(),
            Material {
                base_color: color,
                ..Default::default()
            },
        );
        let mut entities = Vec::with_capacity(positions.len());
        for &position in positions {
            let entity = spawn_mesh_at(world, "Cube", position, Vec3::new(1.0, 1.0, 1.0));
            let previous = world
                .core
                .get_material_ref(entity)
                .map(|material_ref| material_ref.name.clone());
            if let Some(previous_name) = previous
                && let Some((index, _)) = registry_lookup_index(
                    &world.resources.assets.material_registry.registry,
                    &previous_name,
                )
            {
                registry_remove_reference(
                    &mut world.resources.assets.material_registry.registry,
                    index,
                );
            }
            if let Some((index, _)) = registry_lookup_index(
                &world.resources.assets.material_registry.registry,
                &material_name,
            ) {
                registry_add_reference(
                    &mut world.resources.assets.material_registry.registry,
                    index,
                );
            }
            world
                .core
                .set_material_ref(entity, MaterialRef::new(material_name.clone()));
            world.resources.mesh_render_state.mark_entity_added(entity);
            entities.push(entity);
        }
        entities
    }

    pub fn rotate(world: &mut World, entity: Entity, axis: Vec3, radians: f32) {
        if let Some(transform) = mutate_local_transform(world, entity) {
            transform.rotation =
                nalgebra_glm::quat_angle_axis(radians, &axis.normalize()) * transform.rotation;
        }
    }
}