use winit::{ event::*, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; mod camera; mod commands; mod handle; mod mesh; mod model; mod pool; mod procgen; mod renderer; mod scene; mod shader; mod texture; use camera::*; use model::*; use renderer::Renderer; use scene::*; use texture::*; trait WorldState { fn update(&mut self); fn render(&self) -> Vec; } struct Grid { meshes: Vec, } impl Grid { fn new(ren: &mut Renderer) -> Self { let model = GltfModel::load(ren); let mut meshes = Vec::new(); for x in -5..5 { for y in -5..5 { let translation = glam::Vec3::new(x as f32, 0.0, y as f32) * 3.0; let transform = glam::Mat4::from_translation(translation); model.draw(&mut meshes, transform); } } Self { meshes } } } impl WorldState for Grid { fn update(&mut self) {} fn render(&self) -> Vec { self.meshes.clone() } } struct Metaballs { mesh: MeshInstance, } impl Metaballs { fn new(mut ren: &mut Renderer) -> Self { use procgen::marching_cubes::*; let metaballs = vec![ glam::Vec3A::new(-5., -5., 2.), glam::Vec3A::new(8., 0.0, -1.), glam::Vec3A::new(1., 5., -3.), ]; let field = |x: i32, y: i32, z: i32| { let c = glam::Vec3A::new(x as f32, y as f32, z as f32); let mut sum = 0.0; for ball in metaballs.iter() { sum += 1.0 / ball.distance(c); } sum - 0.4 }; let r = 20; let domain = MarchDomain { min: Vec3::new(-r, -r, -r), max: Vec3::new(r, r, r), }; let vertices = marching_cubes(field, &domain); let vertices: Vec = vertices .iter() .map(|v| mesh::Vertex { position: v.position.into(), normal: v.normal.into(), tex_coords: [0.0; 2], }) .collect(); let indices = (0..(vertices.len() as u32)).collect(); let mesh_data = mesh::MeshData { vertices, indices }; let zeroed_data = pool::TextureData { width: 8, height: 8, data: vec![0x00; 256], }; let albedo_raw = include_bytes!("assets/brick_moss_001_diff_1k.jpg"); let albedo_data = load_texture_data(albedo_raw); let metalrough_raw = include_bytes!("assets/brick_moss_001_metalrough_1k.jpg"); let metalrough_data = load_texture_data(metalrough_raw); let mesh = ren.load_mesh(&mesh_data); let albedo = ren.load_texture(&albedo_data); let metalrough = ren.load_texture(&metalrough_data); let material = ren.load_material(&pool::MaterialData { albedo, metallic_roughness: metalrough }); let mesh = scene::MeshInstance { mesh, material, transform: glam::Mat4::IDENTITY, }; Self { mesh } } } impl WorldState for Metaballs { fn update(&mut self) {} fn render(&self) -> Vec { vec![self.mesh] } } struct Planet { speed: f32, offset: f32, radius: f32, size: f32, } struct Planets { start: std::time::Instant, planets: Vec, model: GltfModel, } impl Planets { fn new(ren: &mut Renderer) -> Self { let start = std::time::Instant::now(); let model = GltfModel::load(ren); let mut planets = Vec::new(); for i in 0..10 { let i = i as f32; planets.push(Planet { speed: 1.618 * 1.5 / i, offset: 0.0, radius: i * 2.0, size: 0.5, }); } Self { start, planets, model, } } } impl WorldState for Planets { fn update(&mut self) {} fn render(&self) -> Vec { let elapsed = self.start.elapsed().as_secs_f32(); let mut meshes = Vec::new(); for planet in self.planets.iter() { let translation = glam::Vec3::new(0.0, 0.0, planet.radius); let translation = glam::Mat4::from_translation(translation); let theta = planet.speed * elapsed + planet.offset; let rotation = glam::Mat4::from_rotation_y(theta); self.model.draw(&mut meshes, rotation * translation); } meshes } } async fn make_window_renderer(window: &winit::window::Window) -> Renderer { let size = window.inner_size(); let instance = wgpu::Instance::new(wgpu::Backends::all()); let surface = unsafe { instance.create_surface(window) }; let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::LowPower, compatible_surface: Some(&surface), force_fallback_adapter: false, }) .await .unwrap(); let (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { features: wgpu::Features::empty(), limits: wgpu::Limits::default(), label: None, }, None, ) .await .unwrap(); let config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface.get_preferred_format(&adapter).unwrap(), width: size.width, height: size.height, present_mode: wgpu::PresentMode::Fifo, }; surface.configure(&device, &config); Renderer::new(size, surface, device, queue, config) } fn main() { let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); let mut camera = Flycam::new(10.0, 0.002); let mut is_grabbed = false; let mut ren = pollster::block_on(make_window_renderer(&window)); // let mut state: Box = Box::new(Planets::new(&mut ren)); // let mut state: Box = Box::new(Grid::new(&mut ren)); let mut state: Box = Box::new(Metaballs::new(&mut ren)); let lights = vec![ PointLight { center: glam::Vec3A::new(0.0, 5.0, 0.0), intensity: glam::Vec3A::new(100.0, 100.0, 100.0), }, PointLight { center: glam::Vec3A::new(-7.0, 5.0, 7.0), intensity: glam::Vec3A::new(100.0, 0.0, 0.0), }, PointLight { center: glam::Vec3A::new(7.0, 5.0, 7.0), intensity: glam::Vec3A::new(0.0, 100.0, 0.0), }, PointLight { center: glam::Vec3A::new(0.0, 5.0, -7.0), intensity: glam::Vec3A::new(0.0, 0.0, 100.0), }, PointLight { center: glam::Vec3A::new(0.0, 50.0, 0.0), intensity: glam::Vec3A::new(1000.0, 1000.0, 1000.0), }, ]; event_loop.run(move |event, _, control_flow| match event { Event::RedrawRequested(_) => { let scene = Scene { meshes: &state.render(), point_lights: &lights, }; match ren.render(&camera, &scene) { Ok(_) => {} Err(wgpu::SurfaceError::Lost) => ren.resize(ren.size), Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, Err(wgpu::SurfaceError::Timeout) => {} Err(e) => println!("error: {:?}", e), }; } Event::MainEventsCleared => { camera.update(); state.update(); window.request_redraw(); } Event::DeviceEvent { ref event, .. } => match event { DeviceEvent::MouseMotion { delta } => { if is_grabbed { camera.process_mouse(delta.0, delta.1); } } _ => {} }, Event::WindowEvent { ref event, window_id, } if window_id == window.id() => match event { WindowEvent::KeyboardInput { input: KeyboardInput { virtual_keycode: Some(key), state, .. }, .. } => { if *state == ElementState::Pressed && *key == VirtualKeyCode::Escape { if is_grabbed { window.set_cursor_grab(false).unwrap(); window.set_cursor_visible(true); is_grabbed = false; } } else { camera.process_keyboard(*key, *state); } } WindowEvent::MouseInput { button: MouseButton::Left, state: ElementState::Pressed, .. } => { if !is_grabbed { window.set_cursor_grab(true).unwrap(); window.set_cursor_visible(false); is_grabbed = true; } } WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::Resized(physical_size) => { ren.resize(*physical_size); camera.resize(physical_size.width, physical_size.height); } WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { ren.resize(**new_inner_size); camera.resize(new_inner_size.width, new_inner_size.height); } _ => {} }, _ => {} }); }