ironwood.rs (8052B)
1 use winit::{ 2 application::ApplicationHandler, 3 event::{ElementState, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent}, 4 event_loop::{ActiveEventLoop, ControlFlow, EventLoop}, 5 keyboard::{KeyCode, PhysicalKey}, 6 window::{Window, WindowAttributes, WindowId}, 7 }; 8 9 struct Renderbud { 10 surface: wgpu::Surface<'static>, 11 config: wgpu::SurfaceConfiguration, 12 device: wgpu::Device, 13 queue: wgpu::Queue, 14 renderer: renderbud::Renderer, 15 } 16 17 impl Renderbud { 18 async fn new(window: Window) -> Self { 19 let size = window.inner_size(); 20 21 let instance = wgpu::Instance::default(); 22 let surface = instance.create_surface(window).unwrap(); 23 24 let adapter = instance 25 .request_adapter(&wgpu::RequestAdapterOptions { 26 compatible_surface: Some(&surface), 27 force_fallback_adapter: false, 28 ..Default::default() 29 }) 30 .await 31 .unwrap(); 32 33 let (device, queue) = adapter 34 .request_device( 35 &wgpu::DeviceDescriptor { 36 label: None, 37 memory_hints: wgpu::MemoryHints::MemoryUsage, 38 required_features: wgpu::Features::empty(), 39 required_limits: wgpu::Limits::default(), 40 }, 41 None, 42 ) 43 .await 44 .unwrap(); 45 46 let surface_caps = surface.get_capabilities(&adapter); 47 let format = surface_caps 48 .formats 49 .iter() 50 .copied() 51 .find(|f| f.is_srgb()) 52 .unwrap_or(surface_caps.formats[0]); 53 54 let config = wgpu::SurfaceConfiguration { 55 usage: wgpu::TextureUsages::RENDER_ATTACHMENT, 56 format, 57 width: size.width.max(1), 58 height: size.height.max(1), 59 present_mode: surface_caps.present_modes[0], 60 alpha_mode: surface_caps.alpha_modes[0], 61 view_formats: vec![], 62 desired_maximum_frame_latency: 2, 63 }; 64 65 surface.configure(&device, &config); 66 67 let renderer = 68 renderbud::Renderer::new(&device, &queue, format, (config.width, config.height)); 69 70 Self { 71 config, 72 surface, 73 queue, 74 device, 75 renderer, 76 } 77 } 78 79 fn update(&mut self) { 80 self.renderer.update(); 81 } 82 83 fn prepare(&self) { 84 self.renderer.prepare(&self.queue); 85 } 86 87 fn resize(&mut self, new_size: (u32, u32)) { 88 let width = new_size.0.max(1); 89 let height = new_size.1.max(1); 90 91 self.config.width = width; 92 self.config.height = height; 93 self.surface.configure(&self.device, &self.config); 94 95 self.renderer.set_target_size((width, height)); 96 self.renderer.resize(&self.device) 97 } 98 99 fn size(&self) -> (u32, u32) { 100 self.renderer.size() 101 } 102 103 fn on_mouse_drag(&mut self, delta_x: f32, delta_y: f32) { 104 self.renderer.on_mouse_drag(delta_x, delta_y); 105 } 106 107 fn on_scroll(&mut self, delta: f32) { 108 self.renderer.on_scroll(delta); 109 } 110 111 fn load_gltf_model( 112 &mut self, 113 path: impl AsRef<std::path::Path>, 114 ) -> Result<renderbud::Model, gltf::Error> { 115 self.renderer 116 .load_gltf_model(&self.device, &self.queue, path) 117 } 118 119 fn render(&mut self) -> Result<(), wgpu::SurfaceError> { 120 let frame = self.surface.get_current_texture()?; 121 let view = frame 122 .texture 123 .create_view(&wgpu::TextureViewDescriptor::default()); 124 125 let mut encoder = self 126 .device 127 .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); 128 129 self.renderer.render(&view, &mut encoder); 130 self.queue.submit(Some(encoder.finish())); 131 frame.present(); 132 133 Ok(()) 134 } 135 } 136 137 struct App { 138 renderbud: Option<Renderbud>, 139 mouse_pressed: bool, 140 last_mouse_pos: Option<(f64, f64)>, 141 } 142 143 impl Default for App { 144 fn default() -> Self { 145 Self { 146 renderbud: None, 147 mouse_pressed: false, 148 last_mouse_pos: None, 149 } 150 } 151 } 152 153 impl ApplicationHandler for App { 154 fn resumed(&mut self, el: &ActiveEventLoop) { 155 // Create the window *after* the event loop is running (winit 0.30+). 156 let window: Window = el 157 .create_window(WindowAttributes::default()) 158 .expect("create_window failed"); 159 160 let mut renderbud = pollster::block_on(Renderbud::new(window)); 161 162 // pick a path relative to crate root 163 //let model_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/assets/ironwood.glb"); 164 let model_path = std::path::Path::new("/home/jb55/var/models/WaterBottle.glb"); 165 //let model_path = std::path::Path::new("/home/jb55/dev/github/KhronosGroup/glTF-Sample-Assets/Models/FlightHelmet/glTF/FlightHelmet.gltf"); 166 //let model_path = std::path::Path::new("/home/jb55/var/models/acnh-scuba.glb"); 167 //let model_path = std::path::Path::new("/home/jb55/var/models/ABeautifulGame.glb"); 168 renderbud.load_gltf_model(model_path).unwrap(); 169 170 self.renderbud = Some(renderbud); 171 } 172 173 fn window_event(&mut self, el: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { 174 let Some(renderbud) = self.renderbud.as_mut() else { 175 return; 176 }; 177 178 match event { 179 WindowEvent::CloseRequested => el.exit(), 180 181 WindowEvent::Resized(sz) => renderbud.resize((sz.width, sz.height)), 182 183 WindowEvent::KeyboardInput { event, .. } => { 184 if let KeyEvent { 185 physical_key: PhysicalKey::Code(code), 186 state: ElementState::Pressed, 187 .. 188 } = event 189 { 190 match code { 191 KeyCode::Space => { 192 // do something 193 } 194 _ => {} 195 } 196 } 197 } 198 199 WindowEvent::MouseInput { state, button, .. } => { 200 if button == MouseButton::Left { 201 self.mouse_pressed = state == ElementState::Pressed; 202 if !self.mouse_pressed { 203 self.last_mouse_pos = None; 204 } 205 } 206 } 207 208 WindowEvent::CursorMoved { position, .. } => { 209 let pos = (position.x, position.y); 210 if self.mouse_pressed { 211 if let Some(last) = self.last_mouse_pos { 212 let dx = (pos.0 - last.0) as f32; 213 let dy = (pos.1 - last.1) as f32; 214 renderbud.on_mouse_drag(dx, dy); 215 } 216 } 217 self.last_mouse_pos = Some(pos); 218 } 219 220 WindowEvent::MouseWheel { delta, .. } => { 221 let scroll = match delta { 222 MouseScrollDelta::LineDelta(_, y) => y, 223 MouseScrollDelta::PixelDelta(pos) => pos.y as f32 * 0.01, 224 }; 225 renderbud.on_scroll(scroll); 226 } 227 228 _ => {} 229 } 230 } 231 232 fn about_to_wait(&mut self, el: &ActiveEventLoop) { 233 let Some(renderbud) = self.renderbud.as_mut() else { 234 return; 235 }; 236 237 // Continuous rendering. 238 renderbud.update(); 239 renderbud.prepare(); 240 241 match renderbud.render() { 242 Ok(_) => {} 243 Err(wgpu::SurfaceError::Lost) => renderbud.resize(renderbud.size()), 244 Err(wgpu::SurfaceError::OutOfMemory) => el.exit(), 245 Err(_) => {} 246 } 247 } 248 } 249 250 fn main() -> Result<(), Box<dyn std::error::Error>> { 251 let event_loop = EventLoop::new()?; 252 253 // Equivalent to your `elwt.set_control_flow(ControlFlow::Poll);` 254 event_loop.set_control_flow(ControlFlow::Poll); 255 256 let mut app = App::default(); 257 event_loop.run_app(&mut app)?; 258 Ok(()) 259 }