diff --git a/dunge/Cargo.toml b/dunge/Cargo.toml index c637189..0e766f9 100644 --- a/dunge/Cargo.toml +++ b/dunge/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dunge" -version = "0.2.2" +version = "0.2.3" edition = "2021" description = "Simple and portable 3d render library" license = "MIT" @@ -11,8 +11,8 @@ repository = "https://github.com/nanoqsh/dunge" rust-version = "1.71" [dependencies] -dunge_macros = "0.2" -dunge_shader = "0.2" +dunge_macros = { version = "0.2.3", path = "../dunge_macros" } +dunge_shader = { version = "0.2.3", path = "../dunge_shader" } bytemuck = { version = "1.13", features = ["derive"] } glam = "0.25" instant = "0.1" diff --git a/dunge/src/buffer.rs b/dunge/src/buffer.rs deleted file mode 100644 index 37458bf..0000000 --- a/dunge/src/buffer.rs +++ /dev/null @@ -1,30 +0,0 @@ -use wgpu::{Buffer, BufferAddress, BufferSlice}; - -#[derive(Clone, Copy)] -pub(crate) struct BufferView<'a> { - buf: &'a Buffer, - len: u32, -} - -impl<'a> BufferView<'a> { - pub fn new(buf: &'a Buffer, limit: Option) -> Self { - use std::mem; - - let size = mem::size_of::() as BufferAddress; - assert!(size > 0, "buffer element size cannot be zero"); - - let len = (buf.size() / size) as u32; - Self { - buf, - len: limit.map_or(len, |n| len.min(n)), - } - } - - pub fn slice(&self) -> BufferSlice<'a> { - self.buf.slice(..) - } - - pub fn len(&self) -> u32 { - self.len - } -} diff --git a/dunge/src/canvas.rs b/dunge/src/canvas.rs deleted file mode 100644 index baaed73..0000000 --- a/dunge/src/canvas.rs +++ /dev/null @@ -1,531 +0,0 @@ -use { - crate::{ - context::Context, - element::Element, - r#loop::{Input, Keys, Loop, Mouse}, - render::State, - screen::Screen, - time::{Fps, Time}, - }, - wgpu::AdapterInfo, - winit::{ - error::EventLoopError, - event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopWindowTarget}, - window::{Window, WindowBuilder}, - }, -}; - -/// The render canvas. -pub struct Canvas { - event_loop: EventLoop, - window: Window, - el: Element, -} - -impl Canvas { - /// Calls [`run`](crate::Canvas::run) but blocking instead of async. - /// - /// # Errors - /// Returns [`CanvasError`](crate::error::CanvasError) if backend selection or request device failed. - #[cfg(not(target_arch = "wasm32"))] - pub fn run_blocking(self, config: CanvasConfig, make: M) -> Result<(), Error> - where - M: FnOnce(&mut Context) -> L, - L: Loop + 'static, - { - pollster::block_on(self.run(config, make)) - } - - /// Runs the main loop. - /// - /// This function construct a [`Context`] instance and - /// calls a `make` by passing the context in it. - /// The `make` needs to return an object which - /// implements the [`Loop`] trait. - /// - /// # Errors - /// Returns [`CanvasError`](crate::error::CanvasError) if backend selection or request device failed. - pub async fn run(self, config: CanvasConfig, make: M) -> Result<(), Error> - where - M: FnOnce(&mut Context) -> L, - L: Loop + 'static, - { - let Self { - event_loop, - window, - el, - } = self; - - // Create the context - let mut context = { - // Create the render state - let state = State::new(config, &window).await?; - Context::new(window, event_loop.create_proxy(), state) - }; - - // Create the loop object - let mut lp = make(&mut context); - - // Initial state - let mut active = false; - let mut time = Time::now(); - let mut fps = Fps::default(); - let mut deferred_screen = None; - let mut cursor_position = None; - let mut last_touch = None; - let mut mouse = Mouse::default(); - let mut pressed_keys = vec![]; - let mut released_keys = vec![]; - - let handler = move |ev, target: &EventLoopWindowTarget<_>| { - use { - std::{num::NonZeroU32, time::Duration}, - wgpu::SurfaceError, - winit::{ - dpi::{PhysicalPosition, PhysicalSize}, - event::{ - DeviceEvent, ElementState, Event, KeyEvent, MouseButton, MouseScrollDelta, - StartCause, Touch, TouchPhase, WindowEvent, - }, - keyboard::PhysicalKey, - }, - }; - - const WAIT_TIME: Duration = Duration::from_millis(100); - - match ev { - Event::NewEvents(cause) => match cause { - StartCause::ResumeTimeReached { .. } => { - log::info!("resume time reached"); - - el.set_window_size(&context.window); - context.window.request_redraw(); - } - StartCause::WaitCancelled { - requested_resume, .. - } => { - log::info!("wait cancelled"); - - target.set_control_flow(match requested_resume { - Some(resume) => ControlFlow::WaitUntil(resume), - None => ControlFlow::wait_duration(WAIT_TIME), - }); - } - StartCause::Poll => log::info!("poll"), - StartCause::Init => log::info!("init"), - }, - Event::WindowEvent { event, window_id } if window_id == context.window.id() => { - log::info!("window event: {event:?}"); - - match event { - WindowEvent::Resized(size) => context.render.resize(size.into()), - WindowEvent::ScaleFactorChanged { scale_factor, .. } => { - let PhysicalSize { width, height } = context.window.inner_size(); - let size = ( - (width as f64 * scale_factor) as u32, - (height as f64 * scale_factor) as u32, - ); - - context.render.resize(size); - } - WindowEvent::CloseRequested if lp.close_requested() => target.exit(), - WindowEvent::Focused(true) => context.window.request_redraw(), - WindowEvent::KeyboardInput { - event: - KeyEvent { - physical_key: PhysicalKey::Code(key), - state, - .. - }, - .. - } => match state { - ElementState::Pressed => pressed_keys.push(key), - ElementState::Released => released_keys.push(key), - }, - WindowEvent::CursorMoved { - position: PhysicalPosition { x, y }, - .. - } => { - let PhysicalSize { width, height } = context.window.inner_size(); - let nx = 1. - x as f32 * 2. / width as f32; - let ny = 1. - y as f32 * 2. / height as f32; - cursor_position = Some((nx, ny)); - } - WindowEvent::CursorLeft { .. } => { - cursor_position = None; - } - WindowEvent::MouseWheel { - delta: MouseScrollDelta::LineDelta(x, y), - .. - } => { - mouse.wheel_delta.0 += x; - mouse.wheel_delta.1 += y; - } - WindowEvent::MouseInput { state, button, .. } => match button { - MouseButton::Left => { - mouse.pressed_left = state == ElementState::Pressed; - } - MouseButton::Right => { - mouse.pressed_right = state == ElementState::Pressed; - } - MouseButton::Middle => { - mouse.pressed_middle = state == ElementState::Pressed; - } - _ => {} - }, - WindowEvent::Touch(Touch { - phase, - location: PhysicalPosition { x, y }, - .. - }) => match phase { - TouchPhase::Started => {} - TouchPhase::Moved => { - let (nx, ny) = (x as f32, y as f32); - if let Some((lx, ly)) = last_touch { - mouse.motion_delta.0 = lx - nx; - mouse.motion_delta.1 = ly - ny; - } - - last_touch = Some((nx, ny)); - } - TouchPhase::Ended | TouchPhase::Cancelled => last_touch = None, - }, - WindowEvent::RedrawRequested => { - if active { - log::info!("redraw requested (active)"); - } else { - log::info!("redraw requested"); - - // Wait a while to become active - target.set_control_flow(ControlFlow::wait_duration(WAIT_TIME)); - return; - } - - // Measure the delta time - let delta_time = time.delta(); - - // If frame rate is limited, skip drawing until it's time - let min_delta_time = context.limits.min_delta_time; - if delta_time < min_delta_time { - let wait = Duration::from_secs_f32(min_delta_time - delta_time); - target.set_control_flow(ControlFlow::wait_duration(wait)); - return; - } - - // Count number of frames - if let Some(fps) = fps.count(delta_time) { - context.fps = fps; - } - - // Create an user's input data - let input = Input { - delta_time, - cursor_position, - mouse, - pressed_keys: Keys { - keys: &pressed_keys[..], - }, - released_keys: Keys { - keys: &released_keys[..], - }, - }; - - // Reset delta time - time.reset(); - - // Update the loop - lp.update(&mut context, &input); - - // Reset mouse delta - mouse = Mouse::default(); - - // Reset keys - pressed_keys.clear(); - released_keys.clear(); - - match context.render.draw_frame(&lp) { - Ok(()) => {} - Err(SurfaceError::Timeout) => { - log::info!("suface error: timeout"); - } - Err(SurfaceError::Outdated | SurfaceError::Lost) => { - context.render.resize(context.window.inner_size().into()); - } - Err(SurfaceError::OutOfMemory) => { - log::error!("suface error: out of memory"); - target.exit(); - } - } - } - _ => {} - } - } - Event::DeviceEvent { - event: DeviceEvent::MouseMotion { delta: (x, y) }, - .. - } => { - log::info!("device event: mouse motion"); - - mouse.motion_delta.0 += x as f32; - mouse.motion_delta.1 += y as f32; - } - Event::UserEvent(CanvasEvent::SetScreen(screen)) => { - log::info!("user event: set screen"); - if active { - context.render.set_screen(screen); - } else { - deferred_screen = Some(screen); - } - } - Event::UserEvent(CanvasEvent::Close) if lp.close_requested() => { - log::info!("user event: close"); - target.exit(); - } - Event::Suspended => { - log::info!("suspended"); - context.render.drop_surface(); - active = false; - } - Event::Resumed => { - log::info!("resumed"); - context.render.recreate_surface(&context.window); - - // Set render screen on application start and resume - let (width, height) = context.window.inner_size().into(); - match deferred_screen.take() { - Some(screen) => context.render.set_screen(Screen { - width: NonZeroU32::new(width).unwrap_or(NonZeroU32::MIN), - height: NonZeroU32::new(height).unwrap_or(NonZeroU32::MIN), - ..screen - }), - None => context.render.resize((width, height)), - } - - active = true; - context.window.request_redraw(); - - if let Some(screen) = deferred_screen.take() { - context.render.set_screen(screen); - } - - // Reset the timer before start the loop - time.reset(); - } - _ => {} - } - }; - - event_loop.run(handler).map_err(Error::EventLoop) - } -} - -/// An error returned from the [`Context`] constructors. -#[derive(Debug)] -pub enum Error { - BackendSelection(Backend), - RequestDevice, - EventLoop(EventLoopError), -} - -pub(crate) enum CanvasEvent { - SetScreen(Screen), - Close, -} - -#[cfg(not(target_arch = "wasm32"))] -pub(crate) mod window { - use super::*; - - /// Creates a canvas in the window with given initial state. - /// - /// # Panics - /// Panics if window creation fails. - pub fn make_window(state: InitialState) -> Canvas { - use winit::{dpi::PhysicalSize, window::Fullscreen}; - - let builder = WindowBuilder::new().with_title(state.title); - let builder = match state.mode { - WindowMode::Fullscreen => builder.with_fullscreen(Some(Fullscreen::Borderless(None))), - WindowMode::Windowed { width, height } => { - builder.with_inner_size(PhysicalSize::new(width.max(1), height.max(1))) - } - }; - - let event_loop = EventLoopBuilder::with_user_event() - .build() - .expect("build event loop"); - - let window = builder.build(&event_loop).expect("build window"); - window.set_cursor_visible(state.show_cursor); - - Canvas { - event_loop, - window, - el: Element::default(), - } - } - - /// The initial window state. - #[derive(Clone, Copy)] - pub struct InitialState<'a> { - pub title: &'a str, - pub mode: WindowMode, - pub show_cursor: bool, - } - - impl Default for InitialState<'_> { - fn default() -> Self { - Self { - title: "Dunge", - mode: WindowMode::default(), - show_cursor: true, - } - } - } - - /// The window mode. - #[derive(Clone, Copy, Default)] - pub enum WindowMode { - #[default] - Fullscreen, - Windowed { - width: u32, - height: u32, - }, - } -} - -/// Creates a canvas in the HTML element by its id. -#[cfg(target_arch = "wasm32")] -pub fn from_element(id: &str) -> Canvas { - use {web_sys::Window, winit::platform::web::WindowExtWebSys}; - - let event_loop = EventLoopBuilder::with_user_event() - .build() - .expect("build event loop"); - - let window = WindowBuilder::new() - .build(&event_loop) - .expect("build window"); - - let document = web_sys::window() - .as_ref() - .and_then(Window::document) - .expect("get document"); - - let Some(el) = document.get_element_by_id(id) else { - panic!("an element with id {id:?} not found"); - }; - - let el = Element::new(el); - el.set_window_size(&window); - - let canvas = window.canvas(); - canvas.remove_attribute("style").expect("remove attribute"); - el.append_child(&canvas).expect("append child"); - - Canvas { - event_loop, - window, - el, - } -} - -#[cfg(target_os = "android")] -pub(crate) mod android { - use super::*; - use winit::platform::android::activity::AndroidApp; - - /// Creates a canvas from the `AndroidApp` class. - pub fn from_app(app: AndroidApp) -> Canvas { - use winit::platform::android::EventLoopBuilderExtAndroid; - - let event_loop = EventLoopBuilder::with_user_event() - .with_android_app(app) - .build() - .expect("build event loop"); - - let window = WindowBuilder::new() - .build(&event_loop) - .expect("build window"); - - Canvas { - event_loop, - window, - el: Element::default(), - } - } -} - -/// The [`Canvas`] config. -#[derive(Default)] -pub struct CanvasConfig { - pub backend: Backend, - pub selector: Selector, -} - -/// Description of [backend](Backend) selection behavior. -#[derive(Default)] -pub enum Selector { - #[default] - Auto, - #[cfg(not(target_arch = "wasm32"))] - Callback(Box) -> Option>), -} - -/// The render backend. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum Backend { - #[cfg_attr(not(target_arch = "wasm32"), default)] - Vulkan, - #[cfg_attr(target_arch = "wasm32", default)] - Gl, - Dx12, - Dx11, - Metal, - WebGpu, -} - -/// The render context info. -#[derive(Clone, Debug)] -pub struct Info { - pub backend: Backend, - pub name: String, - pub device: Device, -} - -impl Info { - pub(crate) fn from_adapter_info(info: AdapterInfo) -> Self { - use wgpu::{Backend as Bk, DeviceType}; - - Self { - backend: match info.backend { - Bk::Empty => unreachable!(), - Bk::Vulkan => Backend::Vulkan, - Bk::Metal => Backend::Metal, - Bk::Dx12 => Backend::Dx12, - Bk::Dx11 => Backend::Dx11, - Bk::Gl => Backend::Gl, - Bk::BrowserWebGpu => Backend::WebGpu, - }, - name: info.name, - device: match info.device_type { - DeviceType::IntegratedGpu => Device::IntegratedGpu, - DeviceType::DiscreteGpu => Device::DiscreteGpu, - DeviceType::VirtualGpu => Device::VirtualGpu, - DeviceType::Cpu => Device::Cpu, - DeviceType::Other => Device::Other, - }, - } - } -} - -/// The device type. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Device { - IntegratedGpu, - DiscreteGpu, - VirtualGpu, - Cpu, - Other, -} diff --git a/dunge/src/color.rs b/dunge/src/color.rs deleted file mode 100644 index 8ba7a0a..0000000 --- a/dunge/src/color.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Color types. - -/// A linear RGB color. -pub type Rgb = Color<3>; - -/// A linear RGBA color. -pub type Rgba = Color<4>; - -/// A linear RGB(A) color. -#[derive(Clone, Copy)] -pub struct Color(pub [f32; N]); - -impl Color { - /// Creates a linear `Color` from sRGB(A) color. - pub fn from_standard(col: [f32; N]) -> Self { - fn to_linear(c: f32) -> f32 { - if c > 0.04045 { - ((c + 0.055) / 1.055).powf(2.4) - } else { - c / 12.92 - } - } - - Self(col.map(to_linear)) - } - - /// Creates a linear `Color` from a linear bytes. - pub fn from_bytes(col: [u8; N]) -> Self { - Self(col.map(to_f32_color)) - } - - /// Creates a linear `Color` from a sRGB(A) bytes. - pub fn from_standard_bytes(col: [u8; N]) -> Self { - Self::from_standard(col.map(to_f32_color)) - } -} - -fn to_f32_color(c: u8) -> f32 { - f32::from(c) / u8::MAX as f32 -} diff --git a/dunge/src/context.rs b/dunge/src/context.rs deleted file mode 100644 index b718484..0000000 --- a/dunge/src/context.rs +++ /dev/null @@ -1,197 +0,0 @@ -use { - crate::{ - canvas::{CanvasEvent, Info}, - layer::Layer, - mesh::{Data as MeshData, Mesh}, - pipeline::LayerBuilder, - posteffect::Builder as PostEffectBuilder, - postproc::FrameFilter, - render::{Render, State}, - scheme::Scheme, - screen::Screen, - shader::Shader, - shader_data::{ - globals::Builder as GlobalsBuilder, lights::Builder as LightsBuilder, - spaces::Builder as SpacesBuilder, texture::Texture, - textures::Builder as TexturesBuilder, Instance, InstanceColor, ModelColor, - ModelTransform, TextureData, - }, - topology::Topology, - vertex::Vertex, - }, - winit::{event_loop::EventLoopProxy, window::Window}, -}; - -type Proxy = EventLoopProxy; - -/// The application context. -pub struct Context { - pub(crate) window: Window, - pub(crate) proxy: Proxy, - pub(crate) render: Render, - pub(crate) limits: Limits, - pub(crate) fps: u32, -} - -impl Context { - #[allow(clippy::unnecessary_box_returns)] - pub(crate) fn new(window: Window, proxy: Proxy, state: State) -> Box { - Box::new(Self { - window, - proxy, - render: Render::new(state), - limits: Limits::default(), - fps: 0, - }) - } - - /// Returns the window. - pub fn window(&self) -> &Window { - &self.window - } - - /// Returns the render info. - pub fn info(&self) -> &Info { - self.render.info() - } - - /// Returns the number of rendered frames per second. - pub fn fps(&self) -> u32 { - self.fps - } - - /// Plans the main loop to close. - /// - /// Calling this function dosn't guarantee closing. - /// It triggers the [`close_requested`](crate::Loop::close_requested) - /// function in the [`Loop`](crate::Loop), which can handle the closing event. - pub fn plan_to_close(&self) { - _ = self.proxy.send_event(CanvasEvent::Close); - } - - /// Returns the canvas size. - pub fn size(&self) -> (u32, u32) { - self.render.screen().virtual_size().into() - } - - /// Sets a minimal time between two frames in seconds. - /// - /// The [context](crate::Context) will draw the next - /// frame no earlier than the specified time. - pub fn set_min_delta_time(&mut self, delta: f32) { - self.limits.min_delta_time = delta; - } - - /// Sets context's frame parameters via [`FrameParameters`] struct. - pub fn set_frame_parameters(&self, params: FrameParameters) { - _ = self.proxy.send_event(CanvasEvent::SetScreen(Screen { - pixel_size: params.pixel_size, - filter: params.filter, - ..self.render.screen() - })); - } - - /// Creates a [globals](crate::Globals) builder. - pub fn globals_builder(&self) -> GlobalsBuilder { - GlobalsBuilder::new(&self.render) - } - - /// Creates a [lights](crate::Lights) builder. - pub fn lights_builder(&self) -> LightsBuilder { - LightsBuilder::new(&self.render) - } - - /// Creates a [spaces](crate::Spaces) builder. - pub fn spaces_builder(&self) -> SpacesBuilder { - SpacesBuilder::new(&self.render) - } - - /// Creates a [textures](crate::Textures) builder. - pub fn textures_builder(&self) -> TexturesBuilder { - TexturesBuilder::new(&self.render) - } - - /// Creates a [post-effect](crate::PostEffect) builder. - pub fn posteffect_builder(&self) -> PostEffectBuilder { - PostEffectBuilder::new(&self.render) - } - - /// Creates a new shader [scheme](Scheme). - pub fn create_scheme(&self) -> Scheme - where - S: Shader, - { - Scheme::new() - } - - /// Creates a new [layer](Layer) with default parameters. - /// - /// This is a shortcut for `context.create_layer_with().build(scheme)` - /// with an automatically generated shader [scheme](Scheme). - /// Use the [`create_layer_with`](crate::Context::create_layer_with) - /// function to create a custom layer. - pub fn create_layer(&self) -> Layer - where - S: Shader, - T: Topology, - { - let scheme = self.create_scheme(); - self.create_layer_with().build(&scheme) - } - - /// Creates a [layer](Layer) builder with custom parameters. - pub fn create_layer_with(&self) -> LayerBuilder { - LayerBuilder::new(&self.render) - } - - /// Creates new [instances](Instance). - pub fn create_instances(&self, models: &[ModelTransform]) -> Instance { - Instance::new(models, &self.render) - } - - /// Creates new color [instances](Instance). - pub fn create_instances_color(&self, models: &[ModelColor]) -> InstanceColor { - InstanceColor::new(models, &self.render) - } - - /// Creates a new [mesh](Mesh). - pub fn create_mesh(&self, data: &MeshData) -> Mesh - where - V: Vertex, - T: Topology, - { - Mesh::new(data, &self.render) - } - - /// Creates a new [texture](Texture). - pub fn create_texture(&self, data: TextureData) -> Texture { - Texture::new(data, &self.render) - } -} - -/// The context's limits. -#[derive(Clone, Copy, Default)] -pub(crate) struct Limits { - pub min_delta_time: f32, -} - -/// Describes frame parameters. -#[derive(Clone, Copy, Default)] -pub struct FrameParameters { - /// Virtual pixels size in physical pixels. - pub pixel_size: PixelSize, - - /// The frame filter mode. - pub filter: FrameFilter, -} - -/// Virtual pixels size in physical pixels. -#[derive(Clone, Copy, Default)] -pub enum PixelSize { - Antialiasing, - #[default] - X1, - X2, - X3, - X4, -} diff --git a/dunge/src/element.rs b/dunge/src/element.rs deleted file mode 100644 index 26d37c7..0000000 --- a/dunge/src/element.rs +++ /dev/null @@ -1,50 +0,0 @@ -use {std::ops, winit::window::Window}; - -#[cfg(target_arch = "wasm32")] -use web_sys::Element as El; - -#[cfg(not(target_arch = "wasm32"))] -type El = (); - -#[cfg_attr(not(target_arch = "wasm32"), derive(Default))] -pub(crate) struct Element(El); - -impl Element { - #[cfg(target_arch = "wasm32")] - pub fn new(inner: El) -> Self { - Self(inner) - } - - pub fn set_window_size(&self, window: &Window) { - #[cfg(target_arch = "wasm32")] - { - use winit::dpi::PhysicalSize; - - let new_size = { - let width = self.client_width().max(1) as u32; - let height = self.client_height().max(1) as u32; - PhysicalSize { width, height } - }; - - if new_size == window.inner_size() { - return; - } - - window.set_inner_size(new_size); - } - - #[cfg(not(target_arch = "wasm32"))] - { - _ = self; - _ = window; - } - } -} - -impl ops::Deref for Element { - type Target = El; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} diff --git a/dunge/src/frame.rs b/dunge/src/frame.rs deleted file mode 100644 index f95c5bd..0000000 --- a/dunge/src/frame.rs +++ /dev/null @@ -1,194 +0,0 @@ -use { - crate::{ - framebuffer::Framebuffer, - layer::{ActiveLayer, Builder, Layer}, - pipeline::Pipeline, - posteffect::PostEffect, - postproc::PostProcessor, - render::State, - screen::RenderScreen, - shader_data::Instance, - }, - wgpu::{CommandEncoder, TextureView}, -}; - -/// The type that represented a current frame -/// and creates new [layers](crate::ActiveLayer). -pub struct Frame<'d> { - shot: Snapshot<'d>, - view: TextureView, - encoder: Encoder, - drawn: bool, -} - -impl<'d> Frame<'d> { - pub(crate) fn new(shot: Snapshot<'d>, view: TextureView) -> Self { - Self { - shot, - view, - encoder: Encoder::default(), - drawn: false, - } - } - - /// Starts the [layer](crate::Layer). - pub fn layer<'l, S, T>(&'l mut self, layer: &'l Layer) -> Builder<'d, 'l, S, T> { - Builder::new(self, layer.pipeline()) - } - - pub(crate) fn start_layer<'l, S, T>( - &'l mut self, - pipeline: &'l Pipeline, - clear_color: Option<[f64; 4]>, - clear_depth: bool, - ) -> ActiveLayer<'l, S, T> { - use wgpu::*; - - let shot = &self.shot; - - // Before start a new layer, finish the previous one if it exists - self.encoder.finish(shot.state); - self.drawn = false; - - let mut pass = self - .encoder - .get(shot.state) - .begin_render_pass(&RenderPassDescriptor { - label: None, - color_attachments: &[Some(RenderPassColorAttachment { - view: shot.framebuffer.render_view(), - resolve_target: None, - ops: Operations { - load: clear_color.map_or(LoadOp::Load, |[r, g, b, a]| { - LoadOp::Clear(Color { r, g, b, a }) - }), - store: StoreOp::Store, - }, - })], - depth_stencil_attachment: Some(RenderPassDepthStencilAttachment { - view: shot.framebuffer.depth_view(), - depth_ops: Some(Operations { - load: if clear_depth { - LoadOp::Clear(1.) - } else { - LoadOp::Load - }, - store: StoreOp::Store, - }), - stencil_ops: None, - }), - timestamp_writes: None, - occlusion_query_set: None, - }); - - let view_size = shot.screen.virtual_size_with_antialiasing().as_vec2(); - pass.set_viewport(0., 0., view_size.x, view_size.y, 0., 1.); - pass.set_pipeline(pipeline.as_ref()); - ActiveLayer::new( - pass, - shot.screen.virtual_size().into(), - pipeline.slots(), - shot.instance, - ) - } - - /// Writes a final frame on the screen. - /// - /// When you call this, you may experience problems with borrowing frame references. - /// This is intentional. You should drop the layer object before calling this method. - /// - /// To apply a [post-effect](PostEffect) to the frame, call the - /// [`draw_on_screen_with`](Frame::draw_on_screen_with) method. - pub fn draw_on_screen(&mut self) { - self.draw(None); - } - - /// Writes a final frame on the screen with the [post-effect](PostEffect). - pub fn draw_on_screen_with(&mut self, ef: &PostEffect) { - self.draw(Some(ef)); - } - - fn draw(&mut self, ef: Option<&PostEffect>) { - use wgpu::*; - - // Skip render if not needed - if self.drawn { - log::info!("draw frame (skipped)"); - return; - } - - log::info!("draw frame"); - let shot = &mut self.shot; - let locked_postproc; - { - let mut pass = self - .encoder - .get(shot.state) - .begin_render_pass(&RenderPassDescriptor { - label: None, - color_attachments: &[Some(RenderPassColorAttachment { - view: &self.view, - resolve_target: None, - ops: Operations { - load: LoadOp::Load, - store: StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - - let params = shot.screen.frame_parameters(); - let postproc: &PostProcessor = if let Some(ef) = ef { - locked_postproc = ef.with_parameters(shot.state, params); - &locked_postproc - } else { - shot.postproc.set_parameters(shot.state, params); - shot.postproc - }; - - pass.set_pipeline(postproc.render_pipeline()); - pass.set_bind_group(PostProcessor::DATA_GROUP, postproc.data_bind_group(), &[]); - pass.set_bind_group( - PostProcessor::TEXTURE_GROUP, - postproc.render_bind_group(shot.state, shot.framebuffer.render_view()), - &[], - ); - - pass.draw(0..4, 0..1); - } - - self.encoder.finish(shot.state); - self.drawn = true; - } -} - -pub(crate) struct Snapshot<'d> { - pub state: &'d State, - pub framebuffer: &'d Framebuffer, - pub postproc: &'d mut PostProcessor, - pub screen: RenderScreen, - pub instance: &'d Instance, -} - -#[derive(Default)] -struct Encoder(Option); - -impl Encoder { - fn get(&mut self, state: &State) -> &mut CommandEncoder { - use wgpu::CommandEncoderDescriptor; - - self.0.get_or_insert_with(|| { - state - .device() - .create_command_encoder(&CommandEncoderDescriptor::default()) - }) - } - - fn finish(&mut self, state: &State) { - if let Some(encoder) = self.0.take() { - state.queue().submit([encoder.finish()]); - } - } -} diff --git a/dunge/src/framebuffer/buffer.rs b/dunge/src/framebuffer/buffer.rs deleted file mode 100644 index bcf862b..0000000 --- a/dunge/src/framebuffer/buffer.rs +++ /dev/null @@ -1,69 +0,0 @@ -use { - crate::{ - framebuffer::{depth_frame::DepthFrame, render_frame::RenderFrame}, - render::State, - }, - wgpu::{TextureFormat, TextureView}, -}; - -pub(crate) struct Framebuffer { - depth: DepthFrame, - render: RenderFrame, - size: BufferSize, -} - -impl Framebuffer { - pub const DEPTH_FORMAT: TextureFormat = DepthFrame::FORMAT; - pub const RENDER_FORMAT: TextureFormat = RenderFrame::FORMAT; - - pub fn new(state: &State) -> Self { - const DEFAULT_SIZE: BufferSize = BufferSize::MIN; - - Self { - depth: DepthFrame::new(DEFAULT_SIZE, state), - render: RenderFrame::new(DEFAULT_SIZE, state), - size: DEFAULT_SIZE, - } - } - - pub fn set_size(&mut self, size: BufferSize, state: &State) { - if self.size == size { - return; - } - - self.depth = DepthFrame::new(size, state); - self.render = RenderFrame::new(size, state); - self.size = size; - } - - pub fn render_view(&self) -> &TextureView { - self.render.view() - } - - pub fn depth_view(&self) -> &TextureView { - self.depth.view() - } -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) struct BufferSize(pub u32, pub u32); - -impl BufferSize { - pub(crate) const MIN: Self = Self(1, 1); - - pub(crate) fn new(width: u32, height: u32, max_size: u32) -> Self { - Self(width.clamp(1, max_size), height.clamp(1, max_size)) - } -} - -impl Default for BufferSize { - fn default() -> Self { - Self::MIN - } -} - -impl From for (f32, f32) { - fn from(BufferSize(width, height): BufferSize) -> Self { - (width as f32, height as f32) - } -} diff --git a/dunge/src/framebuffer/depth_frame.rs b/dunge/src/framebuffer/depth_frame.rs deleted file mode 100644 index 38cecdf..0000000 --- a/dunge/src/framebuffer/depth_frame.rs +++ /dev/null @@ -1,39 +0,0 @@ -use { - crate::{framebuffer::buffer::BufferSize, render::State}, - wgpu::{TextureFormat, TextureView}, -}; - -pub(crate) struct DepthFrame { - view: TextureView, -} - -impl DepthFrame { - pub const FORMAT: TextureFormat = TextureFormat::Depth24Plus; - - pub fn new(BufferSize(width, height): BufferSize, state: &State) -> Self { - use wgpu::*; - - let device = state.device(); - let texture = device.create_texture(&TextureDescriptor { - label: None, - size: Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D2, - format: Self::FORMAT, - usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }); - - let view = texture.create_view(&TextureViewDescriptor::default()); - Self { view } - } - - pub fn view(&self) -> &TextureView { - &self.view - } -} diff --git a/dunge/src/framebuffer/render_frame.rs b/dunge/src/framebuffer/render_frame.rs deleted file mode 100644 index 5838d88..0000000 --- a/dunge/src/framebuffer/render_frame.rs +++ /dev/null @@ -1,42 +0,0 @@ -use { - crate::{framebuffer::buffer::BufferSize, render::State}, - wgpu::{TextureFormat, TextureView}, -}; - -pub(crate) struct RenderFrame { - view: TextureView, -} - -impl RenderFrame { - pub const FORMAT: TextureFormat = if cfg!(target_os = "linux") || cfg!(target_os = "windows") { - TextureFormat::Bgra8UnormSrgb - } else { - TextureFormat::Rgba8UnormSrgb - }; - - pub fn new(BufferSize(width, height): BufferSize, state: &State) -> Self { - use wgpu::*; - - let texture = state.device().create_texture(&TextureDescriptor { - label: None, - size: Extent3d { - width, - height, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D2, - format: Self::FORMAT, - usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }); - - let view = texture.create_view(&TextureViewDescriptor::default()); - Self { view } - } - - pub fn view(&self) -> &TextureView { - &self.view - } -} diff --git a/dunge/src/layer.rs b/dunge/src/layer.rs deleted file mode 100644 index 14d25a6..0000000 --- a/dunge/src/layer.rs +++ /dev/null @@ -1,283 +0,0 @@ -use { - crate::{ - color::{Color, Rgba}, - frame::Frame, - mesh::{Mesh, MeshBuffer}, - pipeline::{Inputs, Parameters as PipelineParameters, Pipeline, Slots}, - render::State, - shader::{Shader, ShaderInfo}, - shader_data::{Globals, Instance, InstanceColor, Lights, Spaces, Textures}, - topology::{Topology, TriangleList}, - }, - dunge_shader::Shader as ShaderData, - std::marker::PhantomData, - wgpu::{BindGroup, RenderPass}, -}; - -/// The drawable layer of the [frame](Frame). -/// -/// Can be created from the [context](crate::Context) by calling -/// the [`create_layer`](crate::Context::create_layer) or -/// [`create_layer_with`](crate::Context::create_layer_with) functions. -pub struct Layer { - pipeline: Box, - ty: PhantomData<(S, T)>, -} - -impl Layer { - pub(crate) fn new(state: &State, shader: &ShaderData, params: PipelineParameters) -> Self - where - S: Shader, - T: Topology, - { - let inputs = Inputs::new::(S::INSTANCE_COLORS); - Self { - pipeline: Box::new(Pipeline::new( - state, - shader, - Some(&inputs), - PipelineParameters { - topology: T::VALUE.into_inner(), - ..params - }, - )), - ty: PhantomData, - } - } - - pub(crate) fn pipeline(&self) -> &Pipeline { - &self.pipeline - } -} - -/// The frame's active layer. -/// -/// Can be created from the [`Frame`] instance by calling the [`layer`](Frame::layer) function. -pub struct ActiveLayer<'l, S, T> { - pass: RenderPass<'l>, - size: (u32, u32), - slots: Slots, - instance: &'l Instance, - default_instance: &'l Instance, - instance_color: Option<&'l InstanceColor>, - groups: Groups<'l>, - ty: PhantomData<(S, T)>, -} - -impl<'l, S, T> ActiveLayer<'l, S, T> { - pub(crate) fn new( - pass: RenderPass<'l>, - size: (u32, u32), - slots: Slots, - instance: &'l Instance, - ) -> Self { - Self { - pass, - size, - slots, - instance, - default_instance: instance, - instance_color: None, - groups: Groups::default(), - ty: PhantomData, - } - } - - /// Binds the globals. - pub fn bind_globals(&mut self, globals: &'l Globals) -> &mut Self { - globals.update_size(self.size); - self.groups.globals = Some(globals.bind()); - self - } - - /// Binds the textures. - pub fn bind_textures(&mut self, textures: &'l Textures) -> &mut Self { - self.groups.textures = Some(textures.bind()); - self - } - - /// Binds the light sources. - pub fn bind_lights(&mut self, lights: &'l Lights) -> &mut Self { - self.groups.lights = Some(lights.bind()); - self - } - - /// Binds the light spaces. - pub fn bind_spaces(&mut self, spaces: &'l Spaces) -> &mut Self { - self.groups.spaces = Some(spaces.bind()); - self - } - - /// Binds the instance. - pub fn bind_instance(&mut self, instance: &'l Instance) -> &mut Self { - self.instance = instance; - self - } - - /// Binds the default instance with a position at (0, 0, 0) in world coordinates. - /// - /// After the layer [starts](Frame::layer), this instance is bound by default, - /// so there is no need to additionally call this method. - pub fn bind_default_instance(&mut self) -> &mut Self { - self.instance = self.default_instance; - self - } - - /// Binds the color instance. - /// - /// # Panics - /// Panics if the shader has no instance colors. - pub fn bind_instance_color(&mut self, cols: &'l InstanceColor) -> &mut Self - where - S: Shader, - { - let info = ShaderInfo::new::(); - assert!( - info.has_instance_colors(), - "the shader has no instance colors", - ); - - self.instance_color = Some(cols); - self - } - - /// Draws the [mesh](crate::Mesh). - /// - /// # Panics - /// - If globals is not set but required. - /// - If textures is not set but required. - /// - If light sources is not set but required. - /// - If light spaces is not set but required. - /// - If instance color is not set but required. - pub fn draw(&mut self, mesh: &'l Mesh) - where - S: Shader, - T: Topology, - { - self.set_bind_groups(); - self.draw_mesh(mesh.buffer(None)); - } - - /// Draws the [mesh](crate::Mesh) partially with a limit of drawn elements. - /// - /// For example, pass the `limit` value of 2 to draw only two triangles - /// (for a triangular mesh) instead of the entire mesh. - /// - /// # Panics - /// See [`draw`](ActiveLayer::draw) method. - pub fn draw_limited(&mut self, mesh: &'l Mesh, limit: u32) - where - S: Shader, - T: Topology, - { - self.set_bind_groups(); - self.draw_mesh(mesh.buffer(Some(limit))); - } - - fn set_bind_groups(&mut self) - where - S: Shader, - { - let info = ShaderInfo::new::(); - if info.has_globals() { - let (index, group) = self.groups.globals.expect("globals is not set"); - self.pass.set_bind_group(index, group, &[]); - } - - if info.has_textures() { - let (index, group) = self.groups.textures.expect("textures is not set"); - self.pass.set_bind_group(index, group, &[]); - } - - if info.has_lights() { - let (index, group) = self.groups.lights.expect("light sources is not set"); - self.pass.set_bind_group(index, group, &[]); - } - - if info.has_spaces() { - let (index, group) = self.groups.spaces.expect("light spaces is not set"); - self.pass.set_bind_group(index, group, &[]); - } - - if info.has_instance_colors() { - let instance_color = self.instance_color.expect("instance color is not set"); - self.pass - .set_vertex_buffer(self.slots.instance_color, instance_color.buffer().slice()); - } - } - - fn draw_mesh(&mut self, mesh: MeshBuffer<'l>) { - use wgpu::IndexFormat; - - let instance = self.instance.buffer(); - - self.pass - .set_vertex_buffer(self.slots.instance, instance.slice()); - - self.pass - .set_vertex_buffer(self.slots.vertex, mesh.verts.slice()); - - match mesh.indxs { - Some(buf) => { - self.pass.set_index_buffer(buf.slice(), IndexFormat::Uint16); - self.pass.draw_indexed(0..buf.len(), 0, 0..instance.len()); - } - None => self.pass.draw(0..mesh.verts.len(), 0..instance.len()), - } - } -} - -#[derive(Default)] -struct Groups<'l> { - globals: Option<(u32, &'l BindGroup)>, - textures: Option<(u32, &'l BindGroup)>, - lights: Option<(u32, &'l BindGroup)>, - spaces: Option<(u32, &'l BindGroup)>, -} - -/// The layer builder. It creates a configured [`ActiveLayer`]. -#[must_use] -pub struct Builder<'d, 'l, S, T> { - frame: &'l mut Frame<'d>, - pipeline: &'l Pipeline, - clear_color: Option<[f64; 4]>, - clear_depth: bool, - vertex_type: PhantomData<(S, T)>, -} - -impl<'d, 'l, S, T> Builder<'d, 'l, S, T> { - pub(crate) fn new(frame: &'l mut Frame<'d>, pipeline: &'l Pipeline) -> Self { - Self { - frame, - pipeline, - clear_color: None, - clear_depth: false, - vertex_type: PhantomData, - } - } - - /// Sets clear color for the layer. - /// - /// Don't set this setting if you don't want to fill - /// the previous layer or frame with some color. - pub fn with_clear_color(self, Color(col): Rgba) -> Self { - Self { - clear_color: Some(col.map(f64::from)), - ..self - } - } - - /// Sets the flag to clear the depth buffer or not for the layer. - pub fn with_clear_depth(self) -> Self { - Self { - clear_depth: true, - ..self - } - } - - /// Starts draw the layer. - pub fn start(self) -> ActiveLayer<'l, S, T> { - self.frame - .start_layer(self.pipeline, self.clear_color, self.clear_depth) - } -} diff --git a/dunge/src/lib.rs b/dunge/src/lib.rs index 0ebed02..8b13789 100644 --- a/dunge/src/lib.rs +++ b/dunge/src/lib.rs @@ -1,115 +1 @@ -mod buffer; -mod canvas; -mod color; -mod context; -mod element; -mod frame; -mod layer; -mod r#loop; -mod mesh; -mod pipeline; -mod posteffect; -mod postproc; -mod render; -mod scheme; -mod screen; -pub mod shader; -mod time; -pub mod topology; -mod transform; -pub mod vertex; -mod view; -pub mod error { - //! Error types. - - pub use crate::{ - canvas::Error as CanvasError, - mesh::{Error as MeshError, UpdateError as MeshUpdateError}, - shader_data::{ - DataError, InvalidInstanceSize, InvalidMapSize, LightsUpdateError, SpacesUpdateError, - }, - }; -} - -mod framebuffer { - mod buffer; - mod depth_frame; - mod render_frame; - - pub(crate) use self::buffer::{BufferSize, Framebuffer}; -} - -pub mod input { - //! User's input types. - - pub use crate::r#loop::{Input, Key, Keys, KeysIterator, Mouse}; -} - -mod shader_data { - mod ambient; - mod data; - pub(crate) mod globals; - mod instance; - mod len; - pub(crate) mod lights; - mod post; - mod source; - mod space; - pub(crate) mod spaces; - pub(crate) mod texture; - pub(crate) mod textures; - - pub(crate) use self::post::PostShaderData; - - pub use self::{ - data::{Error as DataError, Format, SpaceData, TextureData}, - globals::{Builder as GlobalsBuilder, Globals}, - instance::{ - Instance, InstanceColor, InvalidSize as InvalidInstanceSize, ModelColor, ModelTransform, - }, - lights::{Builder as LightsBuilder, Lights, UpdateError as LightsUpdateError}, - source::Source, - space::Space, - spaces::{Builder as SpacesBuilder, Spaces, UpdateError as SpacesUpdateError}, - texture::{InvalidSize as InvalidMapSize, Texture}, - textures::{Builder as TexturesBuilder, Map as MapParameter, Textures}, - }; -} - -#[cfg(not(target_arch = "wasm32"))] -pub use crate::canvas::window::{make_window, InitialState, WindowMode}; - -#[cfg(target_arch = "wasm32")] -pub use crate::canvas::from_element; - -#[cfg(target_os = "android")] -pub use crate::canvas::android::from_app; - -pub use { - crate::{ - canvas::{Backend, Canvas, CanvasConfig, Device, Info, Selector}, - color::{Color, Rgb, Rgba}, - context::{Context, FrameParameters, PixelSize}, - frame::Frame, - input::Input, - layer::{ActiveLayer, Builder as ActiveLayerBuilder, Layer}, - mesh::{Data as MeshData, Mesh}, - pipeline::{Blend, Compare, DrawMode, LayerBuilder}, - posteffect::{Builder as PostEffectBuilder, PostEffect}, - postproc::FrameFilter, - r#loop::Loop, - scheme::Scheme, - shader::Shader, - shader_data::{ - Format, Globals, GlobalsBuilder, Instance, InstanceColor, Lights, LightsBuilder, - MapParameter, ModelColor, ModelTransform, Source, Space, SpaceData, Spaces, - SpacesBuilder, Texture, TextureData, Textures, TexturesBuilder, - }, - transform::Transform, - vertex::Vertex, - view::{Orthographic, Perspective, Projection, View, ViewHandle}, - }, - dunge_macros::Vertex, - glam, winit, -}; diff --git a/dunge/src/loop.rs b/dunge/src/loop.rs deleted file mode 100644 index 0e210ae..0000000 --- a/dunge/src/loop.rs +++ /dev/null @@ -1,127 +0,0 @@ -pub use winit::keyboard::KeyCode as Key; - -use { - crate::{context::Context, frame::Frame}, - std::{iter, slice}, -}; - -/// The main application loop. -pub trait Loop { - /// Calls before render a frame to update the state. - /// - /// It accepts the [`Context`] and the [`Input`]. - /// The context uses to update the application state, create or delete any resources. - /// The input contains an inputed data like a mouse position and etc. - fn update(&mut self, context: &mut Context, input: &Input); - - /// Calls on render a frame. - /// - /// It accepts a [`Frame`] to draw something on the canvas. - fn render(&self, frame: &mut Frame); - - /// Calls when a close is requested. - /// - /// Returns a flag whether to terminate the main loop or not. - fn close_requested(&mut self) -> bool { - true - } -} - -impl Loop for &mut L -where - L: Loop, -{ - fn update(&mut self, context: &mut Context, input: &Input) { - (**self).update(context, input); - } - - fn render(&self, frame: &mut Frame) { - (**self).render(frame); - } - - fn close_requested(&mut self) -> bool { - (**self).close_requested() - } -} - -impl Loop for Box -where - L: Loop, -{ - fn update(&mut self, context: &mut Context, input: &Input) { - self.as_mut().update(context, input); - } - - fn render(&self, frame: &mut Frame) { - self.as_ref().render(frame); - } - - fn close_requested(&mut self) -> bool { - self.as_mut().close_requested() - } -} - -/// User input. -pub struct Input<'a> { - /// Seconds since previous [update](crate::Loop::update) was called. - pub delta_time: f32, - - /// The cursor XY position on the screen. - /// [`None`] if the cursor out of screen. - pub cursor_position: Option<(f32, f32)>, - - /// The mouse input. - pub mouse: Mouse, - - /// The pressed keys. - pub pressed_keys: Keys<'a>, - - /// The released keys. - pub released_keys: Keys<'a>, -} - -/// The mouse input. -#[derive(Clone, Copy, Default)] -pub struct Mouse { - pub motion_delta: (f32, f32), - pub wheel_delta: (f32, f32), - pub pressed_left: bool, - pub pressed_middle: bool, - pub pressed_right: bool, -} - -/// Keys input. -#[derive(Clone, Copy)] -pub struct Keys<'a> { - pub(crate) keys: &'a [Key], -} - -impl<'a> IntoIterator for Keys<'a> { - type Item = Key; - type IntoIter = KeysIterator<'a>; - - fn into_iter(self) -> Self::IntoIter { - KeysIterator { - iter: self.keys.iter().copied(), - } - } -} - -/// An iterator over [keys](Key). -pub struct KeysIterator<'a> { - iter: iter::Copied>, -} - -impl Iterator for KeysIterator<'_> { - type Item = Key; - - fn next(&mut self) -> Option { - self.iter.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl ExactSizeIterator for KeysIterator<'_> {} diff --git a/dunge/src/mesh.rs b/dunge/src/mesh.rs deleted file mode 100644 index 1aeece4..0000000 --- a/dunge/src/mesh.rs +++ /dev/null @@ -1,231 +0,0 @@ -use { - crate::{ - buffer::BufferView, - render::State, - topology::{Topology, TriangleList}, - vertex::{self, Vertex}, - }, - std::{borrow::Cow, marker::PhantomData, sync::Arc}, - wgpu::{Buffer, Queue}, -}; - -/// A data struct for a mesh creation. -#[derive(Clone)] -pub struct Data<'a, V, T = TriangleList> -where - T: Topology, -{ - verts: &'a [V], - indxs: Option>, -} - -impl<'a, V, T> Data<'a, V, T> -where - T: Topology, -{ - /// Creates a new [`MeshData`](crate::MeshData) from given vertices. - pub fn from_verts(verts: &'a [V]) -> Self { - Self { verts, indxs: None } - } -} - -impl<'a, V> Data<'a, V> { - /// Creates a new [`MeshData`](crate::MeshData) from given vertices and indices. - /// - /// # Errors - /// Will return - /// - [`MeshError::TooManyVertices`](crate::error::MeshError::TooManyVertices) - /// if vertices length doesn't fit in `u16`. - /// - [`MeshError::WrongIndex`](crate::error::MeshError::WrongIndex) - /// if the vertex index is out of bounds of the vertex slice. - pub fn new(verts: &'a [V], indxs: &'a [[u16; 3]]) -> Result { - let len: u16 = verts.len().try_into().map_err(|_| Error::TooManyVertices)?; - if indxs.iter().flatten().any(|&i| i >= len) { - return Err(Error::WrongIndex); - } - - Ok(Self { - verts, - indxs: Some(Cow::Borrowed(indxs)), - }) - } - - /// Creates a new [`MeshData`](crate::MeshData) from given quadrangles. - /// - /// # Errors - /// Will return - /// - [`MeshError::TooManyVertices`](crate::error::MeshError::TooManyVertices) - /// if vertices length doesn't fit in `u16`. - pub fn from_quads(verts: &'a [[V; 4]]) -> Result { - use std::slice; - - let new_len = verts.len() * 4; - let len: u16 = new_len.try_into().map_err(|_| Error::TooManyVertices)?; - - Ok(Self { - verts: unsafe { slice::from_raw_parts(verts.as_ptr().cast(), new_len) }, - indxs: Some( - (0..len) - .step_by(4) - .flat_map(|i| [[i, i + 1, i + 2], [i, i + 2, i + 3]]) - .collect(), - ), - }) - } -} - -/// An error returned from the [`MeshData`](crate::MeshData) constructors. -#[derive(Debug)] -pub enum Error { - /// Vertices length doesn't fit in `u16`. - TooManyVertices, - - /// The vertex index is out of bounds of the vertex slice. - WrongIndex, -} - -/// The mesh object. -pub struct Mesh { - verts: Buffer, - indxs: Option, - queue: Arc, - ty: PhantomData<(V, T)>, -} - -impl Mesh { - pub(crate) fn new(data: &Data, state: &State) -> Self - where - V: Vertex, - T: Topology, - { - use wgpu::{ - util::{BufferInitDescriptor, DeviceExt}, - BufferUsages, - }; - - let device = state.device(); - Self { - verts: device.create_buffer_init(&BufferInitDescriptor { - label: Some("vertex buffer"), - contents: vertex::verts_as_bytes(data.verts), - usage: BufferUsages::VERTEX | BufferUsages::COPY_DST, - }), - indxs: data.indxs.as_deref().map(|indxs| { - device.create_buffer_init(&BufferInitDescriptor { - label: Some("index buffer"), - contents: bytemuck::cast_slice(indxs), - usage: BufferUsages::INDEX | BufferUsages::COPY_DST, - }) - }), - queue: Arc::clone(state.queue()), - ty: PhantomData, - } - } - - /// Updates the mesh with a new [data](`Data`). - /// - /// # Errors - /// Will return - /// - [`MeshUpdateError::VertexSize`](crate::error::MeshUpdateError::VertexSize) - /// if a slice of vertices is passed with a wrong length. - /// - [`MeshUpdateError::IndexSize`](crate::error::MeshUpdateError::IndexSize) - /// if a slice of indices is passed with a wrong length. - /// - [`MeshUpdateError::NoIndices`](crate::error::MeshUpdateError::NoIndices) - /// if no indices are passed but they are required. - pub fn update(&mut self, data: &Data) -> Result<(), UpdateError> - where - V: Vertex, - T: Topology, - { - let verts = data.verts; - if self.verts.size() != verts.len() as u64 { - return Err(UpdateError::VertexSize); - } - - if let Some(indxs) = &data.indxs { - let buf = self.indxs.as_ref().ok_or(UpdateError::NoIndices)?; - if buf.size() != indxs.len() as u64 { - return Err(UpdateError::IndexSize); - } - - self.queue.write_buffer(buf, 0, bytemuck::cast_slice(indxs)); - } - - self.queue - .write_buffer(&self.verts, 0, vertex::verts_as_bytes(verts)); - - Ok(()) - } - - /// Updates the mesh with new vertices. - /// - /// # Errors - /// Will return - /// - [`MeshUpdateError::VertexSize`](crate::error::MeshUpdateError::VertexSize) - /// if a slice of vertices is passed with a length longer than the length of the mesh. - pub fn update_verts(&mut self, verts: &[V]) -> Result<(), UpdateError> - where - V: Vertex, - T: Topology, - { - if self.verts.size() < verts.len() as u64 { - return Err(UpdateError::VertexSize); - } - - self.queue - .write_buffer(&self.verts, 0, vertex::verts_as_bytes(verts)); - - Ok(()) - } - - pub(crate) fn buffer(&self, limit: Option) -> MeshBuffer - where - T: Topology, - { - let limit = limit.map(|n| n * T::N as u32); - MeshBuffer { - verts: BufferView::new::(&self.verts, limit), - indxs: self - .indxs - .as_ref() - .map(|buf| BufferView::new::(buf, limit)), - } - } -} - -/// An error returned from the [`update`](Mesh::update) function. -#[derive(Debug)] -pub enum UpdateError { - /// A slice of vertices is passed with a wrong length. - VertexSize, - - /// A slice of indices is passed with a wrong length. - IndexSize, - - /// No indices are passed but they are required. - NoIndices, -} - -#[derive(Clone, Copy)] -pub(crate) struct MeshBuffer<'a> { - pub verts: BufferView<'a>, - pub indxs: Option>, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn from_quads() { - let verts = [[0, 1, 2, 3], [4, 5, 6, 7]]; - let data = Data::from_quads(&verts).expect("mesh data"); - let indxs = data.indxs.expect("indices"); - assert_eq!(data.verts.len(), 8); - assert_eq!(indxs.len(), 4); - assert_eq!([data.verts[0], data.verts[1], data.verts[2]], indxs[0]); - assert_eq!([data.verts[0], data.verts[2], data.verts[3]], indxs[1]); - assert_eq!([data.verts[4], data.verts[5], data.verts[6]], indxs[2]); - assert_eq!([data.verts[4], data.verts[6], data.verts[7]], indxs[3]); - } -} diff --git a/dunge/src/pipeline.rs b/dunge/src/pipeline.rs deleted file mode 100644 index bbc7b5a..0000000 --- a/dunge/src/pipeline.rs +++ /dev/null @@ -1,550 +0,0 @@ -use { - crate::{ - framebuffer::Framebuffer, - layer::Layer, - render::State, - scheme::Scheme, - shader::Shader, - shader_data::{ModelColor, ModelTransform}, - topology::Topology, - vertex::Vertex, - }, - dunge_shader::{Layout, Shader as ShaderData, SourceBindings, SpaceBindings, TextureBindings}, - std::marker::PhantomData, - wgpu::{ - BindGroupLayout, BlendState, CompareFunction, PolygonMode, PrimitiveTopology, - RenderPipeline, VertexAttribute, VertexBufferLayout, VertexFormat, - }, -}; - -#[derive(Clone, Copy)] -pub(crate) struct Parameters { - pub blend: BlendState, - pub topology: PrimitiveTopology, - pub cull_faces: bool, - pub mode: PolygonMode, - pub depth_stencil: Option, -} - -impl Default for Parameters { - fn default() -> Self { - Self { - blend: BlendState::REPLACE, - topology: PrimitiveTopology::TriangleList, - cull_faces: true, - mode: PolygonMode::Fill, - depth_stencil: Some(CompareFunction::LessEqual), - } - } -} - -/// Layer's blending. -#[derive(Clone, Copy)] -pub enum Blend { - Replace, - AlphaBlending, -} - -/// Type of drawing mode for polygons. -#[derive(Clone, Copy)] -pub enum DrawMode { - Fill, - Line, - Point, -} - -/// Depth comparison function. -#[derive(Clone, Copy)] -pub enum Compare { - /// Function never passes. - Never, - - /// Function passes if new value less than existing value. - Less, - - /// Function passes if new value is greater than existing value. - Greater, - - /// Function always passes. - Always, -} - -/// Builds new layer with specific parameters. -#[must_use] -pub struct LayerBuilder<'a, S, T> { - state: &'a State, - params: Parameters, - vertex_type: PhantomData<(S, T)>, -} - -impl<'a, S, T> LayerBuilder<'a, S, T> { - pub(crate) fn new(state: &'a State) -> Self { - Self { - state, - params: Parameters::default(), - vertex_type: PhantomData, - } - } - - pub fn with_blend(mut self, blend: Blend) -> Self { - self.params.blend = match blend { - Blend::Replace => BlendState::REPLACE, - Blend::AlphaBlending => BlendState::ALPHA_BLENDING, - }; - - self - } - - pub fn with_cull_faces(mut self, cull: bool) -> Self { - self.params.cull_faces = cull; - self - } - - pub fn with_draw_mode(mut self, mode: DrawMode) -> Self { - self.params.mode = match mode { - DrawMode::Fill => PolygonMode::Fill, - DrawMode::Line => PolygonMode::Line, - DrawMode::Point => PolygonMode::Point, - }; - - self - } - - pub fn with_depth_compare(mut self, cmp: Compare) -> Self { - self.params.depth_stencil = Some(match cmp { - Compare::Never => CompareFunction::Never, - Compare::Less => CompareFunction::Less, - Compare::Greater => CompareFunction::Greater, - Compare::Always => CompareFunction::Always, - }); - - self - } - - /// Builds a new layer. - pub fn build(self, scheme: &Scheme) -> Layer - where - S: Shader, - T: Topology, - { - Layer::new(self.state, scheme.data(), self.params) - } -} - -pub(crate) struct Pipeline { - inner: RenderPipeline, - instances: Instances, - groups: Groups, -} - -impl Pipeline { - pub fn new( - state: &State, - shader: &ShaderData, - inputs: Option<&Inputs>, - params: Parameters, - ) -> Self { - use wgpu::*; - - let device = state.device(); - let module = device.create_shader_module(ShaderModuleDescriptor { - label: None, - source: ShaderSource::Wgsl(shader.source.as_str().into()), - }); - - let groups = Groups::new(state, &shader.layout); - let layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { - label: None, - bind_group_layouts: &groups.layouts(), - push_constant_ranges: &[], - }); - - Self { - inner: device.create_render_pipeline(&RenderPipelineDescriptor { - label: None, - layout: Some(&layout), - vertex: VertexState { - module: &module, - entry_point: ShaderData::VERTEX_ENTRY_POINT, - buffers: &inputs.map(Inputs::buffer_layouts).unwrap_or_default(), - }, - fragment: Some(FragmentState { - module: &module, - entry_point: ShaderData::FRAGMENT_ENTRY_POINT, - targets: &[Some(ColorTargetState { - format: Framebuffer::RENDER_FORMAT, - blend: Some(params.blend), - write_mask: ColorWrites::ALL, - })], - }), - primitive: PrimitiveState { - topology: params.topology, - strip_index_format: None, - front_face: FrontFace::Ccw, - cull_mode: params.cull_faces.then_some(Face::Back), - polygon_mode: params.mode, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: params.depth_stencil.map(|depth_compare| DepthStencilState { - format: Framebuffer::DEPTH_FORMAT, - depth_write_enabled: true, - depth_compare, - stencil: StencilState::default(), - bias: DepthBiasState::default(), - }), - multisample: MultisampleState::default(), - multiview: None, - }), - instances: Instances { - color: inputs.is_some_and(|inputs| inputs.instance_color), - }, - groups, - } - } - - pub fn as_ref(&self) -> &RenderPipeline { - &self.inner - } - - pub fn slots(&self) -> Slots { - self.instances.slots() - } - - pub fn globals(&self) -> Option<&GroupLayout> { - self.groups.globals.as_ref() - } - - pub fn textures(&self) -> Option<&GroupLayout> { - self.groups.textures.as_ref() - } - - pub fn lights(&self) -> Option<&GroupLayout> { - self.groups.lights.as_ref() - } - - pub fn spaces(&self) -> Option<&GroupLayout> { - self.groups.spaces.as_ref() - } -} - -#[derive(Clone, Copy)] -struct Instances { - color: bool, -} - -impl Instances { - fn slots(self) -> Slots { - Slots { - instance: 0, - instance_color: 1, - vertex: 1 + self.color as u32, - } - } -} - -pub(crate) struct Slots { - pub instance: u32, - pub instance_color: u32, - pub vertex: u32, -} - -struct Groups { - globals: Option>, - textures: Option>, - lights: Option>, - spaces: Option>, -} - -impl Groups { - fn new(state: &State, layout: &Layout) -> Self { - use wgpu::*; - - let device = state.device(); - let entry = |binding, visibility| BindGroupLayoutEntry { - binding, - visibility, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }; - - Self { - globals: { - let mut entries = vec![]; - if let Some(binding) = layout.globals.bindings.post_data { - entries.push(entry( - binding, - ShaderStages::VERTEX | ShaderStages::FRAGMENT, - )); - } - - if let Some(binding) = layout.globals.bindings.camera { - entries.push(entry(binding, ShaderStages::VERTEX)); - } - - if let Some(binding) = layout.globals.bindings.ambient { - entries.push(entry(binding, ShaderStages::FRAGMENT)); - } - - entries.first().map(|_| GroupLayout { - layout: device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: Some("globals binding"), - entries: &entries, - }), - bindings: Globals { - group: layout.globals.num, - camera: layout.globals.bindings.camera.unwrap_or_default(), - ambient: layout.globals.bindings.ambient.unwrap_or_default(), - }, - }) - }, - textures: 'textures: { - let tx = &layout.textures.bindings; - if tx.is_empty() { - break 'textures None; - } - - let mut entries = Vec::with_capacity(tx.map.tmaps.len() + 1); - for &tmap in &tx.map.tmaps { - entries.push(BindGroupLayoutEntry { - binding: tmap, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - view_dimension: TextureViewDimension::D2, - sample_type: TextureSampleType::Float { filterable: true }, - }, - count: None, - }); - } - - entries.push(BindGroupLayoutEntry { - binding: tx.map.smap, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Filtering), - count: None, - }); - - Some(GroupLayout { - layout: device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: Some("textures binding"), - entries: &entries, - }), - bindings: Textures { - group: layout.textures.num, - map: tx.map.clone(), - }, - }) - }, - lights: { - let entries: Vec<_> = layout - .lights - .bindings - .source_arrays - .iter() - .flat_map(|bindings| { - [ - entry(bindings.binding_array, ShaderStages::FRAGMENT), - entry(bindings.binding_len, ShaderStages::FRAGMENT), - ] - }) - .collect(); - - entries.first().map(|_| GroupLayout { - layout: device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: Some("lights binding"), - entries: &entries, - }), - bindings: Lights { - group: layout.lights.num, - source_arrays: layout.lights.bindings.source_arrays.clone(), - }, - }) - }, - spaces: 'spaces: { - let ls = &layout.spaces.bindings.light_spaces; - if ls.is_empty() { - break 'spaces None; - } - - let mut entries = vec![entry( - ls.spaces, - ShaderStages::VERTEX | ShaderStages::FRAGMENT, - )]; - - entries.extend(ls.tspaces.iter().map(|&binding| BindGroupLayoutEntry { - binding, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - multisampled: false, - view_dimension: TextureViewDimension::D3, - sample_type: TextureSampleType::Float { filterable: true }, - }, - count: None, - })); - - entries.push(BindGroupLayoutEntry { - binding: ls.sspace, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Filtering), - count: None, - }); - - Some(GroupLayout { - layout: device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: Some("spaces binding"), - entries: &entries, - }), - bindings: Spaces { - group: layout.spaces.num, - bindings: ls.clone(), - }, - }) - }, - } - } - - fn layouts(&self) -> Vec<&BindGroupLayout> { - [ - self.globals.as_ref().map(|group| &group.layout), - self.textures.as_ref().map(|group| &group.layout), - self.lights.as_ref().map(|group| &group.layout), - self.spaces.as_ref().map(|group| &group.layout), - ] - .into_iter() - .flatten() - .collect() - } -} - -pub(crate) struct GroupLayout { - pub layout: BindGroupLayout, - pub bindings: T, -} - -pub(crate) struct Globals { - pub group: u32, - pub camera: u32, - pub ambient: u32, -} - -pub(crate) struct Textures { - pub group: u32, - pub map: TextureBindings, -} - -pub(crate) struct Lights { - pub group: u32, - pub source_arrays: Vec, -} - -pub(crate) struct Spaces { - pub group: u32, - pub bindings: SpaceBindings, -} - -pub(crate) struct Inputs { - instance_color: bool, - vertex: VertexInputLayout, -} - -impl Inputs { - pub fn new(instance_color: bool) -> Self - where - V: Vertex, - { - let mut shader_location = ModelTransform::LAYOUT_ATTRIBUTES_LEN; - if instance_color { - shader_location += ModelColor::LAYOUT_ATTRIBUTES_LEN; - } - - Self { - instance_color, - vertex: VertexInputLayout::new::(shader_location), - } - } - - fn buffer_layouts(&self) -> Vec { - if self.instance_color { - vec![ - ModelTransform::LAYOUT, - ModelColor::LAYOUT, - self.vertex.buffer_layout(), - ] - } else { - vec![ModelTransform::LAYOUT, self.vertex.buffer_layout()] - } - } -} - -struct VertexInputLayout { - size: usize, - attrs: Vec, -} - -impl VertexInputLayout { - fn new(mut shader_location: u32) -> Self - where - V: Vertex, - { - use { - crate::vertex::{Component, Component2D, Component3D}, - std::mem, - }; - - let mut offset = 0; - let mut make_attr = |n| { - let format = Self::format(n); - let new_offset = Self::f32_offset(n); - let attr = VertexAttribute { - format, - offset, - shader_location, - }; - - offset += new_offset; - shader_location += 1; - attr - }; - - Self { - size: mem::size_of::(), - attrs: [ - Some(make_attr(V::Position::N_FLOATS)), - V::Color::OPTIONAL_N_FLOATS.map(&mut make_attr), - V::Texture::OPTIONAL_N_FLOATS.map(&mut make_attr), - ] - .into_iter() - .flatten() - .collect(), - } - } - - fn format(n: u64) -> VertexFormat { - match n { - 2 => VertexFormat::Float32x2, - 3 => VertexFormat::Float32x3, - _ => unreachable!(), - } - } - - fn f32_offset(n: u64) -> u64 { - use std::mem; - - n * mem::size_of::() as u64 - } - - fn buffer_layout(&self) -> VertexBufferLayout { - use wgpu::VertexStepMode; - - VertexBufferLayout { - array_stride: self.size as _, - step_mode: VertexStepMode::Vertex, - attributes: &self.attrs, - } - } -} diff --git a/dunge/src/posteffect.rs b/dunge/src/posteffect.rs deleted file mode 100644 index e728efd..0000000 --- a/dunge/src/posteffect.rs +++ /dev/null @@ -1,64 +0,0 @@ -use { - crate::{ - color::{Color, Rgb}, - postproc::{Parameters, PostProcessor}, - render::State, - }, - dunge_shader::Vignette, - std::sync::{Mutex, MutexGuard}, -}; - -/// The frame post-effect. -/// -/// Can be created from the [context](crate::Context) by calling -/// the [`posteffect_builder`](crate::Context::posteffect_builder) function. -pub struct PostEffect(Box); - -impl PostEffect { - pub(crate) fn vignette(state: &State, Color([r, g, b]): Rgb, force: f32) -> Self { - let inner = Inner { - postproc: Mutex::new(PostProcessor::new(state)), - vignette: [r, g, b, force], - }; - - Self(Box::new(inner)) - } - - pub(crate) fn with_parameters( - &self, - state: &State, - params: Parameters, - ) -> MutexGuard { - let Self(inner) = self; - let [r, g, b, f] = inner.vignette; - let params = Parameters { - vignette: Vignette::Color { r, g, b, f }, - ..params - }; - - let mut postproc = inner.postproc.lock().expect("lock"); - postproc.set_parameters(state, params); - postproc - } -} - -struct Inner { - postproc: Mutex, - vignette: [f32; 4], -} - -/// The [post-effect](PostEffect) builder. -pub struct Builder<'a> { - state: &'a State, -} - -impl<'a> Builder<'a> { - pub(crate) fn new(state: &'a State) -> Self { - Self { state } - } - - /// Builds a post-effect with the vignette effect. - pub fn vignette(self, col: Rgb, force: f32) -> PostEffect { - PostEffect::vignette(self.state, col, force) - } -} diff --git a/dunge/src/postproc.rs b/dunge/src/postproc.rs deleted file mode 100644 index 942cdbe..0000000 --- a/dunge/src/postproc.rs +++ /dev/null @@ -1,178 +0,0 @@ -use { - crate::{ - framebuffer::BufferSize, pipeline::Pipeline, render::State, shader_data::PostShaderData, - }, - dunge_shader::{PostScheme, TextureBindings, Vignette}, - glam::Vec2, - std::sync::OnceLock, - wgpu::{BindGroup, FilterMode, RenderPipeline, Sampler, TextureView}, -}; - -/// Describes a frame render filter mode. -#[derive(Clone, Copy, Default, PartialEq, Eq)] -pub enum FrameFilter { - #[default] - Nearest, - Linear, -} - -impl FrameFilter { - fn mode(self) -> FilterMode { - match self { - Self::Nearest => FilterMode::Nearest, - Self::Linear => FilterMode::Linear, - } - } -} - -#[derive(Clone, Copy, Default)] -pub(crate) struct Parameters { - pub buffer_size: BufferSize, - pub factor: Vec2, - pub filter: FrameFilter, - pub antialiasing: bool, - pub vignette: Vignette, -} - -pub(crate) struct PostProcessor { - data: PostShaderData, - pipeline: Pipeline, - bind_group: OnceLock, - sampler: Sampler, - params: Parameters, -} - -impl PostProcessor { - pub const DATA_GROUP: u32 = 0; - pub const DATA_BINDING: u32 = 0; - pub const TEXTURE_GROUP: u32 = 1; - const TEXTURE_TDIFF_BINDING: u32 = 0; - const TEXTURE_SDIFF_BINDING: u32 = 1; - - pub fn new(state: &State) -> Self { - let params = Parameters::default(); - let Parameters { - buffer_size, - factor, - filter, - antialiasing, - vignette, - } = params; - - let pipeline = Self::pipeline(state, antialiasing, vignette); - let globals = &pipeline.globals().expect("globals").layout; - let data = PostShaderData::new(state, globals); - data.resize(buffer_size.into(), factor.into()); - - Self { - data, - pipeline, - bind_group: OnceLock::new(), - sampler: Self::sampler(state, filter), - params, - } - } - - pub fn set_parameters(&mut self, state: &State, params: Parameters) { - let Parameters { - buffer_size, - factor, - filter, - antialiasing, - vignette, - } = params; - - if self.params.antialiasing != antialiasing || self.params.vignette != vignette { - self.pipeline = Self::pipeline(state, antialiasing, vignette); - } - - if self.params.filter != filter { - self.sampler = Self::sampler(state, filter); - } - - if self.params.buffer_size != buffer_size { - self.bind_group.take(); - self.data.resize(buffer_size.into(), factor.into()); - } else if self.params.factor != factor { - self.data.resize(buffer_size.into(), factor.into()); - } - - self.params = params; - } - - pub fn render_pipeline(&self) -> &RenderPipeline { - self.pipeline.as_ref() - } - - pub fn data_bind_group(&self) -> &BindGroup { - self.data.bind_group() - } - - pub fn render_bind_group(&self, state: &State, view: &TextureView) -> &BindGroup { - use wgpu::*; - - self.bind_group.get_or_init(|| { - state.device().create_bind_group(&BindGroupDescriptor { - label: None, - layout: &self.pipeline.textures().expect("textures layout").layout, - entries: &[ - BindGroupEntry { - binding: Self::TEXTURE_TDIFF_BINDING, - resource: BindingResource::TextureView(view), - }, - BindGroupEntry { - binding: Self::TEXTURE_SDIFF_BINDING, - resource: BindingResource::Sampler(&self.sampler), - }, - ], - }) - }) - } - - fn sampler(state: &State, filter: FrameFilter) -> Sampler { - use wgpu::{AddressMode, SamplerDescriptor}; - - let mode = filter.mode(); - state.device().create_sampler(&SamplerDescriptor { - address_mode_u: AddressMode::ClampToEdge, - address_mode_v: AddressMode::ClampToEdge, - mag_filter: mode, - min_filter: mode, - ..Default::default() - }) - } - - fn pipeline(state: &State, antialiasing: bool, vignette: Vignette) -> Pipeline { - use { - crate::pipeline::Parameters, - dunge_shader::Shader, - wgpu::{BlendState, PrimitiveTopology}, - }; - - let scheme = PostScheme { - post_data: Self::DATA_BINDING, - map: TextureBindings { - tmaps: vec![Self::TEXTURE_TDIFF_BINDING], - smap: Self::TEXTURE_SDIFF_BINDING, - }, - antialiasing, - vignette, - }; - - let shader = Shader::postproc(scheme); - log::debug!("generated post shader:\n{src}", src = shader.source); - - Pipeline::new( - state, - &shader, - None, - Parameters { - blend: BlendState::ALPHA_BLENDING, - topology: PrimitiveTopology::TriangleStrip, - cull_faces: false, - depth_stencil: None, - ..Default::default() - }, - ) - } -} diff --git a/dunge/src/render.rs b/dunge/src/render.rs deleted file mode 100644 index ae357ac..0000000 --- a/dunge/src/render.rs +++ /dev/null @@ -1,277 +0,0 @@ -use { - crate::{ - canvas::{Backend as CanvasBackend, CanvasConfig, Error as CanvasError, Info, Selector}, - frame::{Frame, Snapshot}, - framebuffer::Framebuffer, - postproc::PostProcessor, - r#loop::Loop, - screen::{RenderScreen, Screen}, - shader_data::{Instance as ShaderInstance, ModelTransform}, - }, - std::{ops, sync::Arc}, - wgpu::{Adapter, Device, Instance, Queue, Surface, SurfaceConfiguration, SurfaceError}, - winit::window::Window, -}; - -pub(crate) struct Render { - state: State, - conf: SurfaceConfiguration, - screen: RenderScreen, - postproc: PostProcessor, - framebuffer: Framebuffer, - instance: ShaderInstance, -} - -impl Render { - pub fn new(state: State) -> Self { - use wgpu::*; - - let conf = SurfaceConfiguration { - usage: TextureUsages::RENDER_ATTACHMENT, - format: Framebuffer::RENDER_FORMAT, - width: 1, - height: 1, - present_mode: PresentMode::Fifo, - alpha_mode: CompositeAlphaMode::Auto, - view_formats: vec![], - }; - - let screen = RenderScreen::new(&state); - let postproc = PostProcessor::new(&state); - let framebuffer = Framebuffer::new(&state); - let instance = ShaderInstance::new(&[ModelTransform::default()], &state); - - Self { - state, - conf, - screen, - postproc, - framebuffer, - instance, - } - } - - pub fn info(&self) -> &Info { - &self.state.info - } - - pub fn drop_surface(&mut self) { - self.state.surface.take(); - } - - pub fn recreate_surface(&mut self, window: &Window) { - self.state.surface.get_or_insert_with(|| unsafe { - let surface = self - .state - .instance - .create_surface(&window) - .expect("create surface"); - - surface.configure(&self.state.device, &self.conf); - surface - }); - } - - pub fn screen(&self) -> Screen { - self.screen.screen() - } - - pub fn resize(&mut self, size: (u32, u32)) { - use std::num::NonZeroU32; - - self.set_screen({ - let (width, height) = size; - Screen { - width: NonZeroU32::new(width).unwrap_or(NonZeroU32::MIN), - height: NonZeroU32::new(height).unwrap_or(NonZeroU32::MIN), - ..self.screen() - } - }); - } - - pub fn set_screen(&mut self, screen: Screen) { - self.screen.set_screen(screen); - - let (width, height) = screen.physical_size().into(); - self.conf.width = width; - self.conf.height = height; - self.state - .surface - .as_mut() - .expect("surface") - .configure(&self.state.device, &self.conf); - - self.framebuffer - .set_size(self.screen.buffer_size(), &self.state); - } - - pub fn draw_frame(&mut self, lp: &L) -> Result<(), SurfaceError> - where - L: Loop, - { - use wgpu::*; - - let output = self - .state - .surface - .as_ref() - .expect("surface") - .get_current_texture()?; - - let view = output - .texture - .create_view(&TextureViewDescriptor::default()); - - let mut frame = Frame::new(Snapshot::new(self), view); - lp.render(&mut frame); - frame.draw_on_screen(); - output.present(); - Ok(()) - } -} - -impl ops::Deref for Render { - type Target = State; - - fn deref(&self) -> &Self::Target { - &self.state - } -} - -impl<'d> Snapshot<'d> { - fn new(render: &'d mut Render) -> Self { - Self { - state: &render.state, - framebuffer: &render.framebuffer, - postproc: &mut render.postproc, - screen: render.screen, - instance: &render.instance, - } - } -} - -pub(crate) struct State { - info: Info, - instance: Instance, - device: Arc, - queue: Arc, - surface: Option, -} - -impl State { - pub async fn new(conf: CanvasConfig, window: &Window) -> Result { - use wgpu::*; - - let instance = Instance::new(InstanceDescriptor { - backends: match conf.backend { - CanvasBackend::Vulkan => Backends::VULKAN, - CanvasBackend::Gl => Backends::GL, - CanvasBackend::Dx12 => Backends::DX12, - CanvasBackend::Dx11 => Backends::DX11, - CanvasBackend::Metal => Backends::METAL, - CanvasBackend::WebGpu => Backends::BROWSER_WEBGPU, - }, - dx12_shader_compiler: Dx12Compiler::default(), - flags: InstanceFlags::default(), - gles_minor_version: Gles3MinorVersion::Automatic, - }); - - // In Android a surface will be created later - let surface = if cfg!(target_os = "android") { - None - } else { - Some(unsafe { instance.create_surface(window).expect("create surface") }) - }; - - let (device, queue, info) = { - let adapter = Self::select_adapter(conf.selector, &instance, surface.as_ref()) - .await - .ok_or(CanvasError::BackendSelection(conf.backend))?; - - let info = Info::from_adapter_info(adapter.get_info()); - let backend = info.backend; - log::info!("selected backend: {backend:?}"); - - let desc = DeviceDescriptor { - label: None, - features: Features::empty(), - limits: Limits { - max_texture_dimension_2d: adapter.limits().max_texture_dimension_2d, - max_storage_buffers_per_shader_stage: 0, - max_storage_textures_per_shader_stage: 0, - max_dynamic_storage_buffers_per_pipeline_layout: 0, - max_storage_buffer_binding_size: 0, - max_compute_workgroup_storage_size: 0, - max_compute_invocations_per_workgroup: 0, - max_compute_workgroup_size_x: 0, - max_compute_workgroup_size_y: 0, - max_compute_workgroup_size_z: 0, - max_compute_workgroups_per_dimension: 0, - ..if cfg!(target_arch = "wasm32") { - Limits::downlevel_webgl2_defaults() - } else { - Limits::downlevel_defaults() - } - }, - }; - - let (device, queue) = adapter - .request_device(&desc, None) - .await - .map_err(|_| CanvasError::RequestDevice)?; - - (device, queue, info) - }; - - Ok(Self { - info, - instance, - device: Arc::new(device), - queue: Arc::new(queue), - surface, - }) - } - - async fn select_adapter( - selector: Selector, - instance: &Instance, - surface: Option<&Surface>, - ) -> Option { - match selector { - Selector::Auto => { - use wgpu::{PowerPreference, RequestAdapterOptions}; - - instance - .request_adapter(&RequestAdapterOptions { - power_preference: PowerPreference::HighPerformance, - force_fallback_adapter: false, - compatible_surface: surface, - }) - .await - } - #[cfg(not(target_arch = "wasm32"))] - Selector::Callback(mut callback) => { - use wgpu::Backends; - - let mut adapters = vec![]; - let mut entries = vec![]; - for adapter in instance.enumerate_adapters(Backends::all()) { - let info = adapter.get_info(); - adapters.push(adapter); - entries.push(Info::from_adapter_info(info)); - } - - let selected = callback(entries)?; - (selected < adapters.len()).then(|| adapters.swap_remove(selected)) - } - } - } - - pub fn device(&self) -> &Arc { - &self.device - } - - pub fn queue(&self) -> &Arc { - &self.queue - } -} diff --git a/dunge/src/scheme.rs b/dunge/src/scheme.rs deleted file mode 100644 index f021415..0000000 --- a/dunge/src/scheme.rs +++ /dev/null @@ -1,31 +0,0 @@ -use { - crate::shader::{self, Shader}, - dunge_shader::Shader as ShaderData, - std::marker::PhantomData, -}; - -/// The shader scheme. -pub struct Scheme { - data: ShaderData, - ty: PhantomData, -} - -impl Scheme { - pub(crate) fn new() -> Self - where - S: Shader, - { - let scheme = shader::scheme::(); - let data = ShaderData::generate(scheme); - log::debug!("generated shader:\n{src}", src = data.source); - - Self { - data, - ty: PhantomData, - } - } - - pub(crate) fn data(&self) -> &ShaderData { - &self.data - } -} diff --git a/dunge/src/screen.rs b/dunge/src/screen.rs deleted file mode 100644 index 3fe6e52..0000000 --- a/dunge/src/screen.rs +++ /dev/null @@ -1,137 +0,0 @@ -use { - crate::{ - context::PixelSize, - framebuffer::BufferSize, - postproc::{FrameFilter, Parameters}, - render::State, - }, - glam::{UVec2, Vec2}, - std::num::NonZeroU32, -}; - -#[derive(Clone, Copy)] -pub(crate) struct Screen { - pub width: NonZeroU32, - pub height: NonZeroU32, - pub pixel_size: PixelSize, - pub filter: FrameFilter, -} - -impl Screen { - /// The physical size of the frame. - pub fn physical_size(&self) -> UVec2 { - UVec2::new(self.width.get(), self.height.get()) - } - - /// The virtual size of the frame without antialiasing factor. - pub fn virtual_size(&self) -> UVec2 { - let size = self.physical_size(); - let size = match self.pixel_size { - PixelSize::Antialiasing | PixelSize::X1 => size, - PixelSize::X2 => size / 2, - PixelSize::X3 => size / 3, - PixelSize::X4 => size / 4, - }; - - size.max(UVec2::new(1, 1)) - } - - fn is_antialiasing_enabled(&self) -> bool { - matches!(self.pixel_size, PixelSize::Antialiasing) - } - - /// The virtual size of the frame. - pub fn virtual_size_with_antialiasing(&self) -> UVec2 { - let size = self.virtual_size(); - if self.is_antialiasing_enabled() { - size * 2 - } else { - size - } - } - - /// Factor of physical size relative to virtual aligned size. - pub fn size_factor(&self) -> Vec2 { - let aligned = self.virtual_size_with_antialiasing().as_vec2(); - let aligned = match self.pixel_size { - PixelSize::Antialiasing => aligned / 2., - PixelSize::X1 => aligned, - PixelSize::X2 => aligned * 2., - PixelSize::X3 => aligned * 3., - PixelSize::X4 => aligned * 4., - }; - - let physical = self.physical_size().as_vec2(); - physical / aligned - } -} - -impl Default for Screen { - fn default() -> Self { - let n = 1.try_into().expect("1 is non zero"); - Self { - width: n, - height: n, - pixel_size: PixelSize::default(), - filter: FrameFilter::default(), - } - } -} - -#[derive(Clone, Copy)] -pub(crate) struct RenderScreen { - max_texture_size: u32, - screen: Screen, -} - -impl RenderScreen { - pub fn new(state: &State) -> Self { - Self { - max_texture_size: state.device().limits().max_texture_dimension_2d, - screen: Screen::default(), - } - } - - pub fn set_screen(&mut self, screen: Screen) { - self.screen = screen; - } - - pub fn screen(&self) -> Screen { - self.screen - } - - pub fn virtual_size(&self) -> UVec2 { - self.screen.virtual_size() - } - - pub fn virtual_size_with_antialiasing(&self) -> UVec2 { - self.screen.virtual_size_with_antialiasing() - } - - /// The buffer size of the frame. - pub fn buffer_size(&self) -> BufferSize { - let size = self.screen.virtual_size_with_antialiasing(); - let (width, height) = size.into(); - - let max = self.max_texture_size; - if width > max { - log::warn!("maximum screen buffer width ({max}) exceeded"); - } - - if height > max { - log::warn!("maximum screen buffer height ({max}) exceeded"); - } - - BufferSize::new(width, height, max) - } - - pub fn frame_parameters(&self) -> Parameters { - Parameters { - buffer_size: self.buffer_size(), - factor: self.screen.size_factor(), - filter: self.screen.filter, - antialiasing: self.screen.is_antialiasing_enabled(), - ..Default::default() - } - } -} diff --git a/dunge/src/shader.rs b/dunge/src/shader.rs deleted file mode 100644 index 2dbfed4..0000000 --- a/dunge/src/shader.rs +++ /dev/null @@ -1,140 +0,0 @@ -//! Shader components. - -pub use dunge_shader::{ - Color, LightSpaces, SourceArray, SourceArrays, SourceKind, SpaceKind, TexturesNumber, ViewKind, -}; - -use { - crate::vertex::{Vertex, VertexInfo}, - dunge_shader::{Dimension, Fragment, Scheme, Vertex as SchemeVertex}, -}; - -/// The trait defines the shader information. -/// -/// This trait has no methods, instead it defines static information about -/// the shader and is used to generate it. To create a shader scheme, call -/// the [`create_scheme`](crate::Context::create_scheme) function on -/// the [context](crate::Context). -pub trait Shader { - /// Mesh vertex type description. - type Vertex: Vertex; - - /// Determines whether to use the camera in the shader. - const VIEW: ViewKind = ViewKind::None; - - /// Determines whether to use the ambient color. - const AMBIENT: bool = false; - - /// Specifies a static color. - const STATIC_COLOR: Option = None; - - /// Specifies textures. - const TEXTURES: TexturesNumber = TexturesNumber::N0; - - /// Determines whether to use light sources. - const SOURCES: SourceArrays = SourceArrays::EMPTY; - - /// Determines whether to use light spaces. - const SPACES: LightSpaces = LightSpaces::EMPTY; - - /// Determines whether to use color instancing. - const INSTANCE_COLORS: bool = false; -} - -pub(crate) const fn scheme() -> Scheme -where - S: Shader, -{ - let vert = VertexInfo::new::(); - Scheme { - vert: SchemeVertex { - dimension: match vert.dimensions { - 2 => Dimension::D2, - 3 => Dimension::D3, - _ => unreachable!(), - }, - fragment: Fragment { - vertex_color: vert.has_color, - vertex_texture: vert.has_texture, - }, - }, - view: S::VIEW, - static_color: S::STATIC_COLOR, - ambient: S::AMBIENT, - textures: S::TEXTURES, - source_arrays: S::SOURCES, - light_spaces: S::SPACES, - instance_colors: S::INSTANCE_COLORS, - } -} - -pub(crate) struct ShaderInfo { - instances: Instances, - has_view: bool, - has_ambient: bool, - maps: usize, - source_arrays: usize, - light_spaces: LightSpaces, -} - -impl ShaderInfo { - pub const fn new() -> Self - where - S: Shader, - { - Self { - instances: Instances { - has_color: S::INSTANCE_COLORS, - }, - has_view: matches!(S::VIEW, ViewKind::Camera), - has_ambient: S::AMBIENT, - maps: S::TEXTURES.len(), - source_arrays: S::SOURCES.len(), - light_spaces: S::SPACES, - } - } - - pub const fn has_view(&self) -> bool { - self.has_view - } - - pub const fn has_ambient(&self) -> bool { - self.has_ambient - } - - pub const fn texture_maps(&self) -> usize { - self.maps - } - - pub const fn source_arrays(&self) -> usize { - self.source_arrays - } - - pub const fn light_spaces(&self) -> LightSpaces { - self.light_spaces - } - - pub const fn has_instance_colors(&self) -> bool { - self.instances.has_color - } - - pub const fn has_globals(&self) -> bool { - self.has_view() || self.has_ambient() - } - - pub const fn has_textures(&self) -> bool { - self.texture_maps() > 0 - } - - pub const fn has_lights(&self) -> bool { - self.source_arrays() > 0 - } - - pub const fn has_spaces(&self) -> bool { - !self.light_spaces().is_empty() - } -} - -struct Instances { - has_color: bool, -} diff --git a/dunge/src/shader_data/ambient.rs b/dunge/src/shader_data/ambient.rs deleted file mode 100644 index 3b29a9e..0000000 --- a/dunge/src/shader_data/ambient.rs +++ /dev/null @@ -1,11 +0,0 @@ -use bytemuck::{Pod, Zeroable}; - -#[repr(C)] -#[derive(Copy, Clone, Pod, Zeroable)] -pub(crate) struct AmbientUniform([f32; 4]); - -impl AmbientUniform { - pub fn new([r, g, b]: [f32; 3]) -> Self { - Self([r, g, b, 0.]) - } -} diff --git a/dunge/src/shader_data/data.rs b/dunge/src/shader_data/data.rs deleted file mode 100644 index 149d0bd..0000000 --- a/dunge/src/shader_data/data.rs +++ /dev/null @@ -1,141 +0,0 @@ -use {dunge_shader::SpaceKind, std::fmt, wgpu::TextureFormat}; - -/// A data struct for a light space creation. -#[derive(Clone, Copy)] -pub struct SpaceData<'a>(Data<'a, (u8, u8, u8)>); - -impl<'a> SpaceData<'a> { - /// Creates a new [`SpaceData`](crate::SpaceData). - /// - /// # Errors - /// Will return - /// - [`TextureError::EmptyData`](crate::error::DataError::EmptyData) - /// if the data is empty. - /// - [`TextureError::SizeDoesNotMatch`](crate::error::DataError::SizeDoesNotMatch) - /// if the data length doesn't match with size * number of channels. - pub fn new(data: &'a [u8], size: (u8, u8, u8), format: Format) -> Result { - let inner = Data::new(data, size, format)?; - Ok(Self(inner)) - } - - pub(crate) fn get(self) -> Data<'a, (u8, u8, u8)> { - self.0 - } -} - -/// A data struct for a texture creation. -#[derive(Clone, Copy)] -pub struct TextureData<'a>(Data<'a, (u32, u32)>); - -impl<'a> TextureData<'a> { - /// Creates a new [`TextureData`](crate::TextureData). - /// - /// # Errors - /// Will return - /// - [`TextureError::EmptyData`](crate::error::DataError::EmptyData) - /// if the data is empty. - /// - [`TextureError::SizeDoesNotMatch`](crate::error::DataError::SizeDoesNotMatch) - /// if the data length doesn't match with size * number of channels. - pub fn new(data: &'a [u8], size: (u32, u32), format: Format) -> Result { - let inner = Data::new(data, size, format)?; - Ok(Self(inner)) - } - - pub(crate) fn get(self) -> Data<'a, (u32, u32)> { - self.0 - } -} - -#[derive(Clone, Copy)] -pub(crate) struct Data<'a, S> { - pub data: &'a [u8], - pub size: S, - pub format: Format, -} - -impl<'a, S> Data<'a, S> { - fn new(data: &'a [u8], size: S, format: Format) -> Result - where - S: Size, - { - if data.is_empty() { - return Err(Error::EmptyData); - } - - if data.len() != size.size() * format.n_channels() { - return Err(Error::SizeDoesNotMatch); - } - - Ok(Self { data, size, format }) - } -} - -/// The data error. -#[derive(Debug)] -pub enum Error { - /// The data is empty. - EmptyData, - - /// The data length doesn't match with size * number of channels. - SizeDoesNotMatch, -} - -trait Size: Copy { - fn size(self) -> usize; -} - -impl Size for (u32, u32) { - fn size(self) -> usize { - let (width, height) = self; - width as usize * height as usize - } -} - -impl Size for (u8, u8, u8) { - fn size(self) -> usize { - let (width, height, depth) = self; - width as usize * height as usize * depth as usize - } -} - -/// The texture format. -#[derive(Clone, Copy)] -pub enum Format { - Srgba, - Rgba, - Gray, -} - -impl Format { - pub(crate) const fn matches(self, kind: SpaceKind) -> bool { - matches!( - (self, kind), - (Self::Srgba | Self::Rgba, SpaceKind::Rgba) | (Self::Gray, SpaceKind::Gray) - ) - } - - pub(crate) const fn n_channels(self) -> usize { - match self { - Self::Srgba | Self::Rgba => 4, - Self::Gray => 1, - } - } - - pub(crate) const fn texture_format(self) -> TextureFormat { - match self { - Self::Srgba => TextureFormat::Rgba8UnormSrgb, - Self::Rgba => TextureFormat::Rgba8Unorm, - Self::Gray => TextureFormat::R8Unorm, - } - } -} - -impl fmt::Display for Format { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Srgba => write!(f, "srgba"), - Self::Rgba => write!(f, "rgba"), - Self::Gray => write!(f, "gray"), - } - } -} diff --git a/dunge/src/shader_data/globals.rs b/dunge/src/shader_data/globals.rs deleted file mode 100644 index 3c5ace6..0000000 --- a/dunge/src/shader_data/globals.rs +++ /dev/null @@ -1,217 +0,0 @@ -use { - crate::{ - color::{Color, Rgb}, - layer::Layer, - pipeline::Globals as Bindings, - render::State, - shader::{Shader, ShaderInfo}, - shader_data::{ambient::AmbientUniform, ModelTransform}, - view::ViewHandle, - }, - std::{marker::PhantomData, sync::Arc}, - wgpu::{BindGroup, BindGroupLayout, Buffer, Queue}, -}; - -/// Shader global variables. -/// -/// Can be created from the [context](crate::Context) by calling -/// the [`globals_builder`](crate::Context::globals_builder) function. -pub struct Globals { - group: u32, - bind_group: BindGroup, - view: Option<(ViewHandle, Buffer)>, - ambient: Option, - queue: Arc, - ty: PhantomData, -} - -impl Globals { - fn new(params: Parameters, state: &State) -> Self { - use wgpu::{ - util::{BufferInitDescriptor, DeviceExt}, - BindGroupDescriptor, BindGroupEntry, BufferUsages, - }; - - let Parameters { - bindings, - variables, - layout, - .. - } = params; - - let device = state.device(); - let view = variables.view.map(|view| { - let model = ModelTransform::default(); - let buf = device.create_buffer_init(&BufferInitDescriptor { - label: Some("camera buffer"), - contents: bytemuck::cast_slice(&[model]), - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - }); - - (view, buf) - }); - - let ambient = variables.ambient.map(|uniform| { - device.create_buffer_init(&BufferInitDescriptor { - label: Some("ambient buffer"), - contents: bytemuck::cast_slice(&[uniform]), - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - }) - }); - - let entries: Vec<_> = [ - view.as_ref().map(|(_, buf)| BindGroupEntry { - binding: bindings.camera, - resource: buf.as_entire_binding(), - }), - ambient.as_ref().map(|buf| BindGroupEntry { - binding: bindings.ambient, - resource: buf.as_entire_binding(), - }), - ] - .into_iter() - .flatten() - .collect(); - - Self { - group: bindings.group, - bind_group: device.create_bind_group(&BindGroupDescriptor { - layout, - entries: &entries, - label: Some("globals bind group"), - }), - view, - ambient, - queue: Arc::clone(state.queue()), - ty: PhantomData, - } - } - - /// Updates the view with a new [view](crate::View). - /// - /// # Panics - /// Panics if the shader has no view. - pub fn update_view(&mut self, view: V) - where - S: Shader, - V: Into, - { - let info = ShaderInfo::new::(); - assert!(info.has_view(), "the shader has no view"); - - let (handle, _) = self.view.as_mut().expect("view"); - *handle = view.into(); - } - - /// Updates the ambient with a new [color](`Rgb`). - /// - /// # Panics - /// Panics if the shader has no ambient. - pub fn update_ambient(&self, Color(col): Rgb) - where - S: Shader, - { - let info = ShaderInfo::new::(); - assert!(info.has_ambient(), "the shader has no ambient"); - - let buf = self.ambient.as_ref().expect("ambient"); - let uniform = AmbientUniform::new(col); - self.queue - .write_buffer(buf, 0, bytemuck::cast_slice(&[uniform])); - } - - pub(crate) fn update_size(&self, size: (u32, u32)) { - let Some((camera, buf)) = &self.view else { - return; - }; - - let uniform = camera.model(size); - self.queue - .write_buffer(buf, 0, bytemuck::cast_slice(&[uniform])); - } - - pub(crate) fn bind(&self) -> (u32, &BindGroup) { - (self.group, &self.bind_group) - } -} - -struct Parameters<'a> { - variables: Variables, - bindings: &'a Bindings, - layout: &'a BindGroupLayout, -} - -#[derive(Default)] -struct Variables { - view: Option, - ambient: Option, -} - -/// The [globals](Globals) builder. -#[must_use] -pub struct Builder<'a> { - state: &'a State, - variables: Variables, -} - -impl<'a> Builder<'a> { - pub(crate) fn new(state: &'a State) -> Self { - Self { - state, - variables: Variables::default(), - } - } - - /// Sets a [view](crate::View) for the globals object. - pub fn with_view(mut self, view: V) -> Self - where - V: Into, - { - self.variables.view = Some(view.into()); - self - } - - /// Sets an ambient [color](Color) for the globals object. - pub fn with_ambient(mut self, Color(col): Rgb) -> Self { - self.variables.ambient = Some(AmbientUniform::new(col)); - self - } - - /// Builds the globals. - /// - /// # Panics - /// Panics if the shader requires view or ambient, but they aren't set. They can be set by the - /// [`with_view`](Builder::with_view) or the [`with_ambient`](Builder::with_ambient) functions. - pub fn build(self, layer: &Layer) -> Globals - where - S: Shader, - { - let info = ShaderInfo::new::(); - if info.has_view() { - assert!( - self.variables.view.is_some(), - "the shader requires view, but it's not set", - ); - } - - if info.has_ambient() { - assert!( - self.variables.ambient.is_some(), - "the shader requires ambient, but it's not set", - ); - } - - let globals = layer - .pipeline() - .globals() - .expect("the shader has no globals"); - - let params = Parameters { - variables: self.variables, - bindings: &globals.bindings, - layout: &globals.layout, - }; - - Globals::new(params, self.state) - } -} diff --git a/dunge/src/shader_data/instance.rs b/dunge/src/shader_data/instance.rs deleted file mode 100644 index 6b12428..0000000 --- a/dunge/src/shader_data/instance.rs +++ /dev/null @@ -1,190 +0,0 @@ -use { - crate::{ - buffer::BufferView, - color::{Color, Rgb}, - render::State, - }, - bytemuck::{Pod, Zeroable}, - glam::Mat4, - std::{marker::PhantomData, sync::Arc}, - wgpu::{Buffer, Queue, VertexBufferLayout, VertexStepMode}, -}; - -type Mat = [[f32; 4]; 4]; - -/// The instance transform model. -#[repr(transparent)] -#[derive(Copy, Clone, Pod, Zeroable)] -pub struct ModelTransform(Mat); - -impl ModelTransform { - pub(crate) const LAYOUT_ATTRIBUTES_LEN: u32 = Self::LAYOUT.attributes.len() as u32; - pub(crate) const LAYOUT: VertexBufferLayout<'static> = { - use std::mem; - - VertexBufferLayout { - array_stride: mem::size_of::() as _, - step_mode: VertexStepMode::Instance, - attributes: &wgpu::vertex_attr_array![ - 0 => Float32x4, - 1 => Float32x4, - 2 => Float32x4, - 3 => Float32x4, - ], - } - }; - - pub(crate) fn into_inner(self) -> Mat { - self.0 - } - - pub fn into_mat(self) -> Mat4 { - Mat4::from_cols_array_2d(&self.0) - } -} - -impl Default for ModelTransform { - fn default() -> Self { - Self::from(Mat4::IDENTITY) - } -} - -impl From for ModelTransform { - fn from(mat: Mat) -> Self { - Self(mat) - } -} - -impl From for ModelTransform { - fn from(mat: Mat4) -> Self { - Self::from(mat.to_cols_array_2d()) - } -} - -/// The instance color model. -#[repr(transparent)] -#[derive(Copy, Clone, Default, Pod, Zeroable)] -pub struct ModelColor([f32; 3]); - -impl ModelColor { - pub(crate) const LAYOUT_ATTRIBUTES_LEN: u32 = Self::LAYOUT.attributes.len() as u32; - pub(crate) const LAYOUT: VertexBufferLayout<'static> = { - use std::mem; - - VertexBufferLayout { - array_stride: mem::size_of::() as _, - step_mode: VertexStepMode::Instance, - attributes: &wgpu::vertex_attr_array![ - ModelTransform::LAYOUT_ATTRIBUTES_LEN => Float32x3, - ], - } - }; -} - -impl From for ModelColor { - fn from(Color(col): Rgb) -> Self { - Self(col) - } -} - -/// The instance defining a transform of drawable objects. -/// -/// Can be created from the [context](crate::Context) by calling -/// the [`create_instances`](crate::Context::create_instances) function. -pub struct Instance(Inner); - -impl Instance { - pub(crate) fn new(models: &[ModelTransform], state: &State) -> Self { - Self(Inner::new(models, state)) - } - - /// Updates the instance with new [models](`ModelTransform`). - /// - /// # Errors - /// Will return [`InvalidSize`] if the size of the [models](`ModelTransform`) - /// slice doesn't match the current instance size. - pub fn update(&self, models: &[ModelTransform]) -> Result<(), InvalidSize> { - self.0.update(models) - } - - pub(crate) fn buffer(&self) -> BufferView { - self.0.buffer() - } -} - -/// The instance defining a color of drawable objects. -/// -/// Can be created from the [context](crate::Context) by calling -/// the [`create_instances_color`](crate::Context::create_instances_color) function. -pub struct InstanceColor(Inner); - -impl InstanceColor { - pub(crate) fn new(models: &[ModelColor], state: &State) -> Self { - Self(Inner::new(models, state)) - } - - /// Updates the instance color with new [models](`ModelColor`). - /// - /// # Errors - /// Will return [`InvalidSize`] if the size of the [models](`ModelColor`) - /// slice doesn't match the current instance size. - pub fn update(&self, models: &[ModelColor]) -> Result<(), InvalidSize> { - self.0.update(models) - } - - pub(crate) fn buffer(&self) -> BufferView { - self.0.buffer() - } -} - -struct Inner { - buf: Buffer, - queue: Arc, - ty: PhantomData, -} - -impl Inner { - fn new(models: &[M], state: &State) -> Self - where - M: Pod, - { - use wgpu::{ - util::{BufferInitDescriptor, DeviceExt}, - BufferUsages, - }; - - Self { - buf: state.device().create_buffer_init(&BufferInitDescriptor { - label: Some("instance buffer"), - contents: bytemuck::cast_slice(models), - usage: BufferUsages::VERTEX | BufferUsages::COPY_DST, - }), - queue: Arc::clone(state.queue()), - ty: PhantomData, - } - } - - fn update(&self, models: &[M]) -> Result<(), InvalidSize> - where - M: Pod, - { - use std::mem; - - if self.buf.size() != mem::size_of_val(models) as u64 { - return Err(InvalidSize); - } - - self.queue - .write_buffer(&self.buf, 0, bytemuck::cast_slice(models)); - - Ok(()) - } - - fn buffer(&self) -> BufferView { - BufferView::new::(&self.buf, None) - } -} - -/// An error returned from the instance updation. -#[derive(Debug)] -pub struct InvalidSize; diff --git a/dunge/src/shader_data/len.rs b/dunge/src/shader_data/len.rs deleted file mode 100644 index fef733f..0000000 --- a/dunge/src/shader_data/len.rs +++ /dev/null @@ -1,16 +0,0 @@ -use bytemuck::{Pod, Zeroable}; - -#[repr(C)] -#[derive(Copy, Clone, Pod, Zeroable)] -pub(crate) struct LenUniform([u32; 4]); - -impl LenUniform { - pub fn new(n: u32) -> Self { - Self([n, 0, 0, 0]) - } - - pub fn get(self) -> u32 { - let Self([n, ..]) = self; - n - } -} diff --git a/dunge/src/shader_data/lights.rs b/dunge/src/shader_data/lights.rs deleted file mode 100644 index 69a46bb..0000000 --- a/dunge/src/shader_data/lights.rs +++ /dev/null @@ -1,236 +0,0 @@ -use { - crate::{ - layer::Layer, - pipeline::Lights as Bindings, - render::State, - shader::{Shader, ShaderInfo}, - shader_data::source::{SetLenError, Source, SourceArray, UpdateError as ArrayUpdateError}, - }, - std::{marker::PhantomData, sync::Arc}, - wgpu::{BindGroup, BindGroupLayout, Buffer, Queue}, -}; - -/// Shader lights. -/// -/// Can be created from the [context](crate::Context) by calling -/// the [`lights_builder`](crate::Context::lights_builder) function. -pub struct Lights { - group: u32, - bind_group: BindGroup, - source_arrays: Vec<(SourceArray, SourceArrayBuffers)>, - queue: Arc, - ty: PhantomData, -} - -impl Lights { - fn new(params: Parameters, state: &State) -> Self { - use { - std::iter, - wgpu::{ - util::{BufferInitDescriptor, DeviceExt}, - BindGroupDescriptor, BindGroupEntry, BufferUsages, - }, - }; - - let Parameters { - bindings, - variables, - layout, - } = params; - - let device = state.device(); - let source_arrays: Vec<_> = iter::zip(variables.source_arrays, &bindings.source_arrays) - .map(|(var, bind)| { - let array = SourceArray::new(var, bind.size as usize); - let array_buf = device.create_buffer_init(&BufferInitDescriptor { - label: Some("source array buffer"), - contents: bytemuck::cast_slice(array.sources()), - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - }); - - let len_buf = device.create_buffer_init(&BufferInitDescriptor { - label: Some("source len buffer"), - contents: bytemuck::cast_slice(&[array.len()]), - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - }); - - ( - array, - SourceArrayBuffers { - array: array_buf, - len: len_buf, - }, - ) - }) - .collect(); - - let entries: Vec<_> = iter::zip(&source_arrays, &bindings.source_arrays) - .flat_map(|((_, buf), bind)| { - [ - BindGroupEntry { - binding: bind.binding_array, - resource: buf.array.as_entire_binding(), - }, - BindGroupEntry { - binding: bind.binding_len, - resource: buf.len.as_entire_binding(), - }, - ] - }) - .collect(); - - Self { - group: bindings.group, - bind_group: device.create_bind_group(&BindGroupDescriptor { - layout, - entries: &entries, - label: Some("lights bind group"), - }), - source_arrays, - queue: Arc::clone(state.queue()), - ty: PhantomData, - } - } - - /// Updates the lights with new [sources](`Source`). - /// - /// # Errors - /// Will return - /// - [`UpdateError::Index`] if the index is invalid. - /// - [`UpdateError::Array`] if wrong offset or slice with wrong length is passed. - /// - [`UpdateError::SetLen`] if the new length exceeds the maximum available. - /// - /// # Panics - /// Panics if the shader has no light sources. - pub fn update_sources( - &mut self, - index: usize, - offset: usize, - sources: &[Source], - ) -> Result<(), UpdateError> - where - S: Shader, - { - use std::mem; - - let info = ShaderInfo::new::(); - assert!(info.has_lights(), "the shader has no light sources"); - - let (array, buffers) = self - .source_arrays - .get_mut(index) - .ok_or(UpdateError::Index)?; - - array.update(offset, sources)?; - let data = &array.sources()[offset..]; - self.queue.write_buffer( - &buffers.array, - (offset * mem::size_of::()) as _, - bytemuck::cast_slice(data), - ); - - let old_len = array.len(); - let new_len = (offset + sources.len()) as u32; - if old_len.get() < new_len { - array.set_len(new_len)?; - self.queue - .write_buffer(&buffers.len, 0, bytemuck::cast_slice(&[array.len()])); - } - - Ok(()) - } - - pub(crate) fn bind(&self) -> (u32, &BindGroup) { - (self.group, &self.bind_group) - } -} - -/// An error returned from the [`update_sources`](Lights::update_sources) function. -#[derive(Debug)] -pub enum UpdateError { - /// The index is invalid. - Index, - - /// Wrong offset or slice with wrong length. - Array(ArrayUpdateError), - - /// The new length exceeds the maximum available. - SetLen(SetLenError), -} - -impl From for UpdateError { - fn from(v: ArrayUpdateError) -> Self { - Self::Array(v) - } -} - -impl From for UpdateError { - fn from(v: SetLenError) -> Self { - Self::SetLen(v) - } -} - -struct SourceArrayBuffers { - array: Buffer, - len: Buffer, -} - -struct Parameters<'a> { - variables: Variables, - bindings: &'a Bindings, - layout: &'a BindGroupLayout, -} - -#[derive(Default)] -struct Variables { - source_arrays: Vec>, -} - -/// The [lights](Lights) builder. -#[must_use] -pub struct Builder<'a> { - state: &'a State, - variables: Variables, -} - -impl<'a> Builder<'a> { - pub(crate) fn new(state: &'a State) -> Self { - Self { - state, - variables: Variables::default(), - } - } - - /// Sets light [sources](Source) for the lights object. - pub fn with_sources(mut self, sources: Vec) -> Self { - self.variables.source_arrays.push(sources); - self - } - - /// Builds the lights. - /// - /// # Panics - /// Panics if the shader requires source arrays, but they aren't set. - pub fn build(self, layer: &Layer) -> Lights - where - S: Shader, - { - let actual = self.variables.source_arrays.len(); - let info = ShaderInfo::new::(); - let expected = info.source_arrays(); - - assert_eq!( - actual, expected, - "the shader requires {expected} source arrays, but {actual} is set", - ); - - let lights = layer.pipeline().lights().expect("the shader has no lights"); - let params = Parameters { - variables: self.variables, - bindings: &lights.bindings, - layout: &lights.layout, - }; - - Lights::new(params, self.state) - } -} diff --git a/dunge/src/shader_data/post.rs b/dunge/src/shader_data/post.rs deleted file mode 100644 index 6a65ed7..0000000 --- a/dunge/src/shader_data/post.rs +++ /dev/null @@ -1,79 +0,0 @@ -use { - crate::{postproc::PostProcessor, render::State}, - bytemuck::{Pod, Zeroable}, - std::sync::Arc, - wgpu::{BindGroup, BindGroupLayout, Buffer, Queue}, -}; - -pub(crate) struct PostShaderData { - bind_group: BindGroup, - buf: Buffer, - queue: Arc, -} - -impl PostShaderData { - pub fn new(state: &State, layout: &BindGroupLayout) -> Self { - use wgpu::{ - util::{BufferInitDescriptor, DeviceExt}, - BindGroupDescriptor, BindGroupEntry, BufferUsages, - }; - - let device = state.device(); - let buf = { - let uniform = PostShaderDataUniform::new((1., 1.), (1., 1.)); - device.create_buffer_init(&BufferInitDescriptor { - label: Some("post data buffer"), - contents: bytemuck::cast_slice(&[uniform]), - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - }) - }; - - let bind_group = device.create_bind_group(&BindGroupDescriptor { - layout, - entries: &[BindGroupEntry { - binding: PostProcessor::DATA_BINDING, - resource: buf.as_entire_binding(), - }], - label: Some("post bind group"), - }); - - Self { - bind_group, - buf, - queue: Arc::clone(state.queue()), - } - } - - pub fn resize(&self, size: (f32, f32), factor: (f32, f32)) { - let uniform = PostShaderDataUniform::new(size, factor); - self.queue - .write_buffer(&self.buf, 0, bytemuck::cast_slice(&[uniform])); - } - - pub fn bind_group(&self) -> &BindGroup { - &self.bind_group - } -} - -#[repr(C)] -#[derive(Copy, Clone, Pod, Zeroable)] -struct PostShaderDataUniform { - size: [f32; 2], - step: [f32; 2], - factor: [f32; 2], - pad: u64, -} - -impl PostShaderDataUniform { - fn new(size: (f32, f32), factor: (f32, f32)) -> Self { - const STEP_FACTOR: f32 = 0.5; - - let size = size.into(); - Self { - size, - step: size.map(|v| STEP_FACTOR / v), - factor: factor.into(), - pad: 0, - } - } -} diff --git a/dunge/src/shader_data/source.rs b/dunge/src/shader_data/source.rs deleted file mode 100644 index c411206..0000000 --- a/dunge/src/shader_data/source.rs +++ /dev/null @@ -1,85 +0,0 @@ -use { - crate::{ - color::{Color, Rgb}, - shader_data::len::LenUniform, - }, - bytemuck::{Pod, Zeroable}, -}; - -/// Light source parameters. -#[repr(C)] -#[derive(Clone, Copy, Pod, Zeroable)] -pub struct Source { - col: [f32; 3], - rad: f32, - pos: [f32; 3], - pad: u32, -} - -impl Source { - pub fn new

(Color(col): Rgb, pos: P, rad: f32) -> Self - where - P: Into<[f32; 3]>, - { - Self { - col, - rad, - pos: pos.into(), - pad: 0, - } - } -} - -pub(crate) struct SourceArray { - sources: Box<[Source]>, - len: u32, -} - -impl SourceArray { - pub fn new(mut sources: Vec, max_size: usize) -> Self { - assert!(sources.len() <= max_size, "too many light sources"); - sources.resize(max_size, Source::zeroed()); - - let len = sources.len() as u32; - Self { - sources: sources.into_boxed_slice(), - len, - } - } - - pub fn update(&mut self, offset: usize, sources: &[Source]) -> Result<(), UpdateError> { - let buf = self.sources.get_mut(offset..).ok_or(UpdateError::Offset)?; - if sources.len() > buf.len() { - return Err(UpdateError::Len); - } - - buf[..sources.len()].copy_from_slice(sources); - Ok(()) - } - - pub fn set_len(&mut self, len: u32) -> Result<(), SetLenError> { - if len as usize > self.sources.len() { - return Err(SetLenError); - } - - self.len = len; - Ok(()) - } - - pub fn sources(&self) -> &[Source] { - &self.sources - } - - pub fn len(&self) -> LenUniform { - LenUniform::new(self.len) - } -} - -#[derive(Debug)] -pub enum UpdateError { - Offset, - Len, -} - -#[derive(Debug)] -pub struct SetLenError; diff --git a/dunge/src/shader_data/space.rs b/dunge/src/shader_data/space.rs deleted file mode 100644 index 9e73f56..0000000 --- a/dunge/src/shader_data/space.rs +++ /dev/null @@ -1,56 +0,0 @@ -use { - crate::{ - color::{Color, Rgb}, - shader_data::{data::SpaceData, ModelTransform}, - }, - bytemuck::{Pod, Zeroable}, -}; - -type Mat = [[f32; 4]; 4]; - -/// Parameters of the light space. -#[derive(Clone, Copy)] -pub struct Space<'a> { - pub data: SpaceData<'a>, - pub model: ModelTransform, - pub col: Rgb, -} - -impl Space<'_> { - pub(crate) fn into_uniform(self) -> SpaceUniform { - let Color(col) = self.col; - let size = self.data.get().size; - SpaceUniform::new(size, self.model.into_inner(), col) - } -} - -#[repr(C)] -#[derive(Copy, Clone, Pod, Zeroable)] -pub(crate) struct SpaceUniform { - model: Mat, - col: [f32; 3], - pad: u32, -} - -impl SpaceUniform { - pub fn new(size: (u8, u8, u8), transform: Mat, col: [f32; 3]) -> Self { - Self { - model: { - use glam::{Mat4, Quat, Vec3}; - - let (width, height, depth) = size; - let texture_space = Mat4::from_scale_rotation_translation( - Vec3::new(1. / width as f32, 1. / depth as f32, 1. / height as f32), - Quat::IDENTITY, - Vec3::new(0.5, 0.5, 0.5), - ); - - let model = Mat4::from_cols_array_2d(&transform); - let model = texture_space * model; - model.to_cols_array_2d() - }, - col, - pad: 0, - } - } -} diff --git a/dunge/src/shader_data/spaces.rs b/dunge/src/shader_data/spaces.rs deleted file mode 100644 index abad6ef..0000000 --- a/dunge/src/shader_data/spaces.rs +++ /dev/null @@ -1,283 +0,0 @@ -use { - crate::{ - layer::Layer, - pipeline::Spaces as Bindings, - render::State, - shader::{Shader, ShaderInfo}, - shader_data::{ - data::{Data, SpaceData}, - space::{Space, SpaceUniform}, - }, - }, - std::{marker::PhantomData, sync::Arc}, - wgpu::{BindGroup, BindGroupLayout, Buffer, Queue, Texture}, -}; - -/// Shader light spaces. -/// -/// Can be created from the [context](crate::Context) by calling -/// the [`spaces_builder`](crate::Context::spaces_builder) function. -pub struct Spaces { - group: u32, - bind_group: BindGroup, - #[allow(dead_code)] - spaces: Buffer, - textures: Box<[SpaceTexture]>, - queue: Arc, - ty: PhantomData, -} - -impl Spaces { - fn new(params: Parameters, state: &State) -> Self { - use { - std::iter, - wgpu::{ - util::{BufferInitDescriptor, DeviceExt}, - *, - }, - }; - - let Parameters { - variables, - bindings, - layout, - } = params; - - let device = state.device(); - let spaces = device.create_buffer_init(&BufferInitDescriptor { - label: Some("spaces buffer"), - contents: bytemuck::cast_slice(&variables.light_spaces), - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - }); - - let mut views = vec![]; - let textures: Box<[_]> = variables - .textures_data - .into_iter() - .map(|Data { data, size, format }| { - let (width, height, depth) = size; - let size = Extent3d { - width: width as u32, - height: height as u32, - depth_or_array_layers: depth as u32, - }; - - let texture = device.create_texture(&TextureDescriptor { - label: None, - size, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D3, - format: format.texture_format(), - usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, - view_formats: &[], - }); - - state.queue().write_texture( - ImageCopyTexture { - texture: &texture, - mip_level: 0, - origin: Origin3d::ZERO, - aspect: TextureAspect::All, - }, - data, - ImageDataLayout { - offset: 0, - bytes_per_row: Some(format.n_channels() as u32 * width as u32), - rows_per_image: Some(height as u32), - }, - size, - ); - - views.push(texture.create_view(&TextureViewDescriptor::default())); - SpaceTexture { - texture, - size: (width, height, depth), - } - }) - .collect(); - - let sampler = device.create_sampler(&SamplerDescriptor { - address_mode_u: AddressMode::ClampToEdge, - address_mode_v: AddressMode::ClampToEdge, - address_mode_w: AddressMode::ClampToEdge, - mag_filter: FilterMode::Linear, - min_filter: FilterMode::Linear, - ..Default::default() - }); - - let mut entries = vec![BindGroupEntry { - binding: bindings.bindings.spaces, - resource: spaces.as_entire_binding(), - }]; - - entries.extend( - iter::zip(&views, &bindings.bindings.tspaces).map(|(view, &tspace)| BindGroupEntry { - binding: tspace, - resource: BindingResource::TextureView(view), - }), - ); - - entries.push(BindGroupEntry { - binding: bindings.bindings.sspace, - resource: BindingResource::Sampler(&sampler), - }); - - Self { - group: bindings.group, - bind_group: device.create_bind_group(&BindGroupDescriptor { - layout, - entries: &entries, - label: Some("spaces bind group"), - }), - spaces, - textures, - queue: Arc::clone(state.queue()), - ty: PhantomData, - } - } - - /// Updates the spaces with a new [data](`SpaceData`). - /// - /// # Errors - /// Will return [`UpdateError::Index`] if the index is invalid or - /// [`UpdateError::Size`] if the [data](`SpaceData`) doesn't match the current size. - /// - /// # Panics - /// Panics if the shader has no light spaces. - pub fn update(&self, index: usize, data: SpaceData) -> Result<(), UpdateError> - where - S: Shader, - { - let info = ShaderInfo::new::(); - assert!(info.has_spaces(), "the shader has no light spaces"); - - let texture = self.textures.get(index).ok_or(UpdateError::Index)?; - texture.update(data.get(), &self.queue) - } - - pub(crate) fn bind(&self) -> (u32, &BindGroup) { - (self.group, &self.bind_group) - } -} - -struct SpaceTexture { - texture: Texture, - size: (u8, u8, u8), -} - -impl SpaceTexture { - fn update(&self, data: Data<(u8, u8, u8)>, queue: &Queue) -> Result<(), UpdateError> { - use wgpu::*; - - if data.size != self.size { - return Err(UpdateError::Size); - } - - let (width, height, depth) = self.size; - queue.write_texture( - ImageCopyTexture { - texture: &self.texture, - mip_level: 0, - origin: Origin3d::ZERO, - aspect: TextureAspect::All, - }, - data.data, - ImageDataLayout { - offset: 0, - bytes_per_row: Some(data.format.n_channels() as u32 * width as u32), - rows_per_image: Some(height as u32), - }, - Extent3d { - width: width as u32, - height: height as u32, - depth_or_array_layers: depth as u32, - }, - ); - - Ok(()) - } -} - -/// An error returned from the [`update`](Spaces::update) function. -#[derive(Debug)] -pub enum UpdateError { - /// The index is invalid. - Index, - - /// [`SpaceData`] size doesn't match the current size. - Size, -} - -struct Parameters<'a> { - variables: Variables<'a>, - bindings: &'a Bindings, - layout: &'a BindGroupLayout, -} - -#[derive(Default)] -struct Variables<'a> { - light_spaces: Vec, - textures_data: Vec>, -} - -/// The light [spaces](Spaces) builder. -#[must_use] -pub struct Builder<'a> { - state: &'a State, - variables: Variables<'a>, -} - -impl<'a> Builder<'a> { - pub(crate) fn new(state: &'a State) -> Self { - Self { - state, - variables: Variables::default(), - } - } - - /// Set a [space](Space) to shader. - pub fn with_space(mut self, space: Space<'a>) -> Self { - self.variables.textures_data.push(space.data.get()); - self.variables.light_spaces.push(space.into_uniform()); - self - } - - /// Builds the spaces. - /// - /// # Panics - /// Panics if the shader requires light spaces, but they aren't set - /// or some light space format doesn't match. - pub fn build(self, layer: &Layer) -> Spaces - where - S: Shader, - { - let info = ShaderInfo::new::(); - let light_spaces = info.light_spaces(); - let actual = self.variables.light_spaces.len(); - let expected = light_spaces.len(); - - assert_eq!( - actual, expected, - "the shader requires {expected} light spaces, but {actual} is set", - ); - - for ((n, kind), Data { format, .. }) in - light_spaces.enumerate().zip(&self.variables.textures_data) - { - assert!( - format.matches(kind), - "light space format {format} ({n}) doesn't match", - ); - } - - let spaces = layer.pipeline().spaces().expect("the shader has no spaces"); - let params = Parameters { - variables: self.variables, - bindings: &spaces.bindings, - layout: &spaces.layout, - }; - - Spaces::new(params, self.state) - } -} diff --git a/dunge/src/shader_data/texture.rs b/dunge/src/shader_data/texture.rs deleted file mode 100644 index 5026972..0000000 --- a/dunge/src/shader_data/texture.rs +++ /dev/null @@ -1,123 +0,0 @@ -use { - crate::{ - render::State, - shader_data::data::{Data, TextureData}, - }, - std::sync::Arc, - wgpu::{Queue, Texture as Tx, TextureView}, -}; - -/// The texture object. -#[derive(Clone)] -pub struct Texture(Arc); - -impl Texture { - pub(crate) fn new(data: TextureData, state: &State) -> Self { - let inner = Inner::new(data.get(), state); - Self(Arc::new(inner)) - } - - pub(crate) fn view(&self) -> &TextureView { - &self.0.view - } - - /// Updates the texture with a new [data](`TextureData`). - /// - /// # Errors - /// Will return [`InvalidSize`] if the size of the [data](`TextureData`) - /// doesn't match the current texture size. - pub fn update(&self, data: TextureData) -> Result<(), InvalidSize> { - self.0.update(data.get()) - } -} - -struct Inner { - texture: Tx, - view: TextureView, - queue: Arc, -} - -impl Inner { - fn new(data: Data<(u32, u32)>, state: &State) -> Self { - use wgpu::*; - - let (width, height) = data.size; - let size = Extent3d { - width, - height, - depth_or_array_layers: 1, - }; - - let device = state.device(); - let texture = device.create_texture(&TextureDescriptor { - label: None, - size, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D2, - format: data.format.texture_format(), - usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, - view_formats: &[], - }); - - state.queue().write_texture( - ImageCopyTexture { - texture: &texture, - mip_level: 0, - origin: Origin3d::ZERO, - aspect: TextureAspect::All, - }, - data.data, - ImageDataLayout { - offset: 0, - bytes_per_row: Some(width * data.format.n_channels() as u32), - rows_per_image: Some(height), - }, - size, - ); - - let view = texture.create_view(&TextureViewDescriptor::default()); - Self { - texture, - view, - queue: Arc::clone(state.queue()), - } - } - - fn update(&self, data: Data<(u32, u32)>) -> Result<(), InvalidSize> { - use wgpu::*; - - let (width, height) = data.size; - let size = Extent3d { - width, - height, - depth_or_array_layers: 1, - }; - - if size != self.texture.size() { - return Err(InvalidSize); - } - - self.queue.write_texture( - ImageCopyTexture { - texture: &self.texture, - mip_level: 0, - origin: Origin3d::ZERO, - aspect: TextureAspect::All, - }, - data.data, - ImageDataLayout { - offset: 0, - bytes_per_row: Some(4 * width), - rows_per_image: Some(height), - }, - size, - ); - - Ok(()) - } -} - -/// An error returned from the texture updation. -#[derive(Debug)] -pub struct InvalidSize; diff --git a/dunge/src/shader_data/textures.rs b/dunge/src/shader_data/textures.rs deleted file mode 100644 index 115c6a1..0000000 --- a/dunge/src/shader_data/textures.rs +++ /dev/null @@ -1,188 +0,0 @@ -use { - crate::{ - layer::Layer, - pipeline::Textures as Bindings, - render::State, - shader::{Shader, ShaderInfo}, - shader_data::{data::TextureData, texture::Texture}, - }, - std::marker::PhantomData, - wgpu::{BindGroup, BindGroupLayout}, -}; - -/// Shader textures. -/// -/// Can be created from the [context](crate::Context) by calling -/// the [`textures_builder`](crate::Context::textures_builder) function. -pub struct Textures { - group: u32, - bind_group: BindGroup, - textures: Vec, - ty: PhantomData, -} - -impl Textures { - fn new(params: Parameters, state: &State) -> Self { - use { - std::iter, - wgpu::{ - AddressMode, BindGroupDescriptor, BindGroupEntry, BindingResource, FilterMode, - SamplerDescriptor, - }, - }; - - let Parameters { - bindings, - variables, - layout, - } = params; - - let textures: Vec<_> = variables - .maps - .into_iter() - .map(|map| match map { - Map::Data(data) => Texture::new(data, state), - Map::Texture(texture) => texture, - }) - .collect(); - - let mut entries = Vec::with_capacity(textures.len() + 1); - for (texture, &binding) in iter::zip(&textures, &bindings.map.tmaps) { - entries.push(BindGroupEntry { - binding, - resource: BindingResource::TextureView(texture.view()), - }); - } - - let device = state.device(); - let sampler = device.create_sampler(&SamplerDescriptor { - address_mode_u: AddressMode::ClampToEdge, - address_mode_v: AddressMode::ClampToEdge, - address_mode_w: AddressMode::ClampToEdge, - mag_filter: FilterMode::Nearest, - min_filter: FilterMode::Nearest, - ..Default::default() - }); - - entries.push(BindGroupEntry { - binding: bindings.map.smap, - resource: BindingResource::Sampler(&sampler), - }); - - Self { - group: bindings.group, - bind_group: state.device().create_bind_group(&BindGroupDescriptor { - layout, - entries: &entries, - label: Some("texture bind group"), - }), - textures, - ty: PhantomData, - } - } - - /// Returns the [texture](Texture) map by index. - /// - /// # Panics - /// Panics if the shader has no texture map with given index. - pub fn get_map(&self, index: usize) -> &Texture - where - S: Shader, - { - assert!( - index < self.textures.len(), - "the shader has no a texture map with index {index}", - ); - - &self.textures[index] - } - - pub(crate) fn bind(&self) -> (u32, &BindGroup) { - (self.group, &self.bind_group) - } -} - -struct Parameters<'a> { - variables: Variables<'a>, - bindings: &'a Bindings, - layout: &'a BindGroupLayout, -} - -#[derive(Default)] -struct Variables<'a> { - maps: Vec>, -} - -/// The [textures](Textures) builder. -#[must_use] -pub struct Builder<'a> { - state: &'a State, - variables: Variables<'a>, -} - -impl<'a> Builder<'a> { - pub(crate) fn new(state: &'a State) -> Self { - Self { - state, - variables: Variables::default(), - } - } - - /// Sets a texture map for the textures object. - pub fn with_map(mut self, map: M) -> Self - where - M: Into>, - { - self.variables.maps.push(map.into()); - self - } - - /// Builds the textures. - /// - /// # Panics - /// Panics if the shader requires texture `map`, but it's not set. - pub fn build(self, layer: &Layer) -> Textures - where - S: Shader, - { - let actual = self.variables.maps.len(); - let info = ShaderInfo::new::(); - let expected = info.texture_maps(); - - assert_eq!( - actual, expected, - "the shader requires {expected} texture maps, but {actual} is set", - ); - - let textures = layer - .pipeline() - .textures() - .expect("the shader has no textures"); - - let params = Parameters { - variables: self.variables, - bindings: &textures.bindings, - layout: &textures.layout, - }; - - Textures::new(params, self.state) - } -} - -/// The texture map parameter. -pub enum Map<'a> { - Data(TextureData<'a>), - Texture(Texture), -} - -impl<'a> From> for Map<'a> { - fn from(v: TextureData<'a>) -> Self { - Self::Data(v) - } -} - -impl From for Map<'_> { - fn from(v: Texture) -> Self { - Self::Texture(v) - } -} diff --git a/dunge/src/time.rs b/dunge/src/time.rs deleted file mode 100644 index 02bced4..0000000 --- a/dunge/src/time.rs +++ /dev/null @@ -1,46 +0,0 @@ -use instant::Instant; - -pub(crate) struct Time { - last: Instant, - delta: f32, -} - -impl Time { - pub fn now() -> Self { - Self { - last: Instant::now(), - delta: 0., - } - } - - pub fn delta(&mut self) -> f32 { - let now = Instant::now(); - let delta = now.duration_since(self.last); - self.last = now; - self.delta += delta.as_secs_f32(); - self.delta - } - - pub fn reset(&mut self) { - self.delta = 0.; - } -} - -#[derive(Default)] -pub(crate) struct Fps { - timer: f32, - counter: u32, -} - -impl Fps { - pub fn count(&mut self, delta_time: f32) -> Option { - self.timer += delta_time; - self.counter += 1; - (self.timer > 1.).then(|| { - self.timer = 0.; - let n = self.counter; - self.counter = 0; - n - }) - } -} diff --git a/dunge/src/topology.rs b/dunge/src/topology.rs deleted file mode 100644 index 02924f0..0000000 --- a/dunge/src/topology.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! Topology types and traits. - -use {bytemuck::Pod, wgpu::PrimitiveTopology}; - -/// The topology type of the [topology](crate::topology::Topology) trait. -pub struct TopologyValue(PrimitiveTopology); - -impl TopologyValue { - pub(crate) fn into_inner(self) -> PrimitiveTopology { - let Self(value) = self; - value - } -} - -/// The topology trait. Specifies how the mesh is presented. -pub trait Topology { - type Face: Pod; - const N: usize; - const VALUE: TopologyValue; -} - -/// Represents a vertex data as a list of points. -#[derive(Clone, Copy)] -pub struct PointList; - -impl Topology for PointList { - type Face = u16; - const N: usize = 1; - const VALUE: TopologyValue = TopologyValue(PrimitiveTopology::PointList); -} - -/// Represents a vertex data as a list of lines. -#[derive(Clone, Copy)] -pub struct LineList; - -impl Topology for LineList { - type Face = [u16; Self::N]; - const N: usize = 2; - const VALUE: TopologyValue = TopologyValue(PrimitiveTopology::LineList); -} - -/// Represents a vertex data as a line strip. -#[derive(Clone, Copy)] -pub struct LineStrip; - -impl Topology for LineStrip { - type Face = u16; - const N: usize = 1; - const VALUE: TopologyValue = TopologyValue(PrimitiveTopology::LineStrip); -} - -/// Represents a vertex data as a list of triangles. -#[derive(Clone, Copy)] -pub struct TriangleList; - -impl Topology for TriangleList { - type Face = [u16; Self::N]; - const N: usize = 3; - const VALUE: TopologyValue = TopologyValue(PrimitiveTopology::TriangleList); -} - -/// Represents a vertex data as a triangle strip. -#[derive(Clone, Copy)] -pub struct TriangleStrip; - -impl Topology for TriangleStrip { - type Face = u16; - const N: usize = 1; - const VALUE: TopologyValue = TopologyValue(PrimitiveTopology::TriangleStrip); -} diff --git a/dunge/src/transform.rs b/dunge/src/transform.rs deleted file mode 100644 index 9b2c20e..0000000 --- a/dunge/src/transform.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! Model transformation types and traits. - -use { - crate::shader_data::ModelTransform, - glam::{Mat4, Quat, Vec3}, -}; - -/// An instance transform. -/// -/// It descrides position, rotation and scale. -#[derive(Clone, Copy)] -pub struct Transform { - /// The position in X, Y, Z coordinates. - pub pos: Vec3, - - /// The rotation expressed by a quaternion. - pub rot: Quat, - - /// The scale in X, Y, Z axes. - pub scl: Vec3, -} - -impl Transform { - pub fn from_position

(pos: P) -> Self - where - P: Into, - { - Self { - pos: pos.into(), - ..Default::default() - } - } -} - -impl Default for Transform { - fn default() -> Self { - Self { - pos: Vec3::ZERO, - rot: Quat::IDENTITY, - scl: Vec3::ONE, - } - } -} - -impl From for ModelTransform { - fn from(Transform { pos, rot, scl }: Transform) -> Self { - let mat = Mat4::from_scale_rotation_translation(scl, rot, pos); - Self::from(mat) - } -} diff --git a/dunge/src/vertex.rs b/dunge/src/vertex.rs deleted file mode 100644 index 8383166..0000000 --- a/dunge/src/vertex.rs +++ /dev/null @@ -1,195 +0,0 @@ -//! Vertex components. - -use crate::vertex::private::Format; - -/// Vertex type description. -/// -/// To use a vertex type, you need to describe its fields. -/// For example, if a component of the vertex position is `[f32; 3]`, then the same type -/// must be specified in [`Position`](Vertex::Position) of the trait implementation. -/// If some component of the vertex is not used, the corresponding type must be specified as `()`. -/// -/// Because all trait types must be [`Component`] no other types can be part of the vertex. -/// -/// This implementation also requires some safety invariant, so the trait is `unsafe`. -/// You can safely implement the trait for your type using [deriving](derive@crate::Vertex). -/// -/// # Safety -/// * The fields descriptions must exactly match the actual component types or be the unit `()`. -/// * The fields of `Self` must be ordered, so the struct must have the `#[repr(C)]` attribute. -/// * The fields must be in the same order as they are listed in the trait. -/// * The `Self` type must not have any other fields than those described by components. -/// -/// # Example -/// Let's say we want to create a `Vert` type with a position and a texture map. -/// Then the [`Vertex`] implementation for the type would be: -/// ```rust -/// use dunge::Vertex; -/// -/// #[repr(C)] -/// struct Vert { -/// pos: [f32; 3], -/// map: [f32; 2], -/// } -/// -/// unsafe impl Vertex for Vert { -/// type Position = [f32; 3]; // position type -/// type Color = (); // color component is not used -/// type Texture = [f32; 2]; // texture type -/// } -/// ``` -/// -/// Note that the implementation of the trait requires `unsafe` code, -/// so instead of writing this yourself you can use [deriving](derive@crate::Vertex): -/// ```rust -/// use dunge::Vertex; -/// -/// #[repr(C)] -/// #[derive(Vertex)] -/// struct Vert { -/// #[position] -/// pos: [f32; 3], -/// #[texture] -/// map: [f32; 2], -/// } -/// ``` -/// -pub unsafe trait Vertex { - type Position: Component; - type Color: Component3D; - type Texture: Component2D; -} - -pub(crate) fn verts_as_bytes(verts: &[V]) -> &[u8] -where - V: Vertex, -{ - use std::{mem, slice}; - - // Safety: all vertices consist of components, so they can be safely cast into bytes - unsafe { slice::from_raw_parts(verts.as_ptr().cast(), mem::size_of_val(verts)) } -} - -pub(crate) struct VertexInfo { - pub dimensions: u64, - pub has_color: bool, - pub has_texture: bool, -} - -impl VertexInfo { - pub const fn new() -> Self - where - V: Vertex, - { - Self { - dimensions: V::Position::N_FLOATS, - has_color: V::Color::OPTIONAL_N_FLOATS.is_some(), - has_texture: V::Texture::OPTIONAL_N_FLOATS.is_some(), - } - } -} - -/// The component is something that a [vertex](Vertex) can consist of. -/// -/// Available componets are: -/// * `(f32, f32)` -/// * `(f32, f32, f32)` -/// * `[f32; 2]` -/// * `[f32; 3]` -/// * [`Vec2`](glam::Vec2) -/// * [`Vec3`](glam::Vec3) -/// -pub trait Component: private::Component { - const N_FLOATS: u64; -} - -impl Component for C -where - C: private::Component, -{ - const N_FLOATS: u64 = C::Format::N_FLOATS; -} - -/// The 2D component. -pub trait Component2D: private::OptionalComponent { - const OPTIONAL_N_FLOATS: Option; -} - -impl Component2D for C -where - C: Component, -{ - const OPTIONAL_N_FLOATS: Option = Some(C::Format::N_FLOATS); -} - -/// Specify this type if a component is not used. -impl Component2D for () { - const OPTIONAL_N_FLOATS: Option = None; -} - -/// The 3D component. -pub trait Component3D: private::OptionalComponent { - const OPTIONAL_N_FLOATS: Option; -} - -impl Component3D for C -where - C: Component, -{ - const OPTIONAL_N_FLOATS: Option = Some(C::Format::N_FLOATS); -} - -/// Specify this type if a component is not used. -impl Component3D for () { - const OPTIONAL_N_FLOATS: Option = None; -} - -mod private { - use glam::{Vec2, Vec3}; - - pub trait Format { - const N_FLOATS: u64; - } - - pub struct FormatFloatX2; - impl Format for FormatFloatX2 { - const N_FLOATS: u64 = 2; - } - - pub struct FormatFloatX3; - impl Format for FormatFloatX3 { - const N_FLOATS: u64 = 3; - } - - pub trait Component { - type Format: Format; - } - - impl Component for (f32, f32) { - type Format = FormatFloatX2; - } - - impl Component for (f32, f32, f32) { - type Format = FormatFloatX3; - } - - impl Component for [f32; 2] { - type Format = FormatFloatX2; - } - - impl Component for [f32; 3] { - type Format = FormatFloatX3; - } - - impl Component for Vec2 { - type Format = FormatFloatX2; - } - - impl Component for Vec3 { - type Format = FormatFloatX3; - } - - pub trait OptionalComponent {} - impl OptionalComponent for C where C: Component {} - impl OptionalComponent for () {} -} diff --git a/dunge/src/view.rs b/dunge/src/view.rs deleted file mode 100644 index f8fb78a..0000000 --- a/dunge/src/view.rs +++ /dev/null @@ -1,194 +0,0 @@ -use { - crate::shader_data::ModelTransform, - glam::{Mat4, Quat, Vec3}, - std::sync::{Arc, Mutex, MutexGuard}, -}; - -/// The camera view. -#[derive(Clone, Copy)] -pub struct View { - /// Eye 3d point. - pub eye: Vec3, - - /// Look at 3d point. - pub look: Vec3, - - /// Up direction. - pub up: Vec3, - - /// Camera projection. - pub proj: Projection, -} - -impl View { - pub fn rotation(&self) -> Quat { - let mat = Mat4::look_at_rh(self.eye, self.look, self.up); - let (_, rot, _) = mat.to_scale_rotation_translation(); - rot - } - - fn model(&self, (width, height): (f32, f32)) -> ModelTransform { - let proj = match self.proj { - Projection::Perspective(Perspective { fovy, znear, zfar }) => { - Mat4::perspective_rh(fovy, width / height, znear, zfar) - } - Projection::Orthographic(Orthographic { - width_factor, - height_factor, - near, - far, - }) => { - let wh = width * width_factor * 0.5; - let left = -wh; - let right = wh; - - let hh = height * height_factor * 0.5; - let bottom = -hh; - let top = hh; - - Mat4::orthographic_rh(left, right, bottom, top, near, far) - } - }; - - let view = Mat4::look_at_rh(self.eye, self.look, self.up); - ModelTransform::from(proj * view) - } -} - -impl Default for View { - fn default() -> Self { - Self { - eye: Vec3::Z, - look: Vec3::ZERO, - up: Vec3::Y, - proj: Projection::default(), - } - } -} - -/// The camera projection. -#[derive(Clone, Copy)] -pub enum Projection { - Perspective(Perspective), - Orthographic(Orthographic), -} - -impl Default for Projection { - fn default() -> Self { - Self::Orthographic(Orthographic::default()) - } -} - -impl From for Projection { - fn from(v: Perspective) -> Self { - Self::Perspective(v) - } -} - -impl From for Projection { - fn from(v: Orthographic) -> Self { - Self::Orthographic(v) - } -} - -/// Perspective projection. -#[derive(Clone, Copy)] -pub struct Perspective { - pub fovy: f32, - pub znear: f32, - pub zfar: f32, -} - -impl Default for Perspective { - fn default() -> Self { - Self { - fovy: 1.6, - znear: 0.1, - zfar: 100., - } - } -} - -/// Orthographic projection. -#[derive(Clone, Copy)] -pub struct Orthographic { - pub width_factor: f32, - pub height_factor: f32, - pub near: f32, - pub far: f32, -} - -impl Default for Orthographic { - fn default() -> Self { - Self { - width_factor: 1., - height_factor: 1., - near: -100., - far: 100., - } - } -} - -/// The view handle. This allows to update [views](View) remotely. -/// -/// Can be created from a [view](View) object. -#[derive(Clone, Default)] -pub struct ViewHandle(Arc>); - -impl ViewHandle { - /// Updates the view with a new [view](View). - pub fn update_view(&self, view: View) { - *self.inner() = Inner::new(view); - } - - /// Returns the view [model](ModelTransform). - pub fn model(&self, size: (u32, u32)) -> ModelTransform { - self.inner().model(size) - } - - fn inner(&self) -> MutexGuard { - self.0.lock().expect("lock inner") - } -} - -impl From for ViewHandle { - /// Converts to view [handle](ViewHandle) from the [view](View). - fn from(view: View) -> Self { - let inner = Inner::new(view); - Self(Arc::new(Mutex::new(inner))) - } -} - -#[derive(Default)] -struct Inner { - view: View, - cache: Option, -} - -impl Inner { - fn new(view: View) -> Self { - Self { view, cache: None } - } - - fn model(&mut self, (width, height): (u32, u32)) -> ModelTransform { - match self.cache { - Some(Cache { size, .. }) if (width, height) != size => {} - Some(Cache { model, .. }) => return model, - None => {} - } - - let model = self.view.model((width as f32, height as f32)); - self.cache = Some(Cache { - size: (width, height), - model, - }); - - model - } -} - -#[derive(Clone, Copy)] -struct Cache { - size: (u32, u32), - model: ModelTransform, -} diff --git a/dunge_macros/Cargo.toml b/dunge_macros/Cargo.toml index 5d713b1..13a071b 100644 --- a/dunge_macros/Cargo.toml +++ b/dunge_macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dunge_macros" -version = "0.2.2" +version = "0.2.3" edition = "2021" description = "Procmacro for the dunge library" license = "MIT" diff --git a/dunge_shader/Cargo.toml b/dunge_shader/Cargo.toml index 6819717..6176ab2 100644 --- a/dunge_shader/Cargo.toml +++ b/dunge_shader/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dunge_shader" -version = "0.2.2" +version = "0.2.3" edition = "2021" description = "Shader generator for the dunge library" license = "MIT" diff --git a/dunge_shader/src/lib.rs b/dunge_shader/src/lib.rs index ba48eb5..8b13789 100644 --- a/dunge_shader/src/lib.rs +++ b/dunge_shader/src/lib.rs @@ -1,35 +1 @@ -mod nodes; -mod out; -mod shader; -mod templater; -mod parts { - mod ambient; - mod color; - mod instance; - mod post; - mod sources; - mod spaces; - mod textures; - mod vertex; - mod view; - - pub(crate) use self::{ - ambient::Ambient, - instance::{InstanceColorInput, InstanceInput}, - post::Post, - vertex::{VertexInput, VertexOutput}, - }; - - pub use self::{ - color::Color, - post::Vignette, - sources::{SourceArray, SourceArrays, SourceBindings, SourceKind}, - spaces::{LightSpaces, SpaceBindings, SpaceKind}, - textures::{TextureBindings, TexturesNumber}, - vertex::{Dimension, Fragment}, - view::ViewKind, - }; -} - -pub use crate::{parts::*, shader::*}; diff --git a/dunge_shader/src/nodes.rs b/dunge_shader/src/nodes.rs deleted file mode 100644 index b6a2c97..0000000 --- a/dunge_shader/src/nodes.rs +++ /dev/null @@ -1,153 +0,0 @@ -use std::fmt; - -pub(crate) struct Struct { - pub name: &'static str, - pub fields: Vec, -} - -impl fmt::Display for Struct { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let Self { name, fields } = self; - writeln!(f, "struct {name} {{")?; - for field in fields { - writeln!(f, "{field}")?; - } - writeln!(f, "}}")?; - writeln!(f) - } -} - -#[derive(Clone, Copy)] -pub(crate) struct Field { - pub location: Location, - pub name: Name, - pub ty: Type, -} - -impl fmt::Display for Field { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let &Self { location, name, ty } = self; - write!(f, " {location}{name}: {ty},") - } -} - -#[derive(Clone, Copy)] -pub(crate) enum Location { - None, - Num(u8), - Position, -} - -impl Location { - pub fn new() -> Self { - Self::Num(0) - } - - pub fn next(&mut self) -> Self { - let old = *self; - if let Self::Num(n) = self { - *n += 1; - } - - old - } -} - -impl fmt::Display for Location { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::None => Ok(()), - Self::Num(n) => write!(f, "@location({n}) "), - Self::Position => write!(f, "@builtin(position) "), - } - } -} - -#[derive(Clone, Copy)] -pub(crate) struct Binding { - group: u32, - binding: u32, -} - -impl Binding { - pub fn with_group(group: u32) -> Self { - Self { group, binding: 0 } - } - - pub fn next(&mut self) -> Self { - let old = *self; - self.binding += 1; - old - } - - pub fn get(self) -> u32 { - self.binding - } -} - -#[derive(Clone, Copy)] -pub(crate) struct Var { - pub binding: Binding, - pub uniform: bool, - pub name: Name, - pub ty: Type, -} - -impl fmt::Display for Var { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let &Self { - binding: Binding { group, binding }, - uniform, - name, - ty, - } = self; - - writeln!( - f, - "@group({group}) @binding({binding}) var{spec} {name}: {ty};", - spec = if uniform { "" } else { "" }, - ) - } -} - -#[derive(Clone, Copy)] -pub(crate) enum Name { - Str(&'static str), - Num { str: &'static str, n: u32 }, -} - -impl fmt::Display for Name { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Str(str) => write!(f, "{str}"), - Self::Num { str, n } => write!(f, "{str}_{n}"), - } - } -} - -#[derive(Clone, Copy)] -pub(crate) enum Type { - Simple(&'static str), - Array { ty: &'static Self, size: u8 }, -} - -impl Type { - pub const F32: Self = Self::Simple("f32"); - pub const U32: Self = Self::Simple("u32"); - pub const VEC2: Self = Self::Simple("vec2"); - pub const VEC3: Self = Self::Simple("vec3"); - pub const VEC4: Self = Self::Simple("vec4"); - pub const MAT4: Self = Self::Simple("mat4x4"); - pub const TEXTURE2D: Self = Self::Simple("texture_2d"); - pub const TEXTURE3D: Self = Self::Simple("texture_3d"); - pub const SAMPLER: Self = Self::Simple("sampler"); -} - -impl fmt::Display for Type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Simple(ty) => write!(f, "{ty}"), - Self::Array { ty, size } => write!(f, "array<{ty}, {size}>"), - } - } -} diff --git a/dunge_shader/src/out.rs b/dunge_shader/src/out.rs deleted file mode 100644 index 64e50cd..0000000 --- a/dunge_shader/src/out.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::{ - fmt::{Display, Write}, - ops, -}; - -pub(crate) struct Out { - buf: String, -} - -impl Out { - pub fn new() -> Self { - Self { - buf: String::with_capacity(128), - } - } - - pub fn write(&mut self, d: D) -> &mut Self - where - D: Display, - { - _ = write!(self.buf, "{d}"); - self - } - - pub fn write_f32(&mut self, f: f32) -> &mut Self { - if f.is_finite() { - _ = write!(self.buf, "{f:?}"); - } else { - self.buf.push_str("0.0"); - } - - self - } - - pub fn write_str(&mut self, s: &str) -> &mut Self { - self.buf.push_str(s); - self - } - - pub fn separated<'a>(&'a mut self, sep: &'a str) -> Separated<'a> { - Separated { - out: self, - sep, - add: false, - } - } -} - -impl ops::Deref for Out { - type Target = str; - - fn deref(&self) -> &Self::Target { - &self.buf - } -} - -pub(crate) struct Separated<'a> { - out: &'a mut Out, - sep: &'a str, - add: bool, -} - -impl Separated<'_> { - pub fn out(&mut self) -> &mut Out { - if self.add { - self.out.write_str(self.sep); - } else { - self.add = true; - } - - self.out - } - - pub fn write_default(&mut self, s: &str) { - if !self.add { - self.out.write_str(s); - } - } -} diff --git a/dunge_shader/src/parts/ambient.rs b/dunge_shader/src/parts/ambient.rs deleted file mode 100644 index 6bfad79..0000000 --- a/dunge_shader/src/parts/ambient.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::{nodes::*, out::Out}; - -pub(crate) struct Ambient; - -impl Ambient { - pub fn declare_group(binding: &mut Binding, o: &mut Out) -> u32 { - let binding = binding.next(); - o.write(Var { - binding, - uniform: true, - name: Name::Str("ambient"), - ty: Type::VEC4, - }); - - binding.get() - } -} diff --git a/dunge_shader/src/parts/color.rs b/dunge_shader/src/parts/color.rs deleted file mode 100644 index e584b27..0000000 --- a/dunge_shader/src/parts/color.rs +++ /dev/null @@ -1,7 +0,0 @@ -/// A color in the shader. -#[derive(Clone, Copy)] -pub struct Color { - pub r: f32, - pub g: f32, - pub b: f32, -} diff --git a/dunge_shader/src/parts/instance.rs b/dunge_shader/src/parts/instance.rs deleted file mode 100644 index be4bb82..0000000 --- a/dunge_shader/src/parts/instance.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::{nodes::*, out::Out}; - -pub(crate) struct InstanceInput; - -impl InstanceInput { - pub fn define_type(location: &mut Location, o: &mut Out) { - const FIELDS: [&str; 4] = ["r0", "r1", "r2", "r3"]; - - o.write(Struct { - name: "InstanceInput", - fields: FIELDS - .into_iter() - .map(|name| Field { - location: location.next(), - name: Name::Str(name), - ty: Type::VEC4, - }) - .collect(), - }); - } -} - -#[derive(Clone, Copy)] -pub(crate) struct InstanceColorInput { - enable: bool, -} - -impl InstanceColorInput { - pub fn new(enable: bool) -> Self { - Self { enable } - } - - pub fn define_type(self, location: &mut Location, o: &mut Out) { - if self.enable { - o.write(Struct { - name: "InstanceColorInput", - fields: vec![Field { - location: location.next(), - name: Name::Str("col"), - ty: Type::VEC3, - }], - }); - } - } - - pub fn define_input(self, o: &mut Out) { - if self.enable { - o.write_str(" instcol: InstanceColorInput,"); - } - } -} diff --git a/dunge_shader/src/parts/post.rs b/dunge_shader/src/parts/post.rs deleted file mode 100644 index 022c88e..0000000 --- a/dunge_shader/src/parts/post.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::out::Out; - -pub(crate) struct Post { - pub antialiasing: bool, - pub vignette: Vignette, -} - -impl Post { - pub fn calc_fragment(&self, o: &mut Out) { - if self.antialiasing { - o.write_str( - "\ - let s0 = vec2(data.step.x, data.step.y);\n \ - let s1 = vec2(-data.step.x, data.step.y);\n \ - let s2 = vec2(data.step.x, -data.step.y);\n \ - let s3 = vec2(-data.step.x, -data.step.y);\n \ - col = (\n \ - textureSample(tmap, smap, in.map + s0)\n \ - + textureSample(tmap, smap, in.map + s1)\n \ - + textureSample(tmap, smap, in.map + s2)\n \ - + textureSample(tmap, smap, in.map + s3)\n \ - ) * 0.25;\n ", - ); - } else { - o.write_str("col = textureSample(tmap, smap, in.map);\n "); - } - - if let Vignette::Color { r, g, b, f } = self.vignette { - o.write_str("\n ") - .write_str("let vcol = vec4(") - .write_f32(r) - .write_str(", ") - .write_f32(g) - .write_str(", ") - .write_f32(b) - .write_str(", 1.);\n ") - .write_str("let vforce = length(in.uni * 2. - 1.) * ") - .write_f32(f) - .write_str(";\n ") - .write_str("col = mix(col, vcol, vforce);\n "); - } - } -} - -#[derive(Clone, Copy, Default, PartialEq)] -pub enum Vignette { - #[default] - None, - Color { - r: f32, - g: f32, - b: f32, - f: f32, - }, -} diff --git a/dunge_shader/src/parts/sources.rs b/dunge_shader/src/parts/sources.rs deleted file mode 100644 index 14d0f75..0000000 --- a/dunge_shader/src/parts/sources.rs +++ /dev/null @@ -1,165 +0,0 @@ -use crate::{nodes::*, out::Out}; - -/// Light source arrays. Described by a slice of [source arrays](SourceArray). -#[derive(Clone, Copy)] -pub struct SourceArrays(&'static [SourceArray]); - -impl SourceArrays { - /// Empty light sources. - pub const EMPTY: Self = Self(&[]); - - /// Creates a new [`SourceArrays`] from the slice of [`SourceArray`]. - /// - /// # Panics - /// Panic if the number of source arrays exceeds the maximum allowed. - pub const fn new(arrays: &'static [SourceArray]) -> Self { - assert!( - arrays.len() <= 4, - "the number of source arrays cannot be greater than 4", - ); - - Self(arrays) - } - - /// Returns the length of light source arrays. - pub const fn len(&self) -> usize { - self.0.len() - } - - /// Checks if the light source arrays is empty. - pub const fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub(crate) fn define_type(&self, o: &mut Out) { - if self.is_empty() { - return; - } - - o.write(Struct { - name: "Source", - fields: vec![ - Field { - location: Location::None, - name: Name::Str("col"), - ty: Type::VEC3, - }, - Field { - location: Location::None, - name: Name::Str("rad"), - ty: Type::F32, - }, - Field { - location: Location::None, - name: Name::Str("pos"), - ty: Type::VEC3, - }, - ], - }) - .write(Struct { - name: "Len", - fields: vec![ - Field { - location: Location::None, - name: Name::Str("n"), - ty: Type::U32, - }, - Field { - location: Location::None, - name: Name::Str("pad0"), - ty: Type::U32, - }, - Field { - location: Location::None, - name: Name::Str("pad1"), - ty: Type::U32, - }, - Field { - location: Location::None, - name: Name::Str("pad2"), - ty: Type::U32, - }, - ], - }); - } - - pub(crate) fn declare_group(&self, binding: &mut Binding, o: &mut Out) -> Vec { - self.enumerate() - .map(|(n, SourceArray { size, .. })| SourceBindings { - binding_array: { - let binding = binding.next(); - o.write(Var { - binding, - uniform: true, - name: Name::Num { - str: "sources_array", - n, - }, - ty: Type::Array { - ty: &Type::Simple("Source"), - size, - }, - }); - - binding.get() - }, - binding_len: { - let binding = binding.next(); - o.write(Var { - binding, - uniform: true, - name: Name::Num { - str: "sources_len", - n, - }, - ty: Type::Simple("Len"), - }); - - binding.get() - }, - size, - }) - .collect() - } - - pub(crate) fn enumerate(&self) -> impl Iterator { - (0..).zip(self.0.iter().copied()) - } -} - -/// The light source array. -#[derive(Clone, Copy)] -pub struct SourceArray { - kind: SourceKind, - size: u8, -} - -impl SourceArray { - /// Creates a new [`SourceArray`]. - /// - /// # Panics - /// This function will panic if the source array have a zero size or is larger than 127. - pub const fn new(kind: SourceKind, size: u8) -> Self { - assert!(size != 0, "source array cannot have size equal to zero"); - assert!(size <= 127, "source array cannot be larger than 127"); - Self { kind, size } - } - - pub(crate) fn kind(self) -> SourceKind { - self.kind - } -} - -/// The light sources kind. Describes the nature of light. -#[derive(Clone, Copy)] -pub enum SourceKind { - Glow, - Gloom, -} - -#[derive(Clone, Copy)] -pub struct SourceBindings { - pub binding_array: u32, - pub binding_len: u32, - pub size: u8, -} diff --git a/dunge_shader/src/parts/spaces.rs b/dunge_shader/src/parts/spaces.rs deleted file mode 100644 index 9590624..0000000 --- a/dunge_shader/src/parts/spaces.rs +++ /dev/null @@ -1,161 +0,0 @@ -use crate::{nodes::*, out::Out}; - -/// Light spaces. Described by a slice of [space kinds](SpaceKind). -#[derive(Clone, Copy)] -pub struct LightSpaces(&'static [SpaceKind]); - -impl LightSpaces { - /// Empty light spaces. - pub const EMPTY: Self = Self(&[]); - - /// Creates a new [`LightSpaces`] from the slice of [`SpaceKind`]. - /// - /// # Panics - /// Panic if the number of spaces exceeds the maximum allowed. - pub const fn new(spaces: &'static [SpaceKind]) -> Self { - assert!( - spaces.len() <= 4, - "the number of light spaces cannot be greater than 4", - ); - - Self(spaces) - } - - /// Returns the length of light space. - pub const fn len(&self) -> usize { - self.0.len() - } - - /// Checks if the light space is empty. - pub const fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub(crate) fn define_type(&self, o: &mut Out) { - if self.is_empty() { - return; - } - - o.write(Struct { - name: "Space", - fields: vec![ - Field { - location: Location::None, - name: Name::Str("model"), - ty: Type::MAT4, - }, - Field { - location: Location::None, - name: Name::Str("col"), - ty: Type::VEC3, - }, - ], - }); - } - - pub(crate) fn declare_group(&self, binding: &mut Binding, o: &mut Out) -> SpaceBindings { - if self.is_empty() { - return SpaceBindings::default(); - } - - SpaceBindings { - spaces: { - let binding = binding.next(); - o.write(Var { - binding, - uniform: true, - name: Name::Str("spaces"), - ty: Type::Array { - ty: &Type::Simple("Space"), - size: self.len() as u8, - }, - }); - - binding.get() - }, - tspaces: self - .enumerate() - .map(|(n, ..)| { - let binding = binding.next(); - o.write(Var { - binding, - uniform: false, - name: Name::Num { str: "tspace", n }, - ty: Type::TEXTURE3D, - }); - - binding.get() - }) - .collect(), - sspace: { - let binding = binding.next(); - o.write(Var { - binding, - uniform: false, - name: Name::Str("sspace"), - ty: Type::SAMPLER, - }); - - binding.get() - }, - } - } - - pub fn enumerate(&self) -> impl Iterator { - (0..).zip(self.0.iter().copied()) - } -} - -/// The light spaces kind. Describes the number of color components in the space. -#[derive(Clone, Copy)] -pub enum SpaceKind { - Rgba, - Gray, -} - -impl SpaceKind { - pub(crate) fn calc(self, name: &str, index: u32, o: &mut Out) { - o.write_str("var ") - .write_str(name) - .write_str(" = textureSampleLevel(tspace_") - .write(index) - .write_str(", sspace, out.space_") - .write(index) - .write_str(", 0.);\n ") - .write_str("space = "); - - if index == 0 { - o.write_str(name) - .write_str(match self { - Self::Rgba => ".rgb", - Self::Gray => ".rrr", - }) - .write_str(" * spaces[") - .write(index) - .write_str("].col;\n "); - } else { - o.write_str("max(space, ") - .write_str(name) - .write_str(match self { - Self::Rgba => ".rgb", - Self::Gray => ".rrr", - }) - .write_str(" * spaces[") - .write(index) - .write_str("].col);\n "); - } - } -} - -#[derive(Clone, Default)] -pub struct SpaceBindings { - pub spaces: u32, - pub tspaces: Vec, - pub sspace: u32, -} - -impl SpaceBindings { - pub fn is_empty(&self) -> bool { - self.tspaces.is_empty() - } -} diff --git a/dunge_shader/src/parts/textures.rs b/dunge_shader/src/parts/textures.rs deleted file mode 100644 index fecfef1..0000000 --- a/dunge_shader/src/parts/textures.rs +++ /dev/null @@ -1,129 +0,0 @@ -use crate::{nodes::*, out::Out}; - -/// Sets the number of texture maps in the shader. -#[must_use] -#[derive(Clone, Copy)] -pub struct TexturesNumber { - n: u8, - discard: Option, - gray: bool, -} - -impl TexturesNumber { - pub const N0: Self = Self::new(0); - pub const N1: Self = Self::new(1); - pub const N2: Self = Self::new(2); - pub const N3: Self = Self::new(3); - pub const N4: Self = Self::new(4); - - const fn new(n: u8) -> Self { - Self { - n, - discard: None, - gray: false, - } - } - - /// Discard a pixel if its alpha value is less than the specified. - pub const fn with_discard_threshold(mut self, value: f32) -> Self { - self.discard = Some(value); - self - } - - /// Enables gray mode. - pub const fn with_gray_mode(mut self) -> Self { - self.gray = true; - self - } - - /// Returns the number of texture maps. - pub const fn len(self) -> usize { - self.n as usize - } - - /// Checks if the number of texture maps is zero. - pub const fn is_empty(self) -> bool { - self.n == 0 - } - - pub(crate) fn has_textures(self) -> bool { - !self.is_empty() - } - - pub(crate) fn declare_group(self, binding: &mut Binding, o: &mut Out) -> TextureBindings { - if !self.has_textures() { - return TextureBindings::default(); - } - - let mut tmaps = Vec::with_capacity(self.n as usize); - for n in 0..self.n as u32 { - let binding = binding.next(); - tmaps.push(binding.get()); - o.write(Var { - binding, - uniform: false, - name: Name::Num { str: "tmap", n }, - ty: Type::TEXTURE2D, - }); - } - - let smap = binding.next(); - o.write(Var { - binding: smap, - uniform: false, - name: Name::Str("smap"), - ty: Type::SAMPLER, - }); - - TextureBindings { - tmaps, - smap: smap.get(), - } - } - - pub(crate) fn calc_fragment(self, o: &mut Out) { - match self.n { - 0 => return, - 1 => { - o.write_str("let tex = textureSample(tmap_0, smap, out.map)"); - if self.gray { - o.write_str(".rrrr"); - } - o.write_str(";\n "); - } - num => { - o.write_str("var tex = vec4(0.);\n "); - for n in 0..num as u32 { - o.write_str("let tex_") - .write(n) - .write_str(" = textureSample(tmap_") - .write(n) - .write_str(", smap, out.map)"); - - if self.gray { - o.write_str(".rrrr"); - } - - o.write_str(";\n ") - .write_str("tex = mix(tex, tex_") - .write(n) - .write_str(", tex_") - .write(n) - .write_str(".a);\n "); - } - } - } - - if let Some(value) = self.discard { - o.write_str("if tex.a < ") - .write_f32(value) - .write_str(" { discard; }\n "); - } - } -} - -#[derive(Clone, Default)] -pub struct TextureBindings { - pub tmaps: Vec, - pub smap: u32, -} diff --git a/dunge_shader/src/parts/vertex.rs b/dunge_shader/src/parts/vertex.rs deleted file mode 100644 index 186abf6..0000000 --- a/dunge_shader/src/parts/vertex.rs +++ /dev/null @@ -1,294 +0,0 @@ -use crate::{ - nodes::*, - out::Out, - parts::{ - color::Color, - sources::{SourceArrays, SourceKind}, - spaces::LightSpaces, - textures::TexturesNumber, - view::ViewKind, - }, -}; - -pub(crate) struct VertexInput { - pub fragment: Fragment, - pub pos: Dimension, -} - -impl VertexInput { - pub fn define_type(&self, location: &mut Location, o: &mut Out) { - let mut fields = vec![Field { - location: location.next(), - name: Name::Str("pos"), - ty: match self.pos { - Dimension::D2 => Type::VEC2, - Dimension::D3 => Type::VEC3, - }, - }]; - - if self.fragment.vertex_color { - fields.push(Field { - location: location.next(), - name: Name::Str("col"), - ty: Type::VEC3, - }); - } - - if self.fragment.vertex_texture { - fields.push(Field { - location: location.next(), - name: Name::Str("map"), - ty: Type::VEC2, - }); - } - - o.write(Struct { - name: "VertexInput", - fields, - }); - } - - pub fn calc_world(&self, o: &mut Out) { - o.write_str(match self.pos { - Dimension::D2 => "model * vec4(input.pos, 0., 1.)", - Dimension::D3 => "model * vec4(input.pos, 1.)", - }); - } -} - -#[derive(Clone, Copy)] -pub enum Dimension { - D2, - D3, -} - -pub(crate) struct VertexOutput { - pub fragment: Fragment, - pub static_color: Option, - pub ambient: bool, - pub textures: TexturesNumber, - pub source_arrays: SourceArrays, - pub light_spaces: LightSpaces, - pub instance_colors: bool, -} - -impl VertexOutput { - pub fn define_type(&self, location: &mut Location, o: &mut Out) { - let mut fields = vec![Field { - location: Location::Position, - name: Name::Str("pos"), - ty: Type::VEC4, - }]; - - if self.fragment.vertex_color { - fields.push(Field { - location: location.next(), - name: Name::Str("col"), - ty: Type::VEC3, - }); - } - - if self.fragment.vertex_texture { - fields.push(Field { - location: location.next(), - name: Name::Str("map"), - ty: Type::VEC2, - }); - } - - if self.instance_colors { - fields.push(Field { - location: location.next(), - name: Name::Str("instcol"), - ty: Type::VEC3, - }); - } - - if self.has_sources() { - fields.push(Field { - location: location.next(), - name: Name::Str("world"), - ty: Type::VEC3, - }); - } - - for (n, ..) in self.light_spaces.enumerate() { - fields.push(Field { - location: location.next(), - name: Name::Num { str: "space", n }, - ty: Type::VEC3, - }); - } - - o.write(Struct { - name: "VertexOutput", - fields, - }); - } - - pub fn calc_vertex(&self, input: &VertexInput, camera: ViewKind, o: &mut Out) { - o.write_str("let world = "); - input.calc_world(o); - o.write_str(";\n"); - - o.write_str(" out.pos = "); - camera.calc_view(o); - o.write_str(";\n"); - - if self.fragment.vertex_color { - o.write_str(" out.col = input.col;\n"); - } - - if self.fragment.vertex_texture { - o.write_str(" out.map = input.map;\n"); - } - - if self.instance_colors { - o.write_str(" out.instcol = instcol.col;\n"); - } - - if self.has_sources() { - o.write_str(" out.world = world.xyz;\n"); - } - - for (n, ..) in self.light_spaces.enumerate() { - o.write_str(" out.space_") - .write(n) - .write_str(" = (spaces[") - .write(n) - .write_str("].model * world).xzy;\n"); - } - } - - pub fn calc_fragment(&self, o: &mut Out) { - self.textures.calc_fragment(o); - let has_light = self.calc_light(o); - let mut col = o.write_str("col = ").separated(" * "); - if has_light { - col.out().write_str("light"); - } - - if let Some(Color { r, g, b }) = self.static_color { - col.out() - .write_str("vec3(") - .write_f32(r) - .write_str(", ") - .write_f32(g) - .write_str(", ") - .write_f32(b) - .write_str(")"); - } - - if self.fragment.vertex_color { - col.out().write_str("out.col"); - } - - if self.textures.has_textures() { - col.out().write_str("tex.rgb"); - } - - if self.instance_colors { - col.out().write_str("out.instcol"); - } - - col.write_default("vec3(0.)"); - o.write_str(";\n"); - } - - fn calc_light(&self, o: &mut Out) -> bool { - if !self.ambient && !self.has_sources() && self.light_spaces.is_empty() { - return false; - } - - if self.has_sources() { - o.write_str("\n ") - .write_str("var sources = vec3(0.); \n "); - - for (n, array) in self.source_arrays.enumerate() { - o.write_str("for (var i = 0u; i < ") - .write(Name::Num { - str: "sources_len", - n, - }) - .write_str(".n; i++) {\n ") - .write_str("let src = ") - .write(Name::Num { - str: "sources_array", - n, - }) - .write_str("[i];\n ") - .write_str( - "if out.world.x > src.pos.x - src.rad && out.world.x < src.pos.x + src.rad \ - && out.world.y > src.pos.y - src.rad && out.world.y < src.pos.y + src.rad \ - && out.world.z > src.pos.z - src.rad && out.world.z < src.pos.z + src.rad \ - {\n ", - ) - .write_str("let len = length(out.world - src.pos);\n ") - .write_str("if len < src.rad {\n ") - .write_str("let e = 1. - (len / src.rad);\n "); - - match array.kind() { - SourceKind::Glow => { - o.write_str("sources += e * e * src.col"); - } - SourceKind::Gloom if self.ambient => { - o.write_str("sources -= e * e * src.col * ambient.rgb"); - } - SourceKind::Gloom => { - o.write_str("sources -= e * e * src.col"); - } - } - - o.write_str(";\n ") - .write_str("}\n ") - .write_str("}\n ") - .write_str("}\n "); - } - } - - let has_spaces = self.calc_spaces(o); - let mut light = o.write_str("let light = ").separated(" + "); - if self.ambient { - light.out().write_str("ambient.rgb"); - } - - if self.has_sources() { - light.out().write_str("sources"); - } - - if has_spaces { - light.out().write_str("space"); - } - - o.write_str(";\n "); - true - } - - pub fn has_sources(&self) -> bool { - !self.source_arrays.is_empty() - } - - fn calc_spaces(&self, o: &mut Out) -> bool { - const NAMES: [&str; 4] = ["space_a", "space_b", "space_c", "space_d"]; - - if self.light_spaces.is_empty() { - return false; - } - - o.write_str("\n ") - .write_str("var space = vec3(0.);\n "); - - for ((n, kind), name) in self.light_spaces.enumerate().zip(NAMES) { - kind.calc(name, n, o); - } - - o.write_str("\n "); - true - } -} - -#[derive(Clone, Copy)] -pub struct Fragment { - pub vertex_color: bool, - pub vertex_texture: bool, -} diff --git a/dunge_shader/src/parts/view.rs b/dunge_shader/src/parts/view.rs deleted file mode 100644 index ae74a67..0000000 --- a/dunge_shader/src/parts/view.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::{nodes::*, out::Out}; - -/// The shader view kind. -#[derive(Clone, Copy)] -pub enum ViewKind { - None, - Camera, -} - -impl ViewKind { - pub(crate) fn define_type(self, o: &mut Out) { - if let Self::Camera = self { - o.write(Struct { - name: "Camera", - fields: vec![Field { - location: Location::None, - name: Name::Str("view"), - ty: Type::MAT4, - }], - }); - } - } - - pub(crate) fn declare_group(self, binding: &mut Binding, o: &mut Out) -> Option { - match self { - Self::None => None, - Self::Camera => { - let binding = binding.next(); - o.write(Var { - binding, - uniform: true, - name: Name::Str("camera"), - ty: Type::Simple("Camera"), - }); - - Some(binding.get()) - } - } - } - - pub(crate) fn calc_view(self, o: &mut Out) { - o.write_str(match self { - Self::None => "world", - Self::Camera => "camera.view * world", - }); - } -} diff --git a/dunge_shader/src/shader.rs b/dunge_shader/src/shader.rs deleted file mode 100644 index 20024c5..0000000 --- a/dunge_shader/src/shader.rs +++ /dev/null @@ -1,287 +0,0 @@ -use crate::{ - nodes::{Binding, Location}, - out::Out, - parts::*, - templater::Templater, -}; - -#[derive(Clone, Copy)] -pub struct Scheme { - pub vert: Vertex, - pub view: ViewKind, - pub ambient: bool, - pub static_color: Option, - pub textures: TexturesNumber, - pub source_arrays: SourceArrays, - pub light_spaces: LightSpaces, - pub instance_colors: bool, -} - -#[derive(Clone, Copy)] -pub struct Vertex { - pub dimension: Dimension, - pub fragment: Fragment, -} - -pub struct PostScheme { - pub post_data: u32, - pub map: TextureBindings, - pub antialiasing: bool, - pub vignette: Vignette, -} - -pub struct Shader { - pub layout: Layout, - pub source: String, -} - -impl Shader { - pub const VERTEX_ENTRY_POINT: &'static str = "vsmain"; - pub const FRAGMENT_ENTRY_POINT: &'static str = "fsmain"; - - #[allow(clippy::missing_panics_doc)] - pub fn generate(scheme: Scheme) -> Self { - let Scheme { - vert, - view, - ambient, - static_color, - textures, - source_arrays, - light_spaces, - instance_colors, - } = scheme; - - let vert_input = VertexInput { - fragment: vert.fragment, - pos: vert.dimension, - }; - - let vert_output = VertexOutput { - fragment: vert.fragment, - static_color, - ambient, - textures, - source_arrays, - light_spaces, - instance_colors, - }; - - let instance_color_input = InstanceColorInput::new(instance_colors); - let instance_color = { - let mut o = Out::new(); - instance_color_input.define_input(&mut o); - o - }; - - let types = { - let mut o = Out::new(); - let mut location = Location::new(); - InstanceInput::define_type(&mut location, &mut o); - instance_color_input.define_type(&mut location, &mut o); - vert_input.define_type(&mut location, &mut o); - - let mut location = Location::new(); - vert_output.define_type(&mut location, &mut o); - view.define_type(&mut o); - source_arrays.define_type(&mut o); - light_spaces.define_type(&mut o); - o - }; - - let (layout, groups) = Layout::common( - |binding, o| Globals { - post_data: None, - camera: view.declare_group(binding, o), - ambient: ambient.then(|| Ambient::declare_group(binding, o)), - }, - |binding, o| Textures { - map: textures.declare_group(binding, o), - }, - |binding, o| Lights { - source_arrays: source_arrays.declare_group(binding, o), - }, - |binding, o| Spaces { - light_spaces: light_spaces.declare_group(binding, o), - }, - ); - - let vertex_out = { - let mut o = Out::new(); - vert_output.calc_vertex(&vert_input, view, &mut o); - o - }; - - let fragment_col = { - let mut o = Out::new(); - vert_output.calc_fragment(&mut o); - o - }; - - Self { - layout, - source: Templater::default() - .insert("types", &types) - .insert("groups", &groups) - .insert("instance_color", &instance_color) - .insert("vertex_out", &vertex_out) - .insert("fragment_col", &fragment_col) - .format(include_str!("../template_common.wgsl")) - .expect("generate shader"), - } - } - - #[allow(clippy::missing_panics_doc)] - pub fn postproc(scheme: PostScheme) -> Self { - let PostScheme { - post_data, - map, - antialiasing, - vignette, - } = scheme; - - let fragment_col = { - let mut o = Out::new(); - let post = Post { - antialiasing, - vignette, - }; - - post.calc_fragment(&mut o); - o - }; - - Self { - layout: Layout::postproc(post_data, map), - source: Templater::default() - .insert("fragment_col", &fragment_col) - .format(include_str!("../template_post.wgsl")) - .expect("generate post shader"), - } - } -} - -pub struct Layout { - pub globals: Group, - pub textures: Group, - pub lights: Group, - pub spaces: Group, -} - -impl Layout { - fn common(globals: G, textures: T, lights: L, spaces: S) -> (Self, Out) - where - G: FnOnce(&mut Binding, &mut Out) -> Globals, - T: FnOnce(&mut Binding, &mut Out) -> Textures, - L: FnOnce(&mut Binding, &mut Out) -> Lights, - S: FnOnce(&mut Binding, &mut Out) -> Spaces, - { - let mut o = Out::new(); - let mut num = 0; - let globals = Group { - num, - bindings: globals(&mut Binding::with_group(num), &mut o), - }; - - if !globals.bindings.is_empty() { - num += 1; - } - - let textures = Group { - num, - bindings: textures(&mut Binding::with_group(num), &mut o), - }; - - if !textures.bindings.is_empty() { - num += 1; - } - - let lights = Group { - num, - bindings: lights(&mut Binding::with_group(num), &mut o), - }; - - if !lights.bindings.is_empty() { - num += 1; - } - - let spaces = Group { - num, - bindings: spaces(&mut Binding::with_group(num), &mut o), - }; - - ( - Self { - globals, - textures, - lights, - spaces, - }, - o, - ) - } - - fn postproc(post_data: u32, map: TextureBindings) -> Self { - Self { - globals: Group { - num: 0, - bindings: Globals { - post_data: Some(post_data), - ..Default::default() - }, - }, - textures: Group { - num: 1, - bindings: Textures { map }, - }, - lights: Group::default(), - spaces: Group::default(), - } - } -} - -#[derive(Default)] -pub struct Group { - pub num: u32, - pub bindings: T, -} - -#[derive(Default)] -pub struct Globals { - pub post_data: Option, - pub camera: Option, - pub ambient: Option, -} - -impl Globals { - fn is_empty(&self) -> bool { - self.post_data.is_none() && self.camera.is_none() && self.ambient.is_none() - } -} - -#[derive(Default)] -pub struct Textures { - pub map: TextureBindings, -} - -impl Textures { - pub fn is_empty(&self) -> bool { - self.map.tmaps.is_empty() - } -} - -#[derive(Default)] -pub struct Lights { - pub source_arrays: Vec, -} - -impl Lights { - fn is_empty(&self) -> bool { - self.source_arrays.is_empty() - } -} - -#[derive(Default)] -pub struct Spaces { - pub light_spaces: SpaceBindings, -} diff --git a/dunge_shader/src/templater.rs b/dunge_shader/src/templater.rs deleted file mode 100644 index 1d008ff..0000000 --- a/dunge_shader/src/templater.rs +++ /dev/null @@ -1,103 +0,0 @@ -use std::collections::HashMap; - -#[derive(Default)] -pub(crate) struct Templater<'a> { - subs: HashMap<&'a str, &'a str>, -} - -impl<'a> Templater<'a> { - pub fn insert(&mut self, key: &'a str, value: &'a str) -> &mut Self { - self.subs.insert(key, value); - self - } - - pub fn format(&self, template: &'a str) -> Result> { - let mut out = String::with_capacity(template.len()); - let mut state = State::Tail(template); - loop { - match state.next() { - Entry::Str(s) => out.push_str(s), - Entry::Key(key) => { - let value = self.subs.get(key).ok_or(Error::KeyNotFound(key))?; - out.push_str(value); - } - Entry::Fail => return Err(Error::Parse), - Entry::End => return Ok(out), - } - } - } -} - -#[derive(Debug, PartialEq, Eq)] -pub(crate) enum Error<'a> { - Parse, - KeyNotFound(&'a str), -} - -enum State<'a> { - Tail(&'a str), - Entry { key: &'a str, tail: &'a str }, - End, -} - -impl<'a> State<'a> { - fn next(&mut self) -> Entry<'a> { - match *self { - Self::Tail(tail) => { - let Some((head, tail)) = tail.split_once("[[") else { - *self = Self::End; - return Entry::Str(tail); - }; - - let Some((key, tail)) = tail.split_once("]]") else { - *self = Self::End; - return Entry::Fail; - }; - - *self = Self::Entry { key, tail }; - Entry::Str(head) - } - Self::Entry { key, tail } => { - *self = Self::Tail(tail); - Entry::Key(key) - } - Self::End => Entry::End, - } - } -} - -enum Entry<'a> { - Str(&'a str), - Key(&'a str), - Fail, - End, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn templater() { - let template = "[[a]] + [[b]] = [[c]]"; - let mut t = Templater::default(); - t.insert("a", "1"); - t.insert("b", "2"); - t.insert("c", "3"); - assert_eq!(t.format(template), Ok("1 + 2 = 3".to_owned())); - } - - #[test] - fn templater_parse_error() { - let template = "[[a"; - let t = Templater::default(); - assert_eq!(t.format(template), Err(Error::Parse)); - } - - #[test] - fn templater_key_error() { - let template = "[[a]]"; - let t = Templater::default(); - assert_eq!(t.format(template), Err(Error::KeyNotFound("a"))); - } -} diff --git a/dunge_shader/template_common.wgsl b/dunge_shader/template_common.wgsl deleted file mode 100644 index 5ea296e..0000000 --- a/dunge_shader/template_common.wgsl +++ /dev/null @@ -1,23 +0,0 @@ -[[types]] -[[groups]] - -@vertex -fn vsmain(inst: InstanceInput,[[instance_color]] input: VertexInput) -> VertexOutput { - let model = mat4x4( - inst.r0, - inst.r1, - inst.r2, - inst.r3, - ); - - var out: VertexOutput; - [[vertex_out]] - return out; -} - -@fragment -fn fsmain(out: VertexOutput) -> @location(0) vec4 { - var col: vec3; - [[fragment_col]] - return vec4(col, 1.); -} diff --git a/dunge_shader/template_post.wgsl b/dunge_shader/template_post.wgsl deleted file mode 100644 index ee5df76..0000000 --- a/dunge_shader/template_post.wgsl +++ /dev/null @@ -1,56 +0,0 @@ -struct Data { - size: vec2, - step: vec2, - factor: vec2, - pad: vec2, -} - -@group(0) @binding(0) -var data: Data; - -struct VertexOutput { - @builtin(position) pos: vec4, - @location(0) uni: vec2, - @location(1) map: vec2, -} - -@vertex -fn vsmain(@builtin(vertex_index) in_vertex_index: u32) -> VertexOutput { - var out: VertexOutput; - switch in_vertex_index { - case 0u { - out.pos = vec4(1., -1., 0., 1.); - out.uni = vec2(1., 1.); - out.map = data.factor.xy; - } - case 1u { - out.pos = vec4(1., 1., 0., 1.); - out.uni = vec2(1., 0.); - out.map = vec2(data.factor.x, 0.); - } - case 2u { - out.pos = vec4(-1., -1., 0., 1.); - out.uni = vec2(0., 1.); - out.map = vec2(0., data.factor.y); - } - case 3u { - out.pos = vec4(-1., 1., 0., 1.); - out.uni = vec2(0., 0.); - out.map = vec2(0., 0.); - } - default {} - } - return out; -} - -@group(1) @binding(0) -var tmap: texture_2d; -@group(1) @binding(1) -var smap: sampler; - -@fragment -fn fsmain(in: VertexOutput) -> @location(0) vec4 { - var col: vec4; - [[fragment_col]] - return col; -}