use crate::ui::ViewportWidget; use crate::wgpu; use cyborg::camera::Camera; use cyborg::viewport::{Viewport, ViewportInfo, ViewportViews}; use std::sync::Arc; pub struct OffscreenTextures { pub width: u32, pub height: u32, pub output_texture: wgpu::Texture, pub output_view: wgpu::TextureView, pub depth_texture: wgpu::Texture, pub depth_view: wgpu::TextureView, } impl OffscreenTextures { pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; pub fn new( device: &wgpu::Device, width: u32, height: u32, output_format: wgpu::TextureFormat, ) -> Self { let size = wgpu::Extent3d { width, height, depth_or_array_layers: 1, }; let mut tex_desc = wgpu::TextureDescriptor { label: None, size, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: output_format, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, }; tex_desc.label = Some("Offscreen Output Texture"); let output_texture = device.create_texture(&tex_desc); tex_desc.label = Some("Offscreen Depth Texture"); tex_desc.format = Self::DEPTH_FORMAT; let depth_texture = device.create_texture(&tex_desc); let view_desc = wgpu::TextureViewDescriptor::default(); let output_view = output_texture.create_view(&view_desc); let depth_view = depth_texture.create_view(&view_desc); Self { width, height, output_texture, output_view, depth_texture, depth_view, } } } pub struct OffscreenViewport { pub device: Arc, pub queue: Arc, pub output_format: wgpu::TextureFormat, pub textures: OffscreenTextures, pub egui_texture: egui::TextureId, pub camera: Camera, } impl OffscreenViewport { pub fn new( device: Arc, queue: Arc, output_format: wgpu::TextureFormat, render_pass: &mut egui_wgpu_backend::RenderPass, ) -> Self { let textures = OffscreenTextures::new(&device, 640, 480, output_format); let egui_texture = render_pass.egui_texture_from_wgpu_texture( &device, &textures.output_view, wgpu::FilterMode::Nearest, ); Self { device, queue, output_format, textures, egui_texture, camera: Camera { eye: [1.0, 1.0, 1.0, 0.0], vp: Default::default(), }, } } pub fn get_camera(&self) -> Camera { self.camera.clone() } } impl Viewport for OffscreenViewport { fn get_info(&self) -> ViewportInfo { ViewportInfo { output_format: self.output_format, depth_format: OffscreenTextures::DEPTH_FORMAT, } } fn get_queue(&self) -> &wgpu::Queue { &self.queue } fn get_views(&self) -> ViewportViews { ViewportViews { output: &self.textures.output_view, depth: &self.textures.depth_view, } } } pub struct ViewportStore { pub device: Arc, pub output_format: wgpu::TextureFormat, pub viewport: OffscreenViewport, } impl ViewportStore { pub fn new( device: Arc, queue: Arc, output_format: wgpu::TextureFormat, render_pass: &mut egui_wgpu_backend::RenderPass, ) -> Self { Self { device: device.clone(), output_format, viewport: OffscreenViewport::new(device, queue, output_format, render_pass), } } } impl cyborg::legion::RenderCallbacks for ViewportStore { fn get_viewports(&mut self) -> Vec<(&dyn Viewport, Camera)> { vec![(&self.viewport, self.viewport.get_camera())] } fn present(&mut self) {} } pub struct RenderState { pub world: legion::World, pub resources: legion::Resources, pub render_schedule: legion::Schedule, } impl RenderState { pub fn new( device: Arc, queue: Arc, output_format: wgpu::TextureFormat, render_pass: &mut egui_wgpu_backend::RenderPass, ) -> Self { use cyborg::shader::{ShaderStore, ShaderWatcher}; let mut world = legion::World::default(); let mut resources = legion::Resources::default(); let viewport_store = ViewportStore::new(device.clone(), queue.clone(), output_format, render_pass); let renderer = cyborg::Renderer::new(device.clone(), queue.clone()); resources.insert(renderer); let viewport_info = ViewportInfo { output_format, depth_format: OffscreenTextures::DEPTH_FORMAT, }; resources.insert(viewport_info); let shader_store = Arc::new(ShaderStore::new(device.clone())); let shaders_dir = std::env::current_dir().unwrap(); let shaders_dir = shaders_dir.join("shaders/"); let shader_watcher = 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 = cyborg::pass::mesh::ShaderInfo { store: shader_store.clone(), forward: mesh_forward, skinning: mesh_skinning, }; resources.insert(mesh_shaders); let mut render_schedule = legion::Schedule::builder(); cyborg::legion::build_renderer(viewport_store, &mut resources, &mut render_schedule); let render_schedule = render_schedule.build(); world.push(( cyborg::scene::Transform { transform: Default::default(), }, cyborg::scene::DebugDrawList { vertices: vec![ cyborg::scene::DebugVertex { color: [1.0, 0.0, 0.0], position: [0.0, 0.0, -1.0], }, cyborg::scene::DebugVertex { color: [0.0, 0.0, 1.0], position: [0.0, 0.0, 1.0], }, ], indices: vec![0, 1], }, )); Self { world, resources, render_schedule, } } pub fn update_viewport(&mut self, viewport: &mut ViewportWidget) { let mut store = self.resources.get_mut::().unwrap(); store.viewport.camera = viewport.flycam.get_camera(); } pub fn render(&mut self) { self.render_schedule .execute(&mut self.world, &mut self.resources); } }