use cyborg::camera::{Camera, Flycam}; use cyborg::pass::{mesh, RenderPassBox}; use cyborg::shader; use cyborg::storage::mesh::*; use cyborg::viewport::*; use cyborg::Renderer; use legion::*; use rand::prelude::*; use std::sync::Arc; use winit::dpi::PhysicalSize; use winit::{ event::*, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; struct DeltaTime { pub dt: f32, last_update: std::time::Instant, } impl Default for DeltaTime { fn default() -> Self { DeltaTime { dt: 0.0, last_update: std::time::Instant::now(), } } } struct SpawnDistributions { theta: rand::distributions::Uniform, radius: rand::distributions::Uniform, up: rand::distributions::Uniform, } impl Default for SpawnDistributions { fn default() -> Self { Self { theta: rand::distributions::Uniform::new(0.0, std::f32::consts::TAU), radius: rand::distributions::Uniform::new(3.0, 4.0), up: rand::distributions::Uniform::new(8.0, 12.0), } } } struct Particle { position: glam::Vec3A, mass: f32, } struct Velocity { linear: glam::Vec3A, } #[system] fn update_dt(#[resource] dt: &mut DeltaTime) { dt.dt = dt.last_update.elapsed().as_secs_f32(); dt.last_update = std::time::Instant::now(); } #[system] fn update_app(#[resource] app: &mut Application) { app.update(); } #[system(for_each)] fn apply_gravity(#[resource] dt: &DeltaTime, velocity: &mut Velocity) { const GRAVITY: f32 = 6.0; velocity.linear.y -= GRAVITY * dt.dt; } #[system(for_each)] fn move_particles(#[resource] dt: &DeltaTime, particle: &mut Particle, velocity: &Velocity) { particle.position += velocity.linear * (dt.dt / particle.mass); // TODO angular velocity } #[system(for_each)] fn respawn_particles( #[resource] distributions: &SpawnDistributions, particle: &mut Particle, velocity: &mut Velocity, ) { if particle.position.y < -10.0 { particle.position = glam::Vec3A::ZERO; let rng = &mut rand::thread_rng(); velocity.linear.y = distributions.up.sample(rng); let theta = distributions.theta.sample(rng); let radius = distributions.radius.sample(rng); velocity.linear.x = theta.sin() * radius; velocity.linear.z = theta.cos() * radius; } } #[system(for_each)] fn update_transforms(transform: &mut cyborg::scene::Transform, particle: &Particle) { transform.transform = glam::Mat4::from_translation(particle.position.into()); } #[system(for_each)] fn update_debug(draw_list: &mut cyborg::scene::DebugDrawList, velocity: &Velocity) { draw_list.clear(); draw_list.indices.extend_from_slice(&[0, 1]); let color = [1.0, 1.0, 1.0]; draw_list.vertices.push(cyborg::scene::DebugVertex { position: [0.0, 0.0, 0.0], color, }); draw_list.vertices.push(cyborg::scene::DebugVertex { position: velocity.linear.to_array(), color, }); } fn build_update_schedule() -> Schedule { let mut builder = Schedule::builder(); builder.add_system(update_dt_system()); builder.add_system(update_app_system()); builder.add_system(apply_gravity_system()); builder.add_system(move_particles_system()); builder.add_system(respawn_particles_system()); builder.add_system(update_transforms_system()); builder.add_system(update_debug_system()); builder.build() } fn make_terrain(attributes: &mesh::Attributes) -> MeshBuffer { const MOUNTAINS_RADIUS: f32 = 25.0; const MOUNTAINS_WIDTH: f32 = 10.0; const MOUNTAINS_HEIGHT: f32 = 5.0; const MOUNTAINS_FREQUENCY: f32 = 1.0; use noise::NoiseFn; let noise = noise::OpenSimplex::new(); let sample = |xy: glam::Vec2| -> f32 { let mountains_dist = (xy.length() - MOUNTAINS_RADIUS).abs() / MOUNTAINS_WIDTH; let mountains_scale = 1.0 - mountains_dist; if mountains_scale > 0.0 { let noise_input = (xy * MOUNTAINS_FREQUENCY).as_dvec2(); let sampled = noise.get(noise_input.to_array()) as f32; ((sampled + 0.5) / 0.5) * MOUNTAINS_HEIGHT * mountains_scale } else { 0.0 } }; const GRID_FREQUENCY: f32 = 0.2; const GRID_RADIUS: isize = ((MOUNTAINS_RADIUS + MOUNTAINS_WIDTH) / GRID_FREQUENCY) as isize; const GRID_WIDTH: isize = GRID_RADIUS * 2 + 1; let tile_num = (GRID_RADIUS * GRID_RADIUS) as usize; let mut vertices = Vec::with_capacity(tile_num); let mut indices = Vec::with_capacity(tile_num * 6); for x in -GRID_RADIUS..=GRID_RADIUS { for z in -GRID_RADIUS..=GRID_RADIUS { let x = x as f32 * GRID_FREQUENCY; let z = z as f32 * GRID_FREQUENCY; let y = sample(glam::Vec2::new(x, z)); vertices.push(mesh::Vertex { position: [x, y, z], tan_frame: 0, }); } } let mut cursor: mesh::Index = 0; for _x in -GRID_RADIUS..GRID_RADIUS { for _z in -GRID_RADIUS..GRID_RADIUS { let next_line = cursor + GRID_WIDTH as mesh::Index; indices.extend_from_slice(&[ cursor, next_line, cursor + 1, next_line, cursor + 1, next_line + 1, ]); cursor += 1; } cursor += 1; } let vertices = AttrBuffer { id: attributes.vertex, count: vertices.len(), data: bytemuck::cast_slice(&vertices).to_vec(), }; let indices = AttrBuffer { id: attributes.index, count: indices.len(), data: bytemuck::cast_slice(&indices).to_vec(), }; let mut mesh = MeshBuffer::default(); mesh.attributes.push(vertices); mesh.attributes.push(indices); mesh } struct Application { window: winit::window::Window, viewport: WinitViewport, flycam: Flycam, is_grabbed: bool, } impl Application { pub fn update(&mut self) { self.flycam.update(); self.window.request_redraw(); } pub fn on_mouse_motion(&mut self, x: f64, y: f64) { if self.is_grabbed { self.flycam.process_mouse(x, y); } } pub fn on_mouse_event(&mut self, button: MouseButton, state: ElementState) { if button == MouseButton::Left && state == ElementState::Pressed && !self.is_grabbed { self.window.set_cursor_grab(true).unwrap(); self.window.set_cursor_visible(false); self.is_grabbed = true; } } pub fn on_key_event(&mut self, key: VirtualKeyCode, state: ElementState) { if key == VirtualKeyCode::Escape && state == ElementState::Pressed { if self.is_grabbed { self.window.set_cursor_grab(false).unwrap(); self.window.set_cursor_visible(true); self.is_grabbed = false; } } else { self.flycam.process_keyboard(key, state); } } pub fn on_resize(&mut self, new_size: PhysicalSize) { self.viewport.resize(new_size); self.flycam.resize(new_size.width, new_size.height); } pub fn on_lost_focus(&mut self) { self.flycam.defocus(); } pub fn on_surface_lost(&mut self) { self.viewport.resize(self.viewport.size); } } impl cyborg::legion::RenderCallbacks for Application { fn get_viewports(&mut self) -> Vec<(&dyn Viewport, Camera)> { vec![(&self.viewport as &dyn Viewport, self.flycam.get_camera())] } fn present(&mut self) { self.viewport.present(); } } fn main() { let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); let viewport = pollster::block_on(WinitViewport::from_window(&window)); let mut world = World::new(Default::default()); let mut resources = Resources::default(); resources.insert::(Default::default()); resources.insert::(Default::default()); let mut update_schedule = build_update_schedule(); let renderer = Renderer::new(viewport.device.clone(), viewport.queue.clone()); resources.insert(renderer); resources.insert(viewport.get_info().to_owned()); let flycam = cyborg::camera::Flycam::new(0.002, 10.0, 0.25); let shader_store = Arc::new(shader::ShaderStore::new(viewport.device.clone())); let shaders_dir = std::env::current_dir().unwrap(); let shaders_dir = shaders_dir.join("shaders/"); let shader_watcher = shader::ShaderWatcher::new(shader_store.to_owned(), shaders_dir).unwrap(); let mesh_forward = shader_watcher.add_file("mesh_forward.wgsl").unwrap(); let mesh_skinning = shader_watcher.add_file("mesh_skinning.wgsl").unwrap(); let mesh_shaders = mesh::ShaderInfo { store: shader_store.clone(), forward: mesh_forward, skinning: mesh_skinning, }; resources.insert(mesh_shaders); let application = Application { window, viewport, flycam, is_grabbed: false, }; let mut render_schedule = Schedule::builder(); cyborg::legion::build_renderer(application, &mut resources, &mut render_schedule); let mut render_schedule = render_schedule.build(); let example_vertices = vec![ mesh::Vertex { position: [-0.5, 0.0, 0.5], tan_frame: 0, }, mesh::Vertex { position: [0.5, 0.0, 0.5], tan_frame: 0, }, mesh::Vertex { position: [0.0, -0.5, -0.5], tan_frame: 0, }, mesh::Vertex { position: [0.0, 0.5, -0.5], tan_frame: 0, }, ]; let mesh_pass = resources.get::>().unwrap(); let attributes = mesh_pass.get_attributes(); let example_vertices = AttrBuffer { id: attributes.vertex, count: example_vertices.len(), data: bytemuck::cast_slice(&example_vertices).to_vec(), }; let example_indices: Vec = vec![0, 1, 2, 1, 2, 3, 2, 3, 0, 0, 1, 3]; let example_indices = AttrBuffer { id: attributes.index, count: example_indices.len(), data: bytemuck::cast_slice(&example_indices).to_vec(), }; let mut example_mesh = MeshBuffer::default(); example_mesh.attributes.push(example_vertices); example_mesh.attributes.push(example_indices); let example_mesh = mesh_pass.get_mesh_pool().load(example_mesh).unwrap(); world.push(( cyborg::scene::Mesh { mesh: mesh_pass .get_mesh_pool() .load(make_terrain(attributes)) .unwrap(), }, cyborg::scene::Transform { transform: glam::Mat4::from_translation(glam::Vec3::new(0.0, -10.0, 0.0)), }, )); drop(mesh_pass); let r = 6; for x in -r..r { for y in -r..r { for z in -r..r { let translation = glam::Vec3::new(x as f32, y as f32, z as f32); let transform = glam::Mat4::from_translation(translation); world.push(( cyborg::scene::Mesh { mesh: example_mesh.clone(), }, cyborg::scene::Transform { transform }, cyborg::scene::DebugDrawList::default(), Particle { position: translation.into(), mass: 1.0, }, Velocity { linear: glam::Vec3A::ZERO, }, )); } } } event_loop.run(move |event, _, control_flow| match event { Event::RedrawRequested(_) => { let mut application = resources.get_mut::().unwrap(); match application.viewport.acquire() { Err(wgpu::SurfaceError::Lost) => application.on_surface_lost(), Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, Err(e) => eprintln!("error: {:?}", e), Ok(_) => { drop(application); render_schedule.execute(&mut world, &mut resources); } } } Event::MainEventsCleared => { update_schedule.execute(&mut world, &mut resources); shader_watcher.watch(); } Event::DeviceEvent { ref event, .. } => match event { DeviceEvent::MouseMotion { delta } => { let mut application = resources.get_mut::().unwrap(); application.on_mouse_motion(delta.0, delta.1); } _ => {} }, Event::WindowEvent { ref event, window_id, } => { let mut application = resources.get_mut::().unwrap(); if window_id == application.window.id() { match event { WindowEvent::KeyboardInput { input: KeyboardInput { virtual_keycode: Some(key), state, .. }, .. } => { application.on_key_event(*key, *state); } WindowEvent::MouseInput { button, state, .. } => { application.on_mouse_event(*button, *state); } WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::Resized(physical_size) => { application.on_resize(*physical_size); } WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { application.on_resize(**new_inner_size); } WindowEvent::Focused(false) => { application.on_lost_focus(); } _ => {} } } } _ => {} }); }