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;
}
}
}