notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

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 }