use std::rc::Rc; use crate::mvu::{Event, MvuApp, Size2i}; use { log::info, std::{borrow::Cow, path::PathBuf}, wgpu::{Device, Instance, Queue, RenderPipeline, Surface, SurfaceConfiguration}, }; mod dem_renderer; mod raster; use dem_renderer::DemRenderer; #[derive(Clone)] pub struct Model { dem: Option>, } impl Model { pub fn new() -> Model { Model { dem: None } } } struct Context { config: SurfaceConfiguration, surface: Surface<'static>, device: Device, render_pipeline: RenderPipeline, queue: Queue, scene_data: Option, } #[derive(Default)] pub struct App { context: Option, } impl App { pub fn new() -> Self { Self::default() } } impl MvuApp for App { async fn init(&mut self, instance: &Instance, surface: Surface<'static>, size: Size2i) { let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, force_fallback_adapter: false, compatible_surface: Some(&surface), }) .await .expect("Failed to find an appropriate adapter"); eprintln!("Using {}", adapter.get_info().name); let (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { label: None, required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::downlevel_webgl2_defaults() .using_resolution(adapter.limits()), memory_hints: wgpu::MemoryHints::MemoryUsage, }, None, ) .await .expect("Failed to create device"); let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), }); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, bind_group_layouts: &[], push_constant_ranges: &[], }); let swapchain_capabilities = surface.get_capabilities(&adapter); let swapchain_format = swapchain_capabilities.formats[0]; let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, layout: Some(&pipeline_layout), vertex: wgpu::VertexState { module: &shader, entry_point: Some("vs_main"), buffers: &[], compilation_options: Default::default(), }, fragment: Some(wgpu::FragmentState { module: &shader, entry_point: Some("fs_main"), compilation_options: Default::default(), targets: &[Some(swapchain_format.into())], }), primitive: wgpu::PrimitiveState::default(), depth_stencil: None, multisample: wgpu::MultisampleState::default(), multiview: None, cache: None, }); let mut config = surface .get_default_config(&adapter, size.width, size.height) .unwrap(); config.present_mode = wgpu::PresentMode::AutoVsync; config.view_formats.push(config.format); surface.configure(&device, &config); self.context = Some(Context { config, surface, device, render_pipeline, queue, scene_data: None, }); info!("Initialized {}x{}.", size.width, size.height); } async fn resize(&mut self, new_size: Size2i) { if let Some(Context { config, surface, device, .. }) = &mut self.context { config.width = new_size.width.max(1); config.height = new_size.height.max(1); surface.configure(device, config); info!("Resized_to {}x{}.", new_size.width, new_size.height); } } async fn update(&self, model: Rc, event: Event) -> Rc { match event { Event::MouseButtonPressed => model, Event::OpenTestFile(file_path) => open_test_file(file_path, model.clone()), } } async fn view(&mut self, model: Rc) -> Result<(), Box> { if let Some(context) = &mut self.context { if context.scene_data.is_none() { if let Some(dem) = &model.dem { context.scene_data = Some(DemRenderer::new( dem.clone(), &context.device, &context.config, &context.queue, )) } } let frame = context .surface .get_current_texture() .expect("Failed to acquire next swap chain texture"); let view = frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); if let Some(scene_data) = &mut context.scene_data { scene_data.render(&view, &context.device, &context.queue); } else { let mut encoder = context .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), store: wgpu::StoreOp::Store, }, })], depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, }); rpass.set_pipeline(&context.render_pipeline); rpass.draw(0..3, 0..1); } context.queue.submit(Some(encoder.finish())); } frame.present(); } Ok(()) } } fn open_test_file(file_path: PathBuf, model: Rc) -> Rc { let dem = Some(Rc::new(raster::Dem::load_from_image(&file_path))); Rc::new(Model { dem, ..*model }) }