use std::time::Duration;
use maple::prelude::*;
use maple_egui::prelude::*;
fn main() {
App::new(Config {
resizeable: false,
resolution: Some(Resolution {
width: 1000,
height: 1000,
}),
..Default::default()
})
.add_plugin(Core3D)
.add_plugin(Physics3D)
.add_plugin(EguiPlugin)
.load_scene(MainScene)
.run();
}
pub struct MainScene;
#[derive(Node)]
struct Positions {
#[transform]
pub transform: NodeTransform,
pub wait_until: Duration,
pub position: Vec3,
pub mesh_count: u32,
pub mesh: AssetHandle<Mesh3D>,
pub material: AssetHandle<Material>,
pub hdr: AssetHandle<Texture>,
pub shell: u32,
pub spawn_delay: f32,
pub pending: Vec<(i32, i32, i32)>,
pub spacing: f32,
}
fn ease_out_back(t: f32) -> f32 {
let c1 = 1.70158;
let c3 = c1 + 1.0;
1.0 + c3 * (t - 1.0).powi(3) + c1 * (t - 1.0).powi(2)
}
fn spiral_indices(n: i32) -> Vec<(i32, i32)> {
let mut result = Vec::with_capacity((n * n) as usize);
let (mut top, mut bottom, mut left, mut right) = (0, n - 1, 0, n - 1);
while top <= bottom && left <= right {
for col in left..=right {
result.push((top, col));
}
top += 1;
if top > bottom {
break;
}
for row in top..=bottom {
result.push((row, right));
}
if right == 0 {
break;
}
right -= 1;
if left > right {
break;
}
for col in (left..=right).rev() {
result.push((bottom, col));
}
if bottom == 0 {
break;
}
bottom -= 1;
if top > bottom {
break;
}
for row in (top..=bottom).rev() {
result.push((row, left));
}
left += 1;
}
result
}
impl SceneBuilder for MainScene {
fn build(self, assets: &AssetLibrary) -> Scene {
let scene = Scene::default();
scene
.spawn(
Camera3D::builder()
.position((50.0, 50.0, 50.0))
.looking_at((15.0, 15.0, 15.0))
.far_plane(200.0)
.build(),
)
.on::<EguiUpdate>(|ctx| {
let node = ctx.node_ref().transform.position;
egui::Window::new("fps").show(&ctx, |ui| {
ui.label(format!(
"fps: {}",
ctx.get_resource_mut::<Frame>().avg_fps()
));
ui.label(format!(
"1% low: {}",
ctx.get_resource_mut::<Frame>().low_percent(0.01)
));
ui.label(format!("x: {}, y: {}, z: {}", node.x, node.y, node.z))
});
})
.on::<Update>(Camera3D::free_fly(5.0, 0.5))
.on::<Ready>(|ctx| {
let mut input: ResMut<Window> = ctx.get_resource_mut();
input.set_cursor_locked(true);
});
let hdr = assets.load("res/kloofendal_48d_partly_cloudy_puresky_4k.hdr");
scene.spawn(Environment::new(hdr.clone()).with_ibl_strength(0.2));
scene.spawn(
DirectionalLight::builder()
.direction((-1.0, -1.0, -1.0))
.intensity(4.0)
.build(),
);
let root = scene.spawn(Positions {
transform: NodeTransform::default(),
position: Vec3::ZERO,
wait_until: Duration::ZERO,
mesh_count: 0,
material: assets.add::<Material>(Color::CYAN),
mesh: assets.add::<Mesh3D>(Torus::default()),
hdr,
shell: 0,
spawn_delay: 0.1,
pending: Vec::new(),
spacing: 2.5,
});
root.on::<Update>(|ctx| {
let frame: Res<Frame> = ctx.get_resource();
let mut node = ctx.node_mut();
if ctx.assets().is_loading(&node.hdr) {
node.wait_until = frame.elapsed + Duration::from_secs_f32(1.0);
return;
}
let elapsed = frame.elapsed;
if node.pending.is_empty() {
if node.shell >= 10 {
return;
}
let k = node.shell as i32;
let mut seen = std::collections::HashSet::new();
for (x, y) in spiral_indices(k + 1) {
if seen.insert((x, y, k)) {
node.pending.push((x, y, k));
}
}
for (x, z) in spiral_indices(k + 1) {
if seen.insert((x, k, z)) {
node.pending.push((x, k, z));
}
}
for (y, z) in spiral_indices(k + 1) {
if seen.insert((k, y, z)) {
node.pending.push((k, y, z));
}
}
let shell_time = 0.2;
node.spawn_delay = shell_time / node.pending.len() as f32;
node.shell += 1;
}
while elapsed >= node.wait_until {
let delay = node.spawn_delay;
node.wait_until += Duration::from_secs_f32(delay);
if let Some((x, y, z)) = node.pending.pop() {
let offset = Vec3::new(x as f32, y as f32, z as f32) * node.spacing;
let pos = node.position + offset;
let spawned_at = frame.elapsed;
ctx.node_view()
.spawn_child(
MeshInstance3D::builder()
.mesh(node.mesh.clone())
.material(node.material.clone())
.position(pos)
.scale_factor(0.01),
)
.on::<Update>(move |ctx| {
let frame: Res<Frame> = ctx.get_resource();
let mut node = ctx.node_mut();
let duration = 1.0; let t =
((frame.elapsed - spawned_at).as_secs_f32() / duration).min(1.0);
let scale = ease_out_back(t);
if scale > 0.0 {
node.transform.set_scale(Vec3::new(scale, scale, scale));
}
})
.on::<FixedUpdate>(|ctx| {
let mut node = ctx.node_mut();
let position = node.transform.position;
node.transform.rotate_euler_xyz_degrees(position / 100.0);
});
node.mesh_count += 1;
}
}
})
.on::<EguiUpdate>(|ctx| {
egui::Window::new("meshes").show(&ctx, |ui| {
ui.label(format!("meshes: {}", ctx.node_ref().mesh_count))
});
});
scene
}
}