diff --git a/kubi/src/rendering.rs b/kubi/src/rendering.rs index 16c3f1a..e109e41 100644 --- a/kubi/src/rendering.rs +++ b/kubi/src/rendering.rs @@ -1,21 +1,16 @@ -use pollster::FutureExt; -use raw_window_handle::HasRawWindowHandle; -use shipyard::{AllStoragesView, AllStoragesViewMut, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View}; -use wgpu::SurfaceTargetUnsafe; -use winit::{ - event_loop::ActiveEventLoop, - window::{WindowAttributes, Fullscreen, Window}, - dpi::PhysicalSize -}; +use shipyard::{AllStoragesView, AllStoragesViewMut, IntoIter, Unique, UniqueView, UniqueViewMut, View}; +use winit::dpi::PhysicalSize; use glam::{Vec3, UVec2}; -use crate::{events::WindowResizedEvent, settings::{FullscreenMode, GameSettings}, state::is_ingame}; +use crate::{events::WindowResizedEvent, state::is_ingame}; +pub mod renderer; pub mod primitives; pub mod world; pub mod selection_box; pub mod entities; pub mod sumberge; +pub use renderer::Renderer; pub struct BufferPair { pub index: wgpu::Buffer, pub vertex: wgpu::Buffer, @@ -31,158 +26,10 @@ pub struct BackgroundColor(pub Vec3); #[allow(deprecated)] pub struct WindowSize(pub UVec2); -#[derive(Unique)] -pub struct Renderer { - instance: wgpu::Instance, - surface: wgpu::Surface<'static>, - device: wgpu::Device, - queue: wgpu::Queue, - surface_config: wgpu::SurfaceConfiguration, - size: PhysicalSize, - // pub depth_texture: wgpu::Texture, - - //must be last due to drop order - window: Window, -} - -impl Renderer { - pub fn init(event_loop: &ActiveEventLoop, settings: &GameSettings) -> Self { - log::info!("initializing display"); - - let window_attributes = Window::default_attributes() - .with_title("kubi") - .with_maximized(true) - .with_min_inner_size(PhysicalSize::new(640, 480)) - .with_fullscreen({ - //this has no effect on android, so skip this pointless stuff - #[cfg(target_os = "android")] { - None - } - #[cfg(not(target_os = "android"))] - if let Some(fs_settings) = &settings.fullscreen { - let monitor = event_loop.primary_monitor().or_else(|| { - event_loop.available_monitors().next() - }); - - if let Some(monitor) = monitor { - log::info!("monitor: {}", monitor.name().unwrap_or_else(|| "generic".into())); - match fs_settings.mode { - FullscreenMode::Borderless => { - log::info!("starting in borderless fullscreen mode"); - Some(Fullscreen::Borderless(Some(monitor))) - }, - FullscreenMode::Exclusive => { - log::warn!("exclusive fullscreen mode is experimental"); - log::info!("starting in exclusive fullscreen mode"); - //TODO: grabbing the first video mode is probably not the best idea... - monitor.video_modes().next() - .map(|vmode| { - log::info!("video mode: {}", vmode.to_string()); - Some(Fullscreen::Exclusive(vmode)) - }) - .unwrap_or_else(|| { - log::warn!("no valid video modes found, falling back to windowed mode instead"); - None - }) - } - } - } else { - log::warn!("no monitors found, falling back to windowed mode"); - None - } - } else { - log::info!("starting in windowed mode"); - None - } - }); - let window = event_loop.create_window(window_attributes).unwrap(); - - let size = window.inner_size(); - - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::Backends::BROWSER_WEBGPU | wgpu::Backends::VULKAN | wgpu::Backends::GL, - ..Default::default() - }); - - // Create a surface with `create_surface_unsafe` to get a surface with 'static lifetime - // It should never outlive the window it's created from - let surface = unsafe { - instance.create_surface_unsafe(SurfaceTargetUnsafe::from_window(&window).unwrap()).unwrap() - }; - - let adapter = instance.request_adapter( - &wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }, - ).block_on().unwrap(); - - let (device, queue) = adapter.request_device( - &wgpu::DeviceDescriptor { - label: None, - required_features: wgpu::Features::empty(), - required_limits: wgpu::Limits::downlevel_defaults(), - }, - None, - ).block_on().unwrap(); - - let surface_config = surface.get_default_config(&adapter, size.width, size.height).unwrap(); - surface.configure(&device, &surface_config); - - Self { window, instance, surface, device, queue, surface_config, size } - } - - pub fn resize(&mut self, size: PhysicalSize) { - if size.width == 0 || size.height == 0 { - log::warn!("Ignoring resize event with zero width or height"); - return - } - if self.size == size { - log::warn!("Ignoring resize event with same size"); - return - } - log::debug!("resizing surface to {:?}", size); - self.size = size; - self.surface_config.width = size.width; - self.surface_config.height = size.height; - self.surface.configure(&self.device, &self.surface_config); - } - - pub fn reconfigure(&self) { - self.surface.configure(&self.device, &self.surface_config); - } - - //getters: - pub fn size(&self) -> PhysicalSize { - self.size - } - - pub fn window(&self) -> &Window { - &self.window - } - - pub fn surface(&self) -> &wgpu::Surface<'static> { - &self.surface - } - - pub fn device(&self) -> &wgpu::Device { - &self.device - } - - pub fn queue(&self) -> &wgpu::Queue { - &self.queue - } - - pub fn surface_config(&self) -> &wgpu::SurfaceConfiguration { - &self.surface_config - } -} - pub fn render_master(storages: AllStoragesViewMut) { let renderer = storages.borrow::>().unwrap(); - let mut encoder = renderer.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + let mut encoder = renderer.device().create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("main_encoder"), }); let surface_texture = renderer.surface().get_current_texture().unwrap(); diff --git a/kubi/src/rendering/renderer.rs b/kubi/src/rendering/renderer.rs new file mode 100644 index 0000000..5f251a8 --- /dev/null +++ b/kubi/src/rendering/renderer.rs @@ -0,0 +1,157 @@ +use pollster::FutureExt; +use shipyard::Unique; +use winit::{ + event_loop::ActiveEventLoop, + window::{Fullscreen, Window}, + dpi::PhysicalSize +}; +use crate::settings::{GameSettings, FullscreenMode}; + +#[derive(Unique)] +pub struct Renderer { + instance: wgpu::Instance, + surface: wgpu::Surface<'static>, + device: wgpu::Device, + queue: wgpu::Queue, + surface_config: wgpu::SurfaceConfiguration, + size: PhysicalSize, + // pub depth_texture: wgpu::Texture, + + //must be last due to drop order + window: Window, +} + +impl Renderer { + pub fn init(event_loop: &ActiveEventLoop, settings: &GameSettings) -> Self { + log::info!("initializing display"); + + let window_attributes = Window::default_attributes() + .with_title("kubi") + .with_maximized(true) + .with_min_inner_size(PhysicalSize::new(640, 480)) + .with_fullscreen({ + //this has no effect on android, so skip this pointless stuff + #[cfg(target_os = "android")] { + None + } + #[cfg(not(target_os = "android"))] + if let Some(fs_settings) = &settings.fullscreen { + let monitor = event_loop.primary_monitor().or_else(|| { + event_loop.available_monitors().next() + }); + + if let Some(monitor) = monitor { + log::info!("monitor: {}", monitor.name().unwrap_or_else(|| "generic".into())); + match fs_settings.mode { + FullscreenMode::Borderless => { + log::info!("starting in borderless fullscreen mode"); + Some(Fullscreen::Borderless(Some(monitor))) + }, + FullscreenMode::Exclusive => { + log::warn!("exclusive fullscreen mode is experimental"); + log::info!("starting in exclusive fullscreen mode"); + //TODO: grabbing the first video mode is probably not the best idea... + monitor.video_modes().next() + .map(|vmode| { + log::info!("video mode: {}", vmode.to_string()); + Some(Fullscreen::Exclusive(vmode)) + }) + .unwrap_or_else(|| { + log::warn!("no valid video modes found, falling back to windowed mode instead"); + None + }) + } + } + } else { + log::warn!("no monitors found, falling back to windowed mode"); + None + } + } else { + log::info!("starting in windowed mode"); + None + } + }); + let window = event_loop.create_window(window_attributes).unwrap(); + + let size = window.inner_size(); + + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: wgpu::Backends::BROWSER_WEBGPU | wgpu::Backends::VULKAN | wgpu::Backends::GL, + ..Default::default() + }); + + // Create a surface with `create_surface_unsafe` to get a surface with 'static lifetime + // It should never outlive the window it's created from + let surface = unsafe { + let target = wgpu::SurfaceTargetUnsafe::from_window(&window).unwrap(); + instance.create_surface_unsafe(target).unwrap() + }; + + let adapter = instance.request_adapter( + &wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + compatible_surface: Some(&surface), + force_fallback_adapter: false, + }, + ).block_on().unwrap(); + + let (device, queue) = adapter.request_device( + &wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::empty(), + required_limits: wgpu::Limits::downlevel_defaults(), + }, + None, + ).block_on().unwrap(); + + let surface_config = surface.get_default_config(&adapter, size.width, size.height).unwrap(); + surface.configure(&device, &surface_config); + + Self { window, instance, surface, device, queue, surface_config, size } + } + + pub fn resize(&mut self, size: PhysicalSize) { + if size.width == 0 || size.height == 0 { + log::warn!("Ignoring resize event with zero width or height"); + return + } + if self.size == size { + log::warn!("Ignoring resize event with same size"); + return + } + log::debug!("resizing surface to {:?}", size); + self.size = size; + self.surface_config.width = size.width; + self.surface_config.height = size.height; + self.surface.configure(&self.device, &self.surface_config); + } + + pub fn reconfigure(&self) { + self.surface.configure(&self.device, &self.surface_config); + } + + //getters: + pub fn size(&self) -> PhysicalSize { + self.size + } + + pub fn window(&self) -> &Window { + &self.window + } + + pub fn surface(&self) -> &wgpu::Surface<'static> { + &self.surface + } + + pub fn device(&self) -> &wgpu::Device { + &self.device + } + + pub fn queue(&self) -> &wgpu::Queue { + &self.queue + } + + pub fn surface_config(&self) -> &wgpu::SurfaceConfiguration { + &self.surface_config + } +}