From 3b626b16426ae25e873de15922e322ff36a7b846 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Sun, 9 Jun 2024 17:36:00 -0700 Subject: [PATCH 01/24] Use the layout from the old branch --- crates/components/src/map_view.rs | 2 +- crates/components/src/tilepicker.rs | 21 +- crates/graphics/src/data/mod.rs | 32 ++ crates/graphics/src/{ => data}/quad.rs | 0 crates/graphics/src/{ => data}/vertex.rs | 0 crates/graphics/src/{ => data}/viewport.rs | 26 +- crates/graphics/src/event.rs | 2 +- crates/graphics/src/lib.rs | 70 +--- .../src/{atlas_loader.rs => loaders/atlas.rs} | 2 +- crates/graphics/src/loaders/mod.rs | 26 ++ .../{texture_loader.rs => loaders/texture.rs} | 0 crates/graphics/src/map.rs | 4 +- crates/graphics/src/plane.rs | 2 +- .../{ => primitives}/collision/collision.wgsl | 0 .../{ => primitives}/collision/instance.rs | 0 .../src/{ => primitives}/collision/mod.rs | 7 +- .../src/{ => primitives}/collision/shader.rs | 26 +- .../src/{ => primitives}/collision/vertex.rs | 0 .../src/{ => primitives}/grid/display.rs | 0 .../src/{ => primitives}/grid/grid.wgsl | 0 .../src/{ => primitives}/grid/instance.rs | 0 .../graphics/src/{ => primitives}/grid/mod.rs | 7 +- .../src/{ => primitives}/grid/shader.rs | 26 +- .../src/{ => primitives}/grid/vertex.rs | 0 crates/graphics/src/primitives/mod.rs | 89 +++++ .../src/{ => primitives}/sprite/graphic.rs | 0 .../src/{ => primitives}/sprite/mod.rs | 8 +- .../src/{ => primitives}/sprite/shader.rs | 107 +++-- .../src/{ => primitives}/sprite/sprite.wgsl | 0 .../src/{ => primitives}/sprite/vertices.rs | 2 +- .../src/{ => primitives}/tiles/atlas.rs | 2 +- .../src/primitives/tiles/autotile_ids.rs | 70 ++++ .../src/{ => primitives}/tiles/autotiles.rs | 0 .../src/{ => primitives}/tiles/instance.rs | 2 +- .../src/{ => primitives}/tiles/mod.rs | 7 +- .../src/{ => primitives}/tiles/opacity.rs | 0 .../src/{ => primitives}/tiles/shader.rs | 26 +- .../src/{ => primitives}/tiles/tilemap.wgsl | 0 crates/graphics/src/tiles/autotile_ids.rs | 369 ------------------ crates/ui/src/tabs/map/mod.rs | 4 +- 40 files changed, 348 insertions(+), 591 deletions(-) create mode 100644 crates/graphics/src/data/mod.rs rename crates/graphics/src/{ => data}/quad.rs (100%) rename crates/graphics/src/{ => data}/vertex.rs (100%) rename crates/graphics/src/{ => data}/viewport.rs (86%) rename crates/graphics/src/{atlas_loader.rs => loaders/atlas.rs} (97%) create mode 100644 crates/graphics/src/loaders/mod.rs rename crates/graphics/src/{texture_loader.rs => loaders/texture.rs} (100%) rename crates/graphics/src/{ => primitives}/collision/collision.wgsl (100%) rename crates/graphics/src/{ => primitives}/collision/instance.rs (100%) rename crates/graphics/src/{ => primitives}/collision/mod.rs (97%) rename crates/graphics/src/{ => primitives}/collision/shader.rs (85%) rename crates/graphics/src/{ => primitives}/collision/vertex.rs (100%) rename crates/graphics/src/{ => primitives}/grid/display.rs (100%) rename crates/graphics/src/{ => primitives}/grid/grid.wgsl (100%) rename crates/graphics/src/{ => primitives}/grid/instance.rs (100%) rename crates/graphics/src/{ => primitives}/grid/mod.rs (95%) rename crates/graphics/src/{ => primitives}/grid/shader.rs (86%) rename crates/graphics/src/{ => primitives}/grid/vertex.rs (100%) create mode 100644 crates/graphics/src/primitives/mod.rs rename crates/graphics/src/{ => primitives}/sprite/graphic.rs (100%) rename crates/graphics/src/{ => primitives}/sprite/mod.rs (95%) rename crates/graphics/src/{ => primitives}/sprite/shader.rs (59%) rename crates/graphics/src/{ => primitives}/sprite/sprite.wgsl (100%) rename crates/graphics/src/{ => primitives}/sprite/vertices.rs (98%) rename crates/graphics/src/{ => primitives}/tiles/atlas.rs (99%) create mode 100644 crates/graphics/src/primitives/tiles/autotile_ids.rs rename crates/graphics/src/{ => primitives}/tiles/autotiles.rs (100%) rename crates/graphics/src/{ => primitives}/tiles/instance.rs (99%) rename crates/graphics/src/{ => primitives}/tiles/mod.rs (97%) rename crates/graphics/src/{ => primitives}/tiles/opacity.rs (100%) rename crates/graphics/src/{ => primitives}/tiles/shader.rs (90%) rename crates/graphics/src/{ => primitives}/tiles/tilemap.wgsl (100%) delete mode 100644 crates/graphics/src/tiles/autotile_ids.rs diff --git a/crates/components/src/map_view.rs b/crates/components/src/map_view.rs index f9754376..45b557ff 100644 --- a/crates/components/src/map_view.rs +++ b/crates/components/src/map_view.rs @@ -79,7 +79,7 @@ impl MapView { let tileset = &tilesets.data[map.tileset_id]; let mut passages = luminol_data::Table2::new(map.data.xsize(), map.data.ysize()); - luminol_graphics::collision::calculate_passages( + luminol_graphics::primitives::collision::calculate_passages( &tileset.passages, &tileset.priorities, &map.data, diff --git a/crates/components/src/tilepicker.rs b/crates/components/src/tilepicker.rs index 4e63f9a6..675e9dba 100644 --- a/crates/components/src/tilepicker.rs +++ b/crates/components/src/tilepicker.rs @@ -32,7 +32,7 @@ pub struct Tilepicker { drag_origin: Option, resources: Arc, - viewport: Arc, + viewport: Arc, ani_time: Option, /// When true, brush tile ID randomization is enabled. @@ -42,9 +42,9 @@ pub struct Tilepicker { } struct Resources { - tiles: luminol_graphics::tiles::Tiles, - collision: luminol_graphics::collision::Collision, - grid: luminol_graphics::grid::Grid, + tiles: luminol_graphics::Tiles, + collision: luminol_graphics::Collision, + grid: luminol_graphics::Grid, } // wgpu types are not Send + Sync on webassembly, so we use fragile to make sure we never access any wgpu resources across thread boundaries @@ -137,20 +137,20 @@ impl Tilepicker { tilepicker_data, ); - let viewport = Arc::new(luminol_graphics::viewport::Viewport::new( + let viewport = Arc::new(luminol_graphics::Viewport::new( &update_state.graphics, 256., atlas.tileset_height as f32 + 32., )); - let tiles = luminol_graphics::tiles::Tiles::new( + let tiles = luminol_graphics::Tiles::new( &update_state.graphics, viewport.clone(), atlas, &tilepicker_data, ); - let grid = luminol_graphics::grid::Grid::new( + let grid = luminol_graphics::Grid::new( &update_state.graphics, viewport.clone(), tilepicker_data.xsize(), @@ -173,11 +173,8 @@ impl Tilepicker { (passages.len().saturating_sub(8)).min(tileset.passages.len().saturating_sub(384)); passages.as_mut_slice()[8..8 + length] .copy_from_slice(&tileset.passages.as_slice()[384..384 + length]); - let collision = luminol_graphics::collision::Collision::new( - &update_state.graphics, - viewport.clone(), - &passages, - ); + let collision = + luminol_graphics::Collision::new(&update_state.graphics, viewport.clone(), &passages); let mut brush_seed = [0u8; 16]; brush_seed[0..8].copy_from_slice( diff --git a/crates/graphics/src/data/mod.rs b/crates/graphics/src/data/mod.rs new file mode 100644 index 00000000..4e8c829e --- /dev/null +++ b/crates/graphics/src/data/mod.rs @@ -0,0 +1,32 @@ +// Copyright (C) 2024 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . +// +// Additional permission under GNU GPL version 3 section 7 +// +// If you modify this Program, or any covered work, by linking or combining +// it with Steamworks API by Valve Corporation, containing parts covered by +// terms of the Steamworks API by Valve Corporation, the licensors of this +// Program grant you additional permission to convey the resulting work. + +mod quad; +pub use quad::Quad; + +mod vertex; +pub use vertex::Vertex; + +mod viewport; +pub use viewport::Viewport; diff --git a/crates/graphics/src/quad.rs b/crates/graphics/src/data/quad.rs similarity index 100% rename from crates/graphics/src/quad.rs rename to crates/graphics/src/data/quad.rs diff --git a/crates/graphics/src/vertex.rs b/crates/graphics/src/data/vertex.rs similarity index 100% rename from crates/graphics/src/vertex.rs rename to crates/graphics/src/data/vertex.rs diff --git a/crates/graphics/src/viewport.rs b/crates/graphics/src/data/viewport.rs similarity index 86% rename from crates/graphics/src/viewport.rs rename to crates/graphics/src/data/viewport.rs index 93744412..79c0c2f9 100644 --- a/crates/graphics/src/viewport.rs +++ b/crates/graphics/src/data/viewport.rs @@ -86,18 +86,18 @@ impl Viewport { .write_buffer(uniform, 0, bytemuck::cast_slice(&[self.data.load()])); } } -} -pub fn add_to_bind_group_layout( - layout_builder: &mut BindGroupLayoutBuilder, -) -> &mut BindGroupLayoutBuilder { - layout_builder.append( - wgpu::ShaderStages::VERTEX_FRAGMENT, - wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - None, - ) + pub fn add_to_bind_group_layout( + layout_builder: &mut BindGroupLayoutBuilder, + ) -> &mut BindGroupLayoutBuilder { + layout_builder.append( + wgpu::ShaderStages::VERTEX_FRAGMENT, + wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + None, + ) + } } diff --git a/crates/graphics/src/event.rs b/crates/graphics/src/event.rs index 6270fe0b..da743432 100644 --- a/crates/graphics/src/event.rs +++ b/crates/graphics/src/event.rs @@ -24,7 +24,7 @@ use std::sync::Arc; use fragile::Fragile; -use crate::{quad::Quad, sprite::Sprite, tiles::Atlas, viewport::Viewport, GraphicsState}; +use crate::{Atlas, GraphicsState, Quad, Sprite, Viewport}; pub struct Event { sprite: Arc, diff --git a/crates/graphics/src/lib.rs b/crates/graphics/src/lib.rs index 67fd6559..d0a58b83 100644 --- a/crates/graphics/src/lib.rs +++ b/crates/graphics/src/lib.rs @@ -19,77 +19,47 @@ pub mod binding_helpers; pub use binding_helpers::{BindGroupBuilder, BindGroupLayoutBuilder}; -pub mod collision; -pub mod grid; -pub mod quad; -pub mod sprite; -pub mod tiles; -pub mod vertex; -pub mod viewport; +pub mod loaders; +pub use loaders::texture::Texture; + +// Building blocks that make up more complex parts (i.e. the map view, or events) +pub mod primitives; +pub use primitives::{ + collision::Collision, grid::Grid, sprite::Sprite, tiles::Atlas, tiles::Tiles, +}; + +pub mod data; +pub use data::*; pub mod event; pub mod map; pub mod plane; -pub mod atlas_loader; - -pub mod texture_loader; - pub use event::Event; pub use map::Map; pub use plane::Plane; -pub use texture_loader::Texture; - pub struct GraphicsState { - pub texture_loader: texture_loader::Loader, - pub atlas_loader: atlas_loader::Loader, + pub texture_loader: loaders::texture::Loader, + pub atlas_loader: loaders::atlas::Loader, pub render_state: luminol_egui_wgpu::RenderState, pub nearest_sampler: wgpu::Sampler, - pipelines: Pipelines, - bind_group_layouts: BindGroupLayouts, + pipelines: primitives::Pipelines, + bind_group_layouts: primitives::BindGroupLayouts, texture_error_tx: crossbeam::channel::Sender, texture_error_rx: crossbeam::channel::Receiver, } -pub struct BindGroupLayouts { - sprite: wgpu::BindGroupLayout, - tiles: wgpu::BindGroupLayout, - collision: wgpu::BindGroupLayout, - grid: wgpu::BindGroupLayout, -} - -pub struct Pipelines { - sprites: std::collections::HashMap, - tiles: wgpu::RenderPipeline, - collision: wgpu::RenderPipeline, - grid: wgpu::RenderPipeline, -} - impl GraphicsState { pub fn new(render_state: luminol_egui_wgpu::RenderState) -> Self { - let bind_group_layouts = BindGroupLayouts { - sprite: sprite::create_bind_group_layout(&render_state), - tiles: tiles::create_bind_group_layout(&render_state), - collision: collision::create_bind_group_layout(&render_state), - grid: grid::create_bind_group_layout(&render_state), - }; - - let pipelines = Pipelines { - sprites: sprite::shader::create_sprite_shaders(&render_state, &bind_group_layouts), - tiles: tiles::shader::create_render_pipeline(&render_state, &bind_group_layouts), - collision: collision::shader::create_render_pipeline( - &render_state, - &bind_group_layouts, - ), - grid: grid::shader::create_render_pipeline(&render_state, &bind_group_layouts), - }; - - let texture_loader = texture_loader::Loader::new(render_state.clone()); - let atlas_cache = atlas_loader::Loader::default(); + let bind_group_layouts = primitives::BindGroupLayouts::new(&render_state); + let pipelines = primitives::Pipelines::new(&render_state, &bind_group_layouts); + + let texture_loader = loaders::texture::Loader::new(render_state.clone()); + let atlas_cache = loaders::atlas::Loader::default(); let nearest_sampler = render_state .device diff --git a/crates/graphics/src/atlas_loader.rs b/crates/graphics/src/loaders/atlas.rs similarity index 97% rename from crates/graphics/src/atlas_loader.rs rename to crates/graphics/src/loaders/atlas.rs index da22da04..7507b1b6 100644 --- a/crates/graphics/src/atlas_loader.rs +++ b/crates/graphics/src/loaders/atlas.rs @@ -14,7 +14,7 @@ // // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use crate::{tiles::Atlas, GraphicsState}; +use crate::{Atlas, GraphicsState}; #[derive(Default)] pub struct Loader { diff --git a/crates/graphics/src/loaders/mod.rs b/crates/graphics/src/loaders/mod.rs new file mode 100644 index 00000000..f959b64b --- /dev/null +++ b/crates/graphics/src/loaders/mod.rs @@ -0,0 +1,26 @@ +// Copyright (C) 2024 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . +// +// Additional permission under GNU GPL version 3 section 7 +// +// If you modify this Program, or any covered work, by linking or combining +// it with Steamworks API by Valve Corporation, containing parts covered by +// terms of the Steamworks API by Valve Corporation, the licensors of this +// Program grant you additional permission to convey the resulting work. + +pub mod atlas; +pub mod texture; diff --git a/crates/graphics/src/texture_loader.rs b/crates/graphics/src/loaders/texture.rs similarity index 100% rename from crates/graphics/src/texture_loader.rs rename to crates/graphics/src/loaders/texture.rs diff --git a/crates/graphics/src/map.rs b/crates/graphics/src/map.rs index bda21800..4bb2a0c9 100644 --- a/crates/graphics/src/map.rs +++ b/crates/graphics/src/map.rs @@ -26,9 +26,7 @@ use std::time::Duration; use fragile::Fragile; -use crate::{ - collision::Collision, grid::Grid, tiles::Tiles, viewport::Viewport, GraphicsState, Plane, -}; +use crate::{Collision, GraphicsState, Grid, Plane, Tiles, Viewport}; pub struct Map { resources: Arc, diff --git a/crates/graphics/src/plane.rs b/crates/graphics/src/plane.rs index bab294b2..04d019df 100644 --- a/crates/graphics/src/plane.rs +++ b/crates/graphics/src/plane.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use crate::{quad::Quad, sprite::Sprite, viewport::Viewport, GraphicsState, Texture}; +use crate::{GraphicsState, Quad, Sprite, Texture, Viewport}; use std::sync::Arc; pub struct Plane { diff --git a/crates/graphics/src/collision/collision.wgsl b/crates/graphics/src/primitives/collision/collision.wgsl similarity index 100% rename from crates/graphics/src/collision/collision.wgsl rename to crates/graphics/src/primitives/collision/collision.wgsl diff --git a/crates/graphics/src/collision/instance.rs b/crates/graphics/src/primitives/collision/instance.rs similarity index 100% rename from crates/graphics/src/collision/instance.rs rename to crates/graphics/src/primitives/collision/instance.rs diff --git a/crates/graphics/src/collision/mod.rs b/crates/graphics/src/primitives/collision/mod.rs similarity index 97% rename from crates/graphics/src/collision/mod.rs rename to crates/graphics/src/primitives/collision/mod.rs index 7134b24d..cf742b1b 100644 --- a/crates/graphics/src/collision/mod.rs +++ b/crates/graphics/src/primitives/collision/mod.rs @@ -17,10 +17,7 @@ use std::sync::Arc; -use crate::{ - viewport::{self, Viewport}, - BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, -}; +use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Viewport}; use instance::Instances; use itertools::Itertools; @@ -207,7 +204,7 @@ pub fn create_bind_group_layout( let mut builder = BindGroupLayoutBuilder::new(); if !crate::push_constants_supported(render_state) { - viewport::add_to_bind_group_layout(&mut builder); + Viewport::add_to_bind_group_layout(&mut builder); } builder.build(&render_state.device, Some("collision bind group layout")) diff --git a/crates/graphics/src/collision/shader.rs b/crates/graphics/src/primitives/collision/shader.rs similarity index 85% rename from crates/graphics/src/collision/shader.rs rename to crates/graphics/src/primitives/collision/shader.rs index d367292c..225db131 100644 --- a/crates/graphics/src/collision/shader.rs +++ b/crates/graphics/src/primitives/collision/shader.rs @@ -19,18 +19,13 @@ use super::instance::Instances; use super::Vertex; pub fn create_render_pipeline( + composer: &mut naga_oil::compose::Composer, render_state: &luminol_egui_wgpu::RenderState, - bind_group_layouts: &crate::BindGroupLayouts, -) -> wgpu::RenderPipeline { + bind_group_layouts: &crate::primitives::BindGroupLayouts, +) -> Result { let push_constants_supported = crate::push_constants_supported(render_state); - let mut composer = naga_oil::compose::Composer::default().with_capabilities( - push_constants_supported - .then_some(naga::valid::Capabilities::PUSH_CONSTANT) - .unwrap_or_default(), - ); - - let result = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { + let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { source: include_str!("collision.wgsl"), file_path: "collision.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, @@ -39,14 +34,7 @@ pub fn create_render_pipeline( naga_oil::compose::ShaderDefValue::Bool(push_constants_supported), )]), additional_imports: &[], - }); - let module = match result { - Ok(module) => module, - Err(e) => { - let error = e.emit_to_string(&composer); - panic!("{error}"); - } - }; + })?; let shader_module = render_state .device @@ -89,7 +77,7 @@ pub fn create_render_pipeline( push_constant_ranges, }); - render_state + Ok(render_state .device .create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Tilemap Collision Render Pipeline"), @@ -111,5 +99,5 @@ pub fn create_render_pipeline( depth_stencil: None, multisample: wgpu::MultisampleState::default(), multiview: None, - }) + })) } diff --git a/crates/graphics/src/collision/vertex.rs b/crates/graphics/src/primitives/collision/vertex.rs similarity index 100% rename from crates/graphics/src/collision/vertex.rs rename to crates/graphics/src/primitives/collision/vertex.rs diff --git a/crates/graphics/src/grid/display.rs b/crates/graphics/src/primitives/grid/display.rs similarity index 100% rename from crates/graphics/src/grid/display.rs rename to crates/graphics/src/primitives/grid/display.rs diff --git a/crates/graphics/src/grid/grid.wgsl b/crates/graphics/src/primitives/grid/grid.wgsl similarity index 100% rename from crates/graphics/src/grid/grid.wgsl rename to crates/graphics/src/primitives/grid/grid.wgsl diff --git a/crates/graphics/src/grid/instance.rs b/crates/graphics/src/primitives/grid/instance.rs similarity index 100% rename from crates/graphics/src/grid/instance.rs rename to crates/graphics/src/primitives/grid/instance.rs diff --git a/crates/graphics/src/grid/mod.rs b/crates/graphics/src/primitives/grid/mod.rs similarity index 95% rename from crates/graphics/src/grid/mod.rs rename to crates/graphics/src/primitives/grid/mod.rs index a14e3866..87610d63 100644 --- a/crates/graphics/src/grid/mod.rs +++ b/crates/graphics/src/primitives/grid/mod.rs @@ -17,10 +17,7 @@ use std::sync::Arc; -use crate::{ - viewport::{self, Viewport}, - BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, -}; +use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Viewport}; use display::Display; use instance::Instances; @@ -114,7 +111,7 @@ pub fn create_bind_group_layout( let mut builder = BindGroupLayoutBuilder::new(); if !crate::push_constants_supported(render_state) { - viewport::add_to_bind_group_layout(&mut builder); + Viewport::add_to_bind_group_layout(&mut builder); display::add_to_bind_group_layout(&mut builder); } diff --git a/crates/graphics/src/grid/shader.rs b/crates/graphics/src/primitives/grid/shader.rs similarity index 86% rename from crates/graphics/src/grid/shader.rs rename to crates/graphics/src/primitives/grid/shader.rs index a377713f..801dadff 100644 --- a/crates/graphics/src/grid/shader.rs +++ b/crates/graphics/src/primitives/grid/shader.rs @@ -20,18 +20,13 @@ use super::instance::Instances; use super::Vertex; pub fn create_render_pipeline( + composer: &mut naga_oil::compose::Composer, render_state: &luminol_egui_wgpu::RenderState, - bind_group_layouts: &crate::BindGroupLayouts, -) -> wgpu::RenderPipeline { + bind_group_layouts: &crate::primitives::BindGroupLayouts, +) -> Result { let push_constants_supported = crate::push_constants_supported(render_state); - let mut composer = naga_oil::compose::Composer::default().with_capabilities( - push_constants_supported - .then_some(naga::valid::Capabilities::PUSH_CONSTANT) - .unwrap_or_default(), - ); - - let result = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { + let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { source: include_str!("grid.wgsl"), file_path: "grid.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, @@ -40,14 +35,7 @@ pub fn create_render_pipeline( naga_oil::compose::ShaderDefValue::Bool(push_constants_supported), )]), additional_imports: &[], - }); - let module = match result { - Ok(module) => module, - Err(e) => { - let error = e.emit_to_string(&composer); - panic!("{error}"); - } - }; + })?; let shader_module = render_state .device @@ -95,7 +83,7 @@ pub fn create_render_pipeline( push_constant_ranges, }); - render_state + Ok(render_state .device .create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Tilemap Grid Render Pipeline"), @@ -117,5 +105,5 @@ pub fn create_render_pipeline( depth_stencil: None, multisample: wgpu::MultisampleState::default(), multiview: None, - }) + })) } diff --git a/crates/graphics/src/grid/vertex.rs b/crates/graphics/src/primitives/grid/vertex.rs similarity index 100% rename from crates/graphics/src/grid/vertex.rs rename to crates/graphics/src/primitives/grid/vertex.rs diff --git a/crates/graphics/src/primitives/mod.rs b/crates/graphics/src/primitives/mod.rs new file mode 100644 index 00000000..dbcd28db --- /dev/null +++ b/crates/graphics/src/primitives/mod.rs @@ -0,0 +1,89 @@ +// Copyright (C) 2024 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . +// +// Additional permission under GNU GPL version 3 section 7 +// +// If you modify this Program, or any covered work, by linking or combining +// it with Steamworks API by Valve Corporation, containing parts covered by +// terms of the Steamworks API by Valve Corporation, the licensors of this +// Program grant you additional permission to convey the resulting work. + +pub mod collision; +pub mod grid; +pub mod sprite; +pub mod tiles; + +pub struct BindGroupLayouts { + sprite: wgpu::BindGroupLayout, + tiles: wgpu::BindGroupLayout, + collision: wgpu::BindGroupLayout, + grid: wgpu::BindGroupLayout, +} + +pub struct Pipelines { + sprites: std::collections::HashMap, + tiles: wgpu::RenderPipeline, + collision: wgpu::RenderPipeline, + grid: wgpu::RenderPipeline, +} + +impl BindGroupLayouts { + pub fn new(render_state: &luminol_egui_wgpu::RenderState) -> Self { + Self { + sprite: sprite::create_bind_group_layout(render_state), + tiles: tiles::create_bind_group_layout(render_state), + collision: collision::create_bind_group_layout(render_state), + grid: grid::create_bind_group_layout(render_state), + } + } +} + +macro_rules! create_pipelines { +( + $render_state:ident, $bind_group_layouts:ident, + $($name:ident: $fun:path),* +) => {{ + let mut composer = naga_oil::compose::Composer::default(); + $( + let $name = match $fun(&mut composer, $render_state, $bind_group_layouts) { + Ok(p) => p, + Err(err) => { + let err = err.emit_to_string(&composer); + panic!("Error creating {} render pipeline:\n{err}", stringify!($name)) + } + }; + )* + Pipelines { + $($name,)* + } +}}; +} + +impl Pipelines { + pub fn new( + render_state: &luminol_egui_wgpu::RenderState, + bind_group_layouts: &BindGroupLayouts, + ) -> Self { + create_pipelines! { + render_state, bind_group_layouts, + sprites: sprite::shader::create_sprite_shaders, + tiles: tiles::shader::create_render_pipeline, + collision: collision::shader::create_render_pipeline, + grid: grid::shader::create_render_pipeline + } + } +} diff --git a/crates/graphics/src/sprite/graphic.rs b/crates/graphics/src/primitives/sprite/graphic.rs similarity index 100% rename from crates/graphics/src/sprite/graphic.rs rename to crates/graphics/src/primitives/sprite/graphic.rs diff --git a/crates/graphics/src/sprite/mod.rs b/crates/graphics/src/primitives/sprite/mod.rs similarity index 95% rename from crates/graphics/src/sprite/mod.rs rename to crates/graphics/src/primitives/sprite/mod.rs index ec9097bf..01f669b7 100644 --- a/crates/graphics/src/sprite/mod.rs +++ b/crates/graphics/src/primitives/sprite/mod.rs @@ -16,11 +16,7 @@ // along with Luminol. If not, see . use std::sync::Arc; -use crate::{ - quad::Quad, - viewport::{self, Viewport}, - BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Texture, -}; +use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Quad, Texture, Viewport}; pub(crate) mod graphic; pub(crate) mod shader; @@ -133,7 +129,7 @@ pub fn create_bind_group_layout( ); if !crate::push_constants_supported(render_state) { - viewport::add_to_bind_group_layout(&mut builder); + Viewport::add_to_bind_group_layout(&mut builder); graphic::add_to_bind_group_layout(&mut builder); } diff --git a/crates/graphics/src/sprite/shader.rs b/crates/graphics/src/primitives/sprite/shader.rs similarity index 59% rename from crates/graphics/src/sprite/shader.rs rename to crates/graphics/src/primitives/sprite/shader.rs index fd705c7f..69c82e7b 100644 --- a/crates/graphics/src/sprite/shader.rs +++ b/crates/graphics/src/primitives/sprite/shader.rs @@ -15,33 +15,30 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use crate::{vertex::Vertex, BindGroupLayouts}; +use std::collections::HashMap; + +use naga_oil::compose::ComposerError; + +use crate::{primitives::BindGroupLayouts, Vertex}; fn create_shader( + composer: &mut naga_oil::compose::Composer, render_state: &luminol_egui_wgpu::RenderState, bind_group_layouts: &BindGroupLayouts, target: wgpu::BlendState, -) -> wgpu::RenderPipeline { +) -> Result { let push_constants_supported = crate::push_constants_supported(render_state); - let mut composer = naga_oil::compose::Composer::default().with_capabilities( - push_constants_supported - .then_some(naga::valid::Capabilities::PUSH_CONSTANT) - .unwrap_or_default(), - ); - - let module = composer - .make_naga_module(naga_oil::compose::NagaModuleDescriptor { - source: include_str!("sprite.wgsl"), - file_path: "sprite.wgsl", - shader_type: naga_oil::compose::ShaderType::Wgsl, - shader_defs: std::collections::HashMap::from([( - "USE_PUSH_CONSTANTS".to_string(), - naga_oil::compose::ShaderDefValue::Bool(push_constants_supported), - )]), - additional_imports: &[], - }) - .expect("failed to create sprite shader module"); + let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { + source: include_str!("sprite.wgsl"), + file_path: "sprite.wgsl", + shader_type: naga_oil::compose::ShaderType::Wgsl, + shader_defs: HashMap::from([( + "USE_PUSH_CONSTANTS".to_string(), + naga_oil::compose::ShaderDefValue::Bool(push_constants_supported), + )]), + additional_imports: &[], + })?; let shader_module = render_state .device @@ -80,7 +77,7 @@ fn create_shader( push_constant_ranges, }); - render_state + Ok(render_state .device .create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Tilemap Sprite Render Pipeline"), @@ -105,55 +102,51 @@ fn create_shader( depth_stencil: None, multisample: wgpu::MultisampleState::default(), multiview: None, - }) + })) } +const BLEND_ADD: wgpu::BlendState = wgpu::BlendState { + color: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::One, + operation: wgpu::BlendOperation::Add, + }, + alpha: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::One, + operation: wgpu::BlendOperation::Add, + }, +}; +const BLEND_SUBTRACT: wgpu::BlendState = wgpu::BlendState { + color: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::One, + operation: wgpu::BlendOperation::ReverseSubtract, + }, + alpha: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::Zero, + dst_factor: wgpu::BlendFactor::One, + operation: wgpu::BlendOperation::ReverseSubtract, + }, +}; + pub fn create_sprite_shaders( + composer: &mut naga_oil::compose::Composer, render_state: &luminol_egui_wgpu::RenderState, bind_group_layouts: &BindGroupLayouts, -) -> std::collections::HashMap { +) -> Result, ComposerError> { [ ( luminol_data::BlendMode::Normal, wgpu::BlendState::ALPHA_BLENDING, ), - ( - luminol_data::BlendMode::Add, - wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::One, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::One, - operation: wgpu::BlendOperation::Add, - }, - }, - ), - ( - luminol_data::BlendMode::Subtract, - wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::One, - operation: wgpu::BlendOperation::ReverseSubtract, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::Zero, - dst_factor: wgpu::BlendFactor::One, - operation: wgpu::BlendOperation::ReverseSubtract, - }, - }, - ), + (luminol_data::BlendMode::Add, BLEND_ADD), + (luminol_data::BlendMode::Subtract, BLEND_SUBTRACT), ] .into_iter() .map(|(mode, target)| { - ( - mode, - create_shader(render_state, bind_group_layouts, target), - ) + let shader = create_shader(composer, render_state, bind_group_layouts, target)?; + Ok((mode, shader)) }) .collect() } diff --git a/crates/graphics/src/sprite/sprite.wgsl b/crates/graphics/src/primitives/sprite/sprite.wgsl similarity index 100% rename from crates/graphics/src/sprite/sprite.wgsl rename to crates/graphics/src/primitives/sprite/sprite.wgsl diff --git a/crates/graphics/src/sprite/vertices.rs b/crates/graphics/src/primitives/sprite/vertices.rs similarity index 98% rename from crates/graphics/src/sprite/vertices.rs rename to crates/graphics/src/primitives/sprite/vertices.rs index 85f39592..46e1c685 100644 --- a/crates/graphics/src/sprite/vertices.rs +++ b/crates/graphics/src/primitives/sprite/vertices.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use crate::quad::Quad; +use crate::Quad; #[derive(Debug)] pub struct Vertices { diff --git a/crates/graphics/src/tiles/atlas.rs b/crates/graphics/src/primitives/tiles/atlas.rs similarity index 99% rename from crates/graphics/src/tiles/atlas.rs rename to crates/graphics/src/primitives/tiles/atlas.rs index 79f83bfd..06544149 100644 --- a/crates/graphics/src/tiles/atlas.rs +++ b/crates/graphics/src/primitives/tiles/atlas.rs @@ -21,7 +21,7 @@ use itertools::Itertools; use wgpu::util::DeviceExt; use super::autotile_ids::AUTOTILES; -use crate::{quad::Quad, GraphicsState, Texture}; +use crate::{GraphicsState, Quad, Texture}; pub const MAX_SIZE: u32 = 8192; // Max texture size in one dimension pub const TILE_SIZE: u32 = 32; // Tiles are 32x32 diff --git a/crates/graphics/src/primitives/tiles/autotile_ids.rs b/crates/graphics/src/primitives/tiles/autotile_ids.rs new file mode 100644 index 00000000..19833293 --- /dev/null +++ b/crates/graphics/src/primitives/tiles/autotile_ids.rs @@ -0,0 +1,70 @@ +// Copyright (C) 2023 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . + +/// Hardcoded list of tiles from r48 and old python Luminol. +/// There seems to be very little pattern in autotile IDs so this is sadly +/// the best we can do. +pub const AUTOTILES: [[u32; 4]; 48] = [ + [26, 27, 32, 33], + [4, 27, 32, 33], + [26, 5, 32, 33], + [4, 5, 32, 33], + [26, 27, 32, 11], + [4, 27, 32, 11], + [26, 5, 32, 11], + [4, 5, 32, 11], + [26, 27, 10, 33], + [4, 27, 10, 33], + [26, 5, 10, 33], + [4, 5, 10, 33], + [26, 27, 10, 11], + [4, 27, 10, 11], + [26, 5, 10, 11], + [4, 5, 10, 11], + [24, 25, 30, 31], + [24, 5, 30, 31], + [24, 25, 30, 11], + [24, 5, 30, 11], + [14, 15, 20, 21], + [14, 15, 20, 11], + [14, 15, 10, 21], + [14, 15, 10, 11], + [28, 29, 34, 35], + [28, 29, 10, 35], + [4, 29, 34, 35], + [4, 29, 10, 35], + [38, 39, 44, 45], + [4, 39, 44, 45], + [38, 5, 44, 45], + [4, 5, 44, 45], + [24, 29, 30, 35], + [14, 15, 44, 45], + [12, 13, 18, 19], + [12, 13, 18, 11], + [16, 17, 22, 23], + [16, 17, 10, 23], + [40, 41, 46, 47], + [4, 41, 46, 47], + [36, 37, 42, 43], + [36, 5, 42, 43], + [12, 17, 18, 23], + [12, 13, 42, 43], + [36, 41, 42, 47], + [16, 17, 46, 47], + [12, 17, 42, 47], + [0, 1, 6, 7], +]; diff --git a/crates/graphics/src/tiles/autotiles.rs b/crates/graphics/src/primitives/tiles/autotiles.rs similarity index 100% rename from crates/graphics/src/tiles/autotiles.rs rename to crates/graphics/src/primitives/tiles/autotiles.rs diff --git a/crates/graphics/src/tiles/instance.rs b/crates/graphics/src/primitives/tiles/instance.rs similarity index 99% rename from crates/graphics/src/tiles/instance.rs rename to crates/graphics/src/primitives/tiles/instance.rs index 0daad676..1f7ef0b8 100644 --- a/crates/graphics/src/tiles/instance.rs +++ b/crates/graphics/src/primitives/tiles/instance.rs @@ -18,7 +18,7 @@ use itertools::Itertools; use wgpu::util::DeviceExt; -use crate::quad::Quad; +use crate::Quad; #[derive(Debug)] pub struct Instances { diff --git a/crates/graphics/src/tiles/mod.rs b/crates/graphics/src/primitives/tiles/mod.rs similarity index 97% rename from crates/graphics/src/tiles/mod.rs rename to crates/graphics/src/primitives/tiles/mod.rs index 22f89d75..42ca7ad3 100644 --- a/crates/graphics/src/tiles/mod.rs +++ b/crates/graphics/src/primitives/tiles/mod.rs @@ -17,10 +17,7 @@ use std::sync::Arc; -use crate::{ - viewport::{self, Viewport}, - BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, -}; +use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Viewport}; pub use atlas::Atlas; @@ -170,7 +167,7 @@ pub fn create_bind_group_layout( ); if !crate::push_constants_supported(render_state) { - viewport::add_to_bind_group_layout(&mut builder); + Viewport::add_to_bind_group_layout(&mut builder); autotiles::add_to_bind_group_layout(&mut builder); opacity::add_to_bind_group_layout(&mut builder); } diff --git a/crates/graphics/src/tiles/opacity.rs b/crates/graphics/src/primitives/tiles/opacity.rs similarity index 100% rename from crates/graphics/src/tiles/opacity.rs rename to crates/graphics/src/primitives/tiles/opacity.rs diff --git a/crates/graphics/src/tiles/shader.rs b/crates/graphics/src/primitives/tiles/shader.rs similarity index 90% rename from crates/graphics/src/tiles/shader.rs rename to crates/graphics/src/primitives/tiles/shader.rs index 872062dd..72b8908d 100644 --- a/crates/graphics/src/tiles/shader.rs +++ b/crates/graphics/src/primitives/tiles/shader.rs @@ -16,21 +16,16 @@ // along with Luminol. If not, see . use super::instance::Instances; -use crate::{vertex::Vertex, BindGroupLayouts}; +use crate::{primitives::BindGroupLayouts, Vertex}; pub fn create_render_pipeline( + composer: &mut naga_oil::compose::Composer, render_state: &luminol_egui_wgpu::RenderState, bind_group_layouts: &BindGroupLayouts, -) -> wgpu::RenderPipeline { +) -> Result { let push_constants_supported = crate::push_constants_supported(render_state); - let mut composer = naga_oil::compose::Composer::default().with_capabilities( - push_constants_supported - .then_some(naga::valid::Capabilities::PUSH_CONSTANT) - .unwrap_or_default(), - ); - - let result = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { + let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { source: include_str!("tilemap.wgsl"), file_path: "tilemap.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, @@ -83,14 +78,7 @@ pub fn create_render_pipeline( ), ]), additional_imports: &[], - }); - let module = match result { - Ok(module) => module, - Err(e) => { - let error = e.emit_to_string(&composer); - panic!("{error}"); - } - }; + })?; let shader_module = render_state .device @@ -130,7 +118,7 @@ pub fn create_render_pipeline( push_constant_ranges, }); - render_state + Ok(render_state .device .create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("Tilemap Render Pipeline"), @@ -152,5 +140,5 @@ pub fn create_render_pipeline( depth_stencil: None, multisample: wgpu::MultisampleState::default(), multiview: None, - }) + })) } diff --git a/crates/graphics/src/tiles/tilemap.wgsl b/crates/graphics/src/primitives/tiles/tilemap.wgsl similarity index 100% rename from crates/graphics/src/tiles/tilemap.wgsl rename to crates/graphics/src/primitives/tiles/tilemap.wgsl diff --git a/crates/graphics/src/tiles/autotile_ids.rs b/crates/graphics/src/tiles/autotile_ids.rs deleted file mode 100644 index 000e6f54..00000000 --- a/crates/graphics/src/tiles/autotile_ids.rs +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright (C) 2023 Lily Lyons -// -// This file is part of Luminol. -// -// Luminol is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Luminol is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Luminol. If not, see . - -/* -#[derive(Clone, Copy, Debug, Default)] -pub struct Autotile { - pub x: f32, - pub y: f32, -} - -pub const AUTOTILES: [[Autotile; 4]; 48] = [ - [ - Autotile { x: 32.5, y: 64.5 }, - Autotile { x: 48.5, y: 64.5 }, - Autotile { x: 32.5, y: 80.5 }, - Autotile { x: 48.5, y: 80.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 48.5, y: 64.5 }, - Autotile { x: 32.5, y: 80.5 }, - Autotile { x: 48.5, y: 80.5 }, - ], - [ - Autotile { x: 32.5, y: 64.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 32.5, y: 80.5 }, - Autotile { x: 48.5, y: 80.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 32.5, y: 80.5 }, - Autotile { x: 48.5, y: 80.5 }, - ], - [ - Autotile { x: 32.5, y: 64.5 }, - Autotile { x: 48.5, y: 64.5 }, - Autotile { x: 32.5, y: 80.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 48.5, y: 64.5 }, - Autotile { x: 32.5, y: 80.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 32.5, y: 64.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 32.5, y: 80.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 32.5, y: 80.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 32.5, y: 64.5 }, - Autotile { x: 48.5, y: 64.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 48.5, y: 80.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 48.5, y: 64.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 48.5, y: 80.5 }, - ], - [ - Autotile { x: 32.5, y: 64.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 48.5, y: 80.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 48.5, y: 80.5 }, - ], - [ - Autotile { x: 32.5, y: 64.5 }, - Autotile { x: 48.5, y: 64.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 48.5, y: 64.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 32.5, y: 64.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 0.5, y: 64.5 }, - Autotile { x: 16.5, y: 64.5 }, - Autotile { x: 0.5, y: 80.5 }, - Autotile { x: 16.5, y: 80.5 }, - ], - [ - Autotile { x: 0.5, y: 64.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 0.5, y: 80.5 }, - Autotile { x: 16.5, y: 80.5 }, - ], - [ - Autotile { x: 0.5, y: 64.5 }, - Autotile { x: 16.5, y: 64.5 }, - Autotile { x: 0.5, y: 80.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 0.5, y: 64.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 0.5, y: 80.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 32.5, y: 32.5 }, - Autotile { x: 48.5, y: 32.5 }, - Autotile { x: 32.5, y: 48.5 }, - Autotile { x: 48.5, y: 48.5 }, - ], - [ - Autotile { x: 32.5, y: 32.5 }, - Autotile { x: 48.5, y: 32.5 }, - Autotile { x: 32.5, y: 48.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 32.5, y: 32.5 }, - Autotile { x: 48.5, y: 32.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 48.5, y: 48.5 }, - ], - [ - Autotile { x: 32.5, y: 32.5 }, - Autotile { x: 48.5, y: 32.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 64.5, y: 64.5 }, - Autotile { x: 80.5, y: 64.5 }, - Autotile { x: 64.5, y: 80.5 }, - Autotile { x: 80.5, y: 80.5 }, - ], - [ - Autotile { x: 64.5, y: 64.5 }, - Autotile { x: 80.5, y: 64.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 80.5, y: 80.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 80.5, y: 64.5 }, - Autotile { x: 64.5, y: 80.5 }, - Autotile { x: 80.5, y: 80.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 80.5, y: 64.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 80.5, y: 80.5 }, - ], - [ - Autotile { x: 32.5, y: 96.5 }, - Autotile { x: 48.5, y: 96.5 }, - Autotile { x: 32.5, y: 112.5 }, - Autotile { x: 48.5, y: 112.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 48.5, y: 96.5 }, - Autotile { x: 32.5, y: 112.5 }, - Autotile { x: 48.5, y: 112.5 }, - ], - [ - Autotile { x: 32.5, y: 96.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 32.5, y: 112.5 }, - Autotile { x: 48.5, y: 112.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 32.5, y: 112.5 }, - Autotile { x: 48.5, y: 112.5 }, - ], - [ - Autotile { x: 0.5, y: 64.5 }, - Autotile { x: 80.5, y: 64.5 }, - Autotile { x: 0.5, y: 80.5 }, - Autotile { x: 80.5, y: 80.5 }, - ], - [ - Autotile { x: 32.5, y: 32.5 }, - Autotile { x: 48.5, y: 32.5 }, - Autotile { x: 32.5, y: 112.5 }, - Autotile { x: 48.5, y: 112.5 }, - ], - [ - Autotile { x: 0.5, y: 32.5 }, - Autotile { x: 16.5, y: 32.5 }, - Autotile { x: 0.5, y: 48.5 }, - Autotile { x: 16.5, y: 48.5 }, - ], - [ - Autotile { x: 0.5, y: 32.5 }, - Autotile { x: 16.5, y: 32.5 }, - Autotile { x: 0.5, y: 48.5 }, - Autotile { x: 80.5, y: 16.5 }, - ], - [ - Autotile { x: 64.5, y: 32.5 }, - Autotile { x: 80.5, y: 32.5 }, - Autotile { x: 64.5, y: 48.5 }, - Autotile { x: 80.5, y: 48.5 }, - ], - [ - Autotile { x: 64.5, y: 32.5 }, - Autotile { x: 80.5, y: 32.5 }, - Autotile { x: 64.5, y: 16.5 }, - Autotile { x: 80.5, y: 48.5 }, - ], - [ - Autotile { x: 64.5, y: 96.5 }, - Autotile { x: 80.5, y: 96.5 }, - Autotile { x: 64.5, y: 112.5 }, - Autotile { x: 80.5, y: 112.5 }, - ], - [ - Autotile { x: 64.5, y: 0.5 }, - Autotile { x: 80.5, y: 96.5 }, - Autotile { x: 64.5, y: 112.5 }, - Autotile { x: 80.5, y: 112.5 }, - ], - [ - Autotile { x: 0.5, y: 96.5 }, - Autotile { x: 16.5, y: 96.5 }, - Autotile { x: 0.5, y: 112.5 }, - Autotile { x: 16.5, y: 112.5 }, - ], - [ - Autotile { x: 0.5, y: 96.5 }, - Autotile { x: 80.5, y: 0.5 }, - Autotile { x: 0.5, y: 112.5 }, - Autotile { x: 16.5, y: 112.5 }, - ], - [ - Autotile { x: 0.5, y: 32.5 }, - Autotile { x: 80.5, y: 32.5 }, - Autotile { x: 0.5, y: 48.5 }, - Autotile { x: 80.5, y: 48.5 }, - ], - [ - Autotile { x: 0.5, y: 32.5 }, - Autotile { x: 16.5, y: 32.5 }, - Autotile { x: 0.5, y: 112.5 }, - Autotile { x: 16.5, y: 112.5 }, - ], - [ - Autotile { x: 0.5, y: 96.5 }, - Autotile { x: 80.5, y: 96.5 }, - Autotile { x: 0.5, y: 112.5 }, - Autotile { x: 80.5, y: 112.5 }, - ], - [ - Autotile { x: 64.5, y: 32.5 }, - Autotile { x: 80.5, y: 32.5 }, - Autotile { x: 64.5, y: 112.5 }, - Autotile { x: 80.5, y: 112.5 }, - ], - [ - Autotile { x: 0.5, y: 32.5 }, - Autotile { x: 80.5, y: 32.5 }, - Autotile { x: 0.5, y: 112.5 }, - Autotile { x: 80.5, y: 112.5 }, - ], - [ - Autotile { x: 0.5, y: 0.5 }, - Autotile { x: 16.5, y: 0.5 }, - Autotile { x: 0.5, y: 16.5 }, - Autotile { x: 16.5, y: 16.5 }, - ], -]; -*/ - -/// Hardcoded list of tiles from r48 and old python Luminol. -/// There seems to be very little pattern in autotile IDs so this is sadly -/// the best we can do. -pub const AUTOTILES: [[u32; 4]; 48] = [ - [26, 27, 32, 33], - [4, 27, 32, 33], - [26, 5, 32, 33], - [4, 5, 32, 33], - [26, 27, 32, 11], - [4, 27, 32, 11], - [26, 5, 32, 11], - [4, 5, 32, 11], - [26, 27, 10, 33], - [4, 27, 10, 33], - [26, 5, 10, 33], - [4, 5, 10, 33], - [26, 27, 10, 11], - [4, 27, 10, 11], - [26, 5, 10, 11], - [4, 5, 10, 11], - [24, 25, 30, 31], - [24, 5, 30, 31], - [24, 25, 30, 11], - [24, 5, 30, 11], - [14, 15, 20, 21], - [14, 15, 20, 11], - [14, 15, 10, 21], - [14, 15, 10, 11], - [28, 29, 34, 35], - [28, 29, 10, 35], - [4, 29, 34, 35], - [4, 29, 10, 35], - [38, 39, 44, 45], - [4, 39, 44, 45], - [38, 5, 44, 45], - [4, 5, 44, 45], - [24, 29, 30, 35], - [14, 15, 44, 45], - [12, 13, 18, 19], - [12, 13, 18, 11], - [16, 17, 22, 23], - [16, 17, 10, 23], - [40, 41, 46, 47], - [4, 41, 46, 47], - [36, 37, 42, 43], - [36, 5, 42, 43], - [12, 17, 18, 23], - [12, 13, 42, 43], - [36, 41, 42, 47], - [16, 17, 46, 47], - [12, 17, 42, 47], - [0, 1, 6, 7], -]; diff --git a/crates/ui/src/tabs/map/mod.rs b/crates/ui/src/tabs/map/mod.rs index 417cb8b8..cc1a9afe 100644 --- a/crates/ui/src/tabs/map/mod.rs +++ b/crates/ui/src/tabs/map/mod.rs @@ -130,7 +130,7 @@ impl Tab { let tileset = &tilesets.data[map.tileset_id]; let mut passages = luminol_data::Table2::new(map.data.xsize(), map.data.ysize()); - luminol_graphics::collision::calculate_passages( + luminol_graphics::primitives::collision::calculate_passages( &tileset.passages, &tileset.priorities, &map.data, @@ -647,7 +647,7 @@ impl luminol_core::Tab for Tab { } // Update the collision preview - luminol_graphics::collision::calculate_passages( + luminol_graphics::primitives::collision::calculate_passages( &tileset.passages, &tileset.priorities, &map.data, From 874e4569524c2340d22242f0937645553cf058b1 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Sun, 9 Jun 2024 17:56:06 -0700 Subject: [PATCH 02/24] Remove support for push constants --- crates/graphics/src/data/viewport.rs | 32 +++++++------- crates/graphics/src/lib.rs | 11 ----- .../src/primitives/collision/collision.wgsl | 11 ----- .../graphics/src/primitives/collision/mod.rs | 32 +++++--------- .../src/primitives/collision/shader.rs | 38 ++-------------- .../graphics/src/primitives/grid/display.rs | 30 ++++++------- crates/graphics/src/primitives/grid/grid.wgsl | 16 ------- crates/graphics/src/primitives/grid/mod.rs | 41 +++++------------ crates/graphics/src/primitives/grid/shader.rs | 44 ++----------------- .../graphics/src/primitives/sprite/graphic.rs | 32 +++++++------- crates/graphics/src/primitives/sprite/mod.rs | 29 +++--------- .../graphics/src/primitives/sprite/shader.rs | 32 ++------------ .../src/primitives/sprite/sprite.wgsl | 17 ------- .../src/primitives/tiles/autotiles.rs | 32 +++++++------- crates/graphics/src/primitives/tiles/mod.rs | 38 ++++------------ .../graphics/src/primitives/tiles/opacity.rs | 32 +++++++------- .../graphics/src/primitives/tiles/shader.rs | 32 +------------- .../src/primitives/tiles/tilemap.wgsl | 22 ---------- src/main.rs | 11 +---- 19 files changed, 126 insertions(+), 406 deletions(-) diff --git a/crates/graphics/src/data/viewport.rs b/crates/graphics/src/data/viewport.rs index 79c0c2f9..68195fc2 100644 --- a/crates/graphics/src/data/viewport.rs +++ b/crates/graphics/src/data/viewport.rs @@ -23,7 +23,7 @@ use crate::{BindGroupLayoutBuilder, GraphicsState}; #[derive(Debug)] pub struct Viewport { data: AtomicCell, - uniform: Option, + uniform: wgpu::Buffer, } impl Viewport { @@ -35,15 +35,13 @@ impl Viewport { } pub fn new_proj(graphics_state: &GraphicsState, proj: glam::Mat4) -> Self { - let uniform = (!graphics_state.push_constants_supported()).then(|| { - graphics_state.render_state.device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("tilemap viewport buffer"), - contents: bytemuck::cast_slice(&[proj]), - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, - }, - ) - }); + let uniform = graphics_state.render_state.device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("tilemap viewport buffer"), + contents: bytemuck::cast_slice(&[proj]), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, + }, + ); Self { data: AtomicCell::new(proj), @@ -75,16 +73,16 @@ impl Viewport { bytemuck::cast(self.data.load()) } - pub fn as_buffer(&self) -> Option<&wgpu::Buffer> { - self.uniform.as_ref() + pub fn as_buffer(&self) -> &wgpu::Buffer { + &self.uniform } fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { - if let Some(uniform) = &self.uniform { - render_state - .queue - .write_buffer(uniform, 0, bytemuck::cast_slice(&[self.data.load()])); - } + render_state.queue.write_buffer( + &self.uniform, + 0, + bytemuck::cast_slice(&[self.data.load()]), + ); } pub fn add_to_bind_group_layout( diff --git a/crates/graphics/src/lib.rs b/crates/graphics/src/lib.rs index d0a58b83..2340faa6 100644 --- a/crates/graphics/src/lib.rs +++ b/crates/graphics/src/lib.rs @@ -89,10 +89,6 @@ impl GraphicsState { } } - pub fn push_constants_supported(&self) -> bool { - push_constants_supported(&self.render_state) - } - pub fn send_texture_error(&self, error: color_eyre::Report) { self.texture_error_tx .try_send(error) @@ -109,10 +105,3 @@ impl GraphicsState { .to_rgba8() } } - -pub fn push_constants_supported(render_state: &luminol_egui_wgpu::RenderState) -> bool { - render_state - .device - .features() - .contains(wgpu::Features::PUSH_CONSTANTS) -} diff --git a/crates/graphics/src/primitives/collision/collision.wgsl b/crates/graphics/src/primitives/collision/collision.wgsl index 5e46e76b..7abeb3e7 100644 --- a/crates/graphics/src/primitives/collision/collision.wgsl +++ b/crates/graphics/src/primitives/collision/collision.wgsl @@ -16,24 +16,13 @@ struct Viewport { proj: mat4x4, } -#if USE_PUSH_CONSTANTS == true -struct PushConstants { - viewport: Viewport, -} -var push_constants: PushConstants; -#else @group(0) @binding(0) var viewport: Viewport; -#endif @vertex fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { var out: VertexOutput; -#if USE_PUSH_CONSTANTS == true - let viewport = push_constants.viewport; -#endif - if (instance.passage & vertex.direction) == 0u { return out; } diff --git a/crates/graphics/src/primitives/collision/mod.rs b/crates/graphics/src/primitives/collision/mod.rs index cf742b1b..22ff3dcb 100644 --- a/crates/graphics/src/primitives/collision/mod.rs +++ b/crates/graphics/src/primitives/collision/mod.rs @@ -31,7 +31,7 @@ mod vertex; pub struct Collision { pub instances: Instances, pub viewport: Arc, - pub bind_group: Option, + pub bind_group: wgpu::BindGroup, } #[derive(Debug, Clone)] @@ -149,15 +149,13 @@ impl Collision { ) -> Self { let instances = Instances::new(&graphics_state.render_state, passages); - let bind_group = (!graphics_state.push_constants_supported()).then(|| { - let mut bind_group_builder = BindGroupBuilder::new(); - bind_group_builder.append_buffer(viewport.as_buffer().unwrap()); - bind_group_builder.build( - &graphics_state.render_state.device, - Some("collision bind group"), - &graphics_state.bind_group_layouts.collision, - ) - }); + let mut bind_group_builder = BindGroupBuilder::new(); + bind_group_builder.append_buffer(viewport.as_buffer()); + let bind_group = bind_group_builder.build( + &graphics_state.render_state.device, + Some("collision bind group"), + &graphics_state.bind_group_layouts.collision, + ); Self { instances, @@ -183,15 +181,7 @@ impl Collision { render_pass.push_debug_group("tilemap collision renderer"); render_pass.set_pipeline(&graphics_state.pipelines.collision); - if let Some(bind_group) = &self.bind_group { - render_pass.set_bind_group(0, bind_group, &[]) - } else { - render_pass.set_push_constants( - wgpu::ShaderStages::VERTEX, - 0, - &self.viewport.as_bytes(), - ); - } + render_pass.set_bind_group(0, &self.bind_group, &[]); self.instances.draw(render_pass); render_pass.pop_debug_group(); @@ -203,9 +193,7 @@ pub fn create_bind_group_layout( ) -> wgpu::BindGroupLayout { let mut builder = BindGroupLayoutBuilder::new(); - if !crate::push_constants_supported(render_state) { - Viewport::add_to_bind_group_layout(&mut builder); - } + Viewport::add_to_bind_group_layout(&mut builder); builder.build(&render_state.device, Some("collision bind group layout")) } diff --git a/crates/graphics/src/primitives/collision/shader.rs b/crates/graphics/src/primitives/collision/shader.rs index 225db131..eeb70557 100644 --- a/crates/graphics/src/primitives/collision/shader.rs +++ b/crates/graphics/src/primitives/collision/shader.rs @@ -23,16 +23,11 @@ pub fn create_render_pipeline( render_state: &luminol_egui_wgpu::RenderState, bind_group_layouts: &crate::primitives::BindGroupLayouts, ) -> Result { - let push_constants_supported = crate::push_constants_supported(render_state); - let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { source: include_str!("collision.wgsl"), file_path: "collision.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, - shader_defs: std::collections::HashMap::from([( - "USE_PUSH_CONSTANTS".to_string(), - naga_oil::compose::ShaderDefValue::Bool(push_constants_supported), - )]), + shader_defs: std::collections::HashMap::new(), additional_imports: &[], })?; @@ -43,38 +38,13 @@ pub fn create_render_pipeline( source: wgpu::ShaderSource::Naga(std::borrow::Cow::Owned(module)), }); - let push_constant_ranges: &[_] = if push_constants_supported { - &[ - // Viewport - wgpu::PushConstantRange { - stages: wgpu::ShaderStages::VERTEX, - range: 0..64, - }, - ] - } else { - &[] - }; - let label = if push_constants_supported { - "Tilemap Collision Render Pipeline Layout (push constants)" - } else { - "Tilemap Collision Render Pipeline Layout (uniforms)" - }; - - let collision_bgl: &wgpu::BindGroupLayout = &bind_group_layouts.collision; - let bind_group_layout_slice = std::slice::from_ref(&collision_bgl); - let bind_group_layouts: &[&wgpu::BindGroupLayout] = if push_constants_supported { - &[] - } else { - bind_group_layout_slice - }; - let pipeline_layout = render_state .device .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some(label), - bind_group_layouts, - push_constant_ranges, + label: Some("Tilemap Collision Render Pipeline Layout"), + bind_group_layouts: &[&bind_group_layouts.collision], + push_constant_ranges: &[], }); Ok(render_state diff --git a/crates/graphics/src/primitives/grid/display.rs b/crates/graphics/src/primitives/grid/display.rs index a864f819..8cb72fb3 100644 --- a/crates/graphics/src/primitives/grid/display.rs +++ b/crates/graphics/src/primitives/grid/display.rs @@ -23,7 +23,7 @@ use crate::{BindGroupLayoutBuilder, GraphicsState}; #[derive(Debug)] pub struct Display { data: AtomicCell, - uniform: Option, + uniform: wgpu::Buffer, } #[repr(C, align(16))] @@ -42,15 +42,13 @@ impl Display { inner_thickness_in_points: 1., }; - let uniform = (!graphics_state.push_constants_supported()).then(|| { - graphics_state.render_state.device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("grid display buffer"), - contents: bytemuck::bytes_of(&display), - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, - }, - ) - }); + let uniform = graphics_state.render_state.device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("grid display buffer"), + contents: bytemuck::bytes_of(&display), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, + }, + ); Display { data: AtomicCell::new(display), @@ -62,8 +60,8 @@ impl Display { bytemuck::cast(self.data.load()) } - pub fn as_buffer(&self) -> Option<&wgpu::Buffer> { - self.uniform.as_ref() + pub fn as_buffer(&self) -> &wgpu::Buffer { + &self.uniform } pub fn set_inner_thickness( @@ -106,11 +104,9 @@ impl Display { } fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { - if let Some(uniform) = &self.uniform { - render_state - .queue - .write_buffer(uniform, 0, bytemuck::bytes_of(&self.data.load())); - } + render_state + .queue + .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data.load())); } } diff --git a/crates/graphics/src/primitives/grid/grid.wgsl b/crates/graphics/src/primitives/grid/grid.wgsl index 49645607..c8bd220c 100644 --- a/crates/graphics/src/primitives/grid/grid.wgsl +++ b/crates/graphics/src/primitives/grid/grid.wgsl @@ -24,27 +24,15 @@ struct Display { inner_thickness_in_points: f32, } -#if USE_PUSH_CONSTANTS == true -struct PushConstants { - viewport: Viewport, - display: Display, -} -var push_constants: PushConstants; -#else @group(0) @binding(0) var viewport: Viewport; @group(0) @binding(1) var display: Display; -#endif @vertex fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { var out: VertexOutput; -#if USE_PUSH_CONSTANTS == true - let viewport = push_constants.viewport; -#endif - out.position = (viewport.proj * vec4((vertex.position + instance.tile_position) * 32., 0., 1.)).xy; out.vertex_position = out.position; out.clip_position = vec4(out.position, 0., 1.); @@ -53,10 +41,6 @@ fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { @fragment fn fs_main(input: VertexOutput) -> @location(0) vec4 { -#if USE_PUSH_CONSTANTS == true - let display = push_constants.display; -#endif - if display.viewport_size_in_pixels.x == 0. || display.viewport_size_in_pixels.y == 0. { discard; } diff --git a/crates/graphics/src/primitives/grid/mod.rs b/crates/graphics/src/primitives/grid/mod.rs index 87610d63..bcf8d3e6 100644 --- a/crates/graphics/src/primitives/grid/mod.rs +++ b/crates/graphics/src/primitives/grid/mod.rs @@ -34,7 +34,7 @@ pub struct Grid { pub display: display::Display, pub viewport: Arc, - pub bind_group: Option, + pub bind_group: wgpu::BindGroup, } impl Grid { @@ -47,16 +47,14 @@ impl Grid { let instances = Instances::new(&graphics_state.render_state, map_width, map_height); let display = Display::new(graphics_state); - let bind_group = (!graphics_state.push_constants_supported()).then(|| { - let mut bind_group_builder = BindGroupBuilder::new(); - bind_group_builder.append_buffer(viewport.as_buffer().unwrap()); - bind_group_builder.append_buffer(display.as_buffer().unwrap()); - bind_group_builder.build( - &graphics_state.render_state.device, - Some("grid bind group"), - &graphics_state.bind_group_layouts.grid, - ) - }); + let mut bind_group_builder = BindGroupBuilder::new(); + bind_group_builder.append_buffer(viewport.as_buffer()); + bind_group_builder.append_buffer(display.as_buffer()); + let bind_group = bind_group_builder.build( + &graphics_state.render_state.device, + Some("grid bind group"), + &graphics_state.bind_group_layouts.grid, + ); Self { instances, @@ -82,20 +80,7 @@ impl Grid { render_pass.push_debug_group("tilemap grid renderer"); render_pass.set_pipeline(&graphics_state.pipelines.grid); - if let Some(bind_group) = &self.bind_group { - render_pass.set_bind_group(0, bind_group, &[]) - } else { - render_pass.set_push_constants( - wgpu::ShaderStages::VERTEX, - 0, - &self.viewport.as_bytes(), - ); - render_pass.set_push_constants( - wgpu::ShaderStages::FRAGMENT, - 64, - &self.display.as_bytes(), - ); - } + render_pass.set_bind_group(0, &self.bind_group, &[]); self.display .update_viewport_size(&graphics_state.render_state, info); @@ -110,10 +95,8 @@ pub fn create_bind_group_layout( ) -> wgpu::BindGroupLayout { let mut builder = BindGroupLayoutBuilder::new(); - if !crate::push_constants_supported(render_state) { - Viewport::add_to_bind_group_layout(&mut builder); - display::add_to_bind_group_layout(&mut builder); - } + Viewport::add_to_bind_group_layout(&mut builder); + display::add_to_bind_group_layout(&mut builder); builder.build(&render_state.device, Some("grid bind group layout")) } diff --git a/crates/graphics/src/primitives/grid/shader.rs b/crates/graphics/src/primitives/grid/shader.rs index 801dadff..c09adce7 100644 --- a/crates/graphics/src/primitives/grid/shader.rs +++ b/crates/graphics/src/primitives/grid/shader.rs @@ -15,7 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use super::display; use super::instance::Instances; use super::Vertex; @@ -24,16 +23,11 @@ pub fn create_render_pipeline( render_state: &luminol_egui_wgpu::RenderState, bind_group_layouts: &crate::primitives::BindGroupLayouts, ) -> Result { - let push_constants_supported = crate::push_constants_supported(render_state); - let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { source: include_str!("grid.wgsl"), file_path: "grid.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, - shader_defs: std::collections::HashMap::from([( - "USE_PUSH_CONSTANTS".to_string(), - naga_oil::compose::ShaderDefValue::Bool(push_constants_supported), - )]), + shader_defs: std::collections::HashMap::default(), additional_imports: &[], })?; @@ -44,43 +38,13 @@ pub fn create_render_pipeline( source: wgpu::ShaderSource::Naga(std::borrow::Cow::Owned(module)), }); - let push_constant_ranges: &[_] = if push_constants_supported { - &[ - // Vertex - wgpu::PushConstantRange { - stages: wgpu::ShaderStages::VERTEX, - range: 0..64, - }, - // Fragment - wgpu::PushConstantRange { - stages: wgpu::ShaderStages::FRAGMENT, - range: 64..64 + std::mem::size_of::() as u32, - }, - ] - } else { - &[] - }; - let label = if push_constants_supported { - "Tilemap Grid Render Pipeline Layout (push constants)" - } else { - "Tilemap Grid Render Pipeline Layout (uniforms)" - }; - - let grid_bgl: &wgpu::BindGroupLayout = &bind_group_layouts.grid; - let bind_group_layout_slice = std::slice::from_ref(&grid_bgl); - let bind_group_layouts: &[&wgpu::BindGroupLayout] = if push_constants_supported { - &[] - } else { - bind_group_layout_slice - }; - let pipeline_layout = render_state .device .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some(label), - bind_group_layouts, - push_constant_ranges, + label: Some("Tilemap Grid Render Pipeline Layout"), + bind_group_layouts: &[&bind_group_layouts.grid], + push_constant_ranges: &[], }); Ok(render_state diff --git a/crates/graphics/src/primitives/sprite/graphic.rs b/crates/graphics/src/primitives/sprite/graphic.rs index df74b081..5f08bd36 100644 --- a/crates/graphics/src/primitives/sprite/graphic.rs +++ b/crates/graphics/src/primitives/sprite/graphic.rs @@ -23,7 +23,7 @@ use crate::{BindGroupLayoutBuilder, GraphicsState}; #[derive(Debug)] pub struct Graphic { data: AtomicCell, - uniform: Option, + uniform: wgpu::Buffer, } #[repr(C)] @@ -46,15 +46,13 @@ impl Graphic { _padding: 0, }; - let uniform = (!graphics_state.push_constants_supported()).then(|| { - graphics_state.render_state.device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("tilemap sprite graphic buffer"), - contents: bytemuck::cast_slice(&[data]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }, - ) - }); + let uniform = graphics_state.render_state.device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("tilemap sprite graphic buffer"), + contents: bytemuck::cast_slice(&[data]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }, + ); Self { data: AtomicCell::new(data), @@ -114,16 +112,16 @@ impl Graphic { bytemuck::cast(self.data.load()) } - pub fn as_buffer(&self) -> Option<&wgpu::Buffer> { - self.uniform.as_ref() + pub fn as_buffer(&self) -> &wgpu::Buffer { + &self.uniform } fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { - if let Some(uniform) = &self.uniform { - render_state - .queue - .write_buffer(uniform, 0, bytemuck::cast_slice(&[self.data.load()])); - } + render_state.queue.write_buffer( + &self.uniform, + 0, + bytemuck::cast_slice(&[self.data.load()]), + ); } } diff --git a/crates/graphics/src/primitives/sprite/mod.rs b/crates/graphics/src/primitives/sprite/mod.rs index 01f669b7..53f5718c 100644 --- a/crates/graphics/src/primitives/sprite/mod.rs +++ b/crates/graphics/src/primitives/sprite/mod.rs @@ -50,11 +50,11 @@ impl Sprite { bind_group_builder .append_texture_view(&texture.view) .append_sampler(&graphics_state.nearest_sampler); - if !graphics_state.push_constants_supported() { - bind_group_builder - .append_buffer(viewport.as_buffer().unwrap()) - .append_buffer(graphic.as_buffer().unwrap()); - } + + bind_group_builder + .append_buffer(viewport.as_buffer()) + .append_buffer(graphic.as_buffer()); + let bind_group = bind_group_builder.build( &graphics_state.render_state.device, Some("sprite bind group"), @@ -90,19 +90,6 @@ impl Sprite { render_pass.set_pipeline(&graphics_state.pipelines.sprites[&self.blend_mode]); render_pass.set_bind_group(0, &self.bind_group, &[]); - if graphics_state.push_constants_supported() { - render_pass.set_push_constants( - wgpu::ShaderStages::VERTEX, - 0, - &self.viewport.as_bytes(), - ); - render_pass.set_push_constants( - wgpu::ShaderStages::FRAGMENT, - 64, - &self.graphic.as_bytes(), - ); - } - self.vertices.draw(render_pass); render_pass.pop_debug_group(); } @@ -128,10 +115,8 @@ pub fn create_bind_group_layout( None, ); - if !crate::push_constants_supported(render_state) { - Viewport::add_to_bind_group_layout(&mut builder); - graphic::add_to_bind_group_layout(&mut builder); - } + Viewport::add_to_bind_group_layout(&mut builder); + graphic::add_to_bind_group_layout(&mut builder); builder.build(&render_state.device, Some("sprite bind group layout")) } diff --git a/crates/graphics/src/primitives/sprite/shader.rs b/crates/graphics/src/primitives/sprite/shader.rs index 69c82e7b..f921b8ac 100644 --- a/crates/graphics/src/primitives/sprite/shader.rs +++ b/crates/graphics/src/primitives/sprite/shader.rs @@ -27,16 +27,11 @@ fn create_shader( bind_group_layouts: &BindGroupLayouts, target: wgpu::BlendState, ) -> Result { - let push_constants_supported = crate::push_constants_supported(render_state); - let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { source: include_str!("sprite.wgsl"), file_path: "sprite.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, - shader_defs: HashMap::from([( - "USE_PUSH_CONSTANTS".to_string(), - naga_oil::compose::ShaderDefValue::Bool(push_constants_supported), - )]), + shader_defs: HashMap::new(), additional_imports: &[], })?; @@ -47,34 +42,13 @@ fn create_shader( source: wgpu::ShaderSource::Naga(std::borrow::Cow::Owned(module)), }); - let push_constant_ranges: &[_] = if push_constants_supported { - &[ - // Viewport - wgpu::PushConstantRange { - stages: wgpu::ShaderStages::VERTEX, - range: 0..64, - }, - wgpu::PushConstantRange { - stages: wgpu::ShaderStages::FRAGMENT, - range: 64..(64 + 16), - }, - ] - } else { - &[] - }; - let label = if push_constants_supported { - "Sprite Pipeline Layout (push constants)" - } else { - "Sprite Pipeline Layout (uniforms)" - }; - let pipeline_layout = render_state .device .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some(label), + label: Some("Sprite Pipeline Layout"), bind_group_layouts: &[&bind_group_layouts.sprite], - push_constant_ranges, + push_constant_ranges: &[], }); Ok(render_state diff --git a/crates/graphics/src/primitives/sprite/sprite.wgsl b/crates/graphics/src/primitives/sprite/sprite.wgsl index d9f66723..aabae7b5 100644 --- a/crates/graphics/src/primitives/sprite/sprite.wgsl +++ b/crates/graphics/src/primitives/sprite/sprite.wgsl @@ -25,19 +25,10 @@ var t_diffuse: texture_2d; @group(0) @binding(1) var s_diffuse: sampler; -#if USE_PUSH_CONSTANTS == true -struct PushConstants { - viewport: Viewport, - graphic: Graphic, -} -var push_constants: PushConstants; -#else @group(0) @binding(2) var viewport: Viewport; @group(0) @binding(3) var graphic: Graphic; -#endif - fn rgb_to_hsv(c: vec3) -> vec3 { let K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); @@ -68,10 +59,6 @@ fn vs_main( var out: VertexOutput; out.tex_coords = model.tex_coords; -#if USE_PUSH_CONSTANTS == true - let viewport = push_constants.viewport; -#endif - var position = viewport.proj * vec4(model.position.xy, 0.0, 1.0); out.clip_position = vec4(position.xy, model.position.z, 1.0); @@ -95,10 +82,6 @@ fn gamma_from_linear_rgba(linear_rgba: vec4) -> vec4 { fn fs_main(in: VertexOutput) -> @location(0) vec4 { var tex_sample = textureSample(t_diffuse, s_diffuse, in.tex_coords); -#if USE_PUSH_CONSTANTS == true - let graphic = push_constants.graphic; -#endif - tex_sample.a *= graphic.opacity * graphic.opacity_multiplier; if tex_sample.a <= 0. { discard; diff --git a/crates/graphics/src/primitives/tiles/autotiles.rs b/crates/graphics/src/primitives/tiles/autotiles.rs index a763aa3d..e2282ffa 100644 --- a/crates/graphics/src/primitives/tiles/autotiles.rs +++ b/crates/graphics/src/primitives/tiles/autotiles.rs @@ -23,7 +23,7 @@ use crate::{BindGroupLayoutBuilder, GraphicsState}; #[derive(Debug)] pub struct Autotiles { data: AtomicCell, - uniform: Option, + uniform: wgpu::Buffer, } #[repr(C, align(16))] @@ -46,15 +46,13 @@ impl Autotiles { _end_padding: 0, }; - let uniform = (!graphics_state.push_constants_supported()).then(|| { - graphics_state.render_state.device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("tilemap autotile buffer"), - contents: bytemuck::cast_slice(&[autotiles]), - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, - }, - ) - }); + let uniform = graphics_state.render_state.device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("tilemap autotile buffer"), + contents: bytemuck::cast_slice(&[autotiles]), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, + }, + ); Autotiles { data: AtomicCell::new(autotiles), @@ -75,16 +73,16 @@ impl Autotiles { bytemuck::cast(self.data.load()) } - pub fn as_buffer(&self) -> Option<&wgpu::Buffer> { - self.uniform.as_ref() + pub fn as_buffer(&self) -> &wgpu::Buffer { + &self.uniform } fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { - if let Some(uniform) = &self.uniform { - render_state - .queue - .write_buffer(uniform, 0, bytemuck::cast_slice(&[self.data.load()])); - } + render_state.queue.write_buffer( + &self.uniform, + 0, + bytemuck::cast_slice(&[self.data.load()]), + ); } } diff --git a/crates/graphics/src/primitives/tiles/mod.rs b/crates/graphics/src/primitives/tiles/mod.rs index 42ca7ad3..cae20349 100644 --- a/crates/graphics/src/primitives/tiles/mod.rs +++ b/crates/graphics/src/primitives/tiles/mod.rs @@ -61,12 +61,12 @@ impl Tiles { bind_group_builder .append_texture_view(&atlas.atlas_texture.view) .append_sampler(&graphics_state.nearest_sampler); - if !graphics_state.push_constants_supported() { - bind_group_builder - .append_buffer(viewport.as_buffer().unwrap()) - .append_buffer(autotiles.as_buffer().unwrap()) - .append_buffer(opacity.as_buffer().unwrap()); - } + + bind_group_builder + .append_buffer(viewport.as_buffer()) + .append_buffer(autotiles.as_buffer()) + .append_buffer(opacity.as_buffer()); + let bind_group = bind_group_builder.build( &graphics_state.render_state.device, Some("tilemap bind group"), @@ -111,17 +111,6 @@ impl Tiles { render_pass.set_pipeline(&graphics_state.pipelines.tiles); render_pass.set_bind_group(0, &self.bind_group, &[]); - if graphics_state.push_constants_supported() { - render_pass.set_push_constants( - wgpu::ShaderStages::VERTEX, - 0, - bytemuck::bytes_of(&VertexPushConstant { - viewport: self.viewport.as_bytes(), - autotiles: self.autotiles.as_bytes(), - }), - ); - } - for (layer, enabled) in enabled_layers.iter().copied().enumerate() { let opacity = if selected_layer.is_some_and(|s| s != layer) { 0.5 @@ -131,13 +120,6 @@ impl Tiles { if enabled { self.opacity .set_opacity(&graphics_state.render_state, layer, opacity); - if graphics_state.push_constants_supported() { - render_pass.set_push_constants( - wgpu::ShaderStages::FRAGMENT, - 64 + 48, - bytemuck::bytes_of::(&opacity), - ); - } self.instances.draw(render_pass, layer); } @@ -166,11 +148,9 @@ pub fn create_bind_group_layout( None, ); - if !crate::push_constants_supported(render_state) { - Viewport::add_to_bind_group_layout(&mut builder); - autotiles::add_to_bind_group_layout(&mut builder); - opacity::add_to_bind_group_layout(&mut builder); - } + Viewport::add_to_bind_group_layout(&mut builder); + autotiles::add_to_bind_group_layout(&mut builder); + opacity::add_to_bind_group_layout(&mut builder); builder.build(&render_state.device, Some("tilemap bind group layout")) } diff --git a/crates/graphics/src/primitives/tiles/opacity.rs b/crates/graphics/src/primitives/tiles/opacity.rs index 65721a11..db2b07d5 100644 --- a/crates/graphics/src/primitives/tiles/opacity.rs +++ b/crates/graphics/src/primitives/tiles/opacity.rs @@ -23,22 +23,20 @@ use crate::{BindGroupLayoutBuilder, GraphicsState}; #[derive(Debug)] pub struct Opacity { data: AtomicCell<[f32; 4]>, // length has to be a multiple of 4 - uniform: Option, + uniform: wgpu::Buffer, } impl Opacity { pub fn new(graphics_state: &GraphicsState) -> Self { let opacity = [1.; 4]; - let uniform = (!graphics_state.push_constants_supported()).then(|| { - graphics_state.render_state.device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("tilemap opacity buffer"), - contents: bytemuck::cast_slice(&[opacity]), - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, - }, - ) - }); + let uniform = graphics_state.render_state.device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("tilemap opacity buffer"), + contents: bytemuck::cast_slice(&[opacity]), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, + }, + ); Self { data: AtomicCell::new(opacity), @@ -50,8 +48,8 @@ impl Opacity { self.data.load()[layer] } - pub fn as_buffer(&self) -> Option<&wgpu::Buffer> { - self.uniform.as_ref() + pub fn as_buffer(&self) -> &wgpu::Buffer { + &self.uniform } pub fn set_opacity( @@ -69,11 +67,11 @@ impl Opacity { } fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { - if let Some(uniform) = &self.uniform { - render_state - .queue - .write_buffer(uniform, 0, bytemuck::cast_slice(&[self.data.load()])); - } + render_state.queue.write_buffer( + &self.uniform, + 0, + bytemuck::cast_slice(&[self.data.load()]), + ); } } diff --git a/crates/graphics/src/primitives/tiles/shader.rs b/crates/graphics/src/primitives/tiles/shader.rs index 72b8908d..2a178ae3 100644 --- a/crates/graphics/src/primitives/tiles/shader.rs +++ b/crates/graphics/src/primitives/tiles/shader.rs @@ -23,17 +23,11 @@ pub fn create_render_pipeline( render_state: &luminol_egui_wgpu::RenderState, bind_group_layouts: &BindGroupLayouts, ) -> Result { - let push_constants_supported = crate::push_constants_supported(render_state); - let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { source: include_str!("tilemap.wgsl"), file_path: "tilemap.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, shader_defs: std::collections::HashMap::from([ - ( - "USE_PUSH_CONSTANTS".to_string(), - naga_oil::compose::ShaderDefValue::Bool(push_constants_supported), - ), ( "AUTOTILE_ID_AMOUNT".to_string(), naga_oil::compose::ShaderDefValue::UInt(super::atlas::AUTOTILE_ID_AMOUNT), @@ -87,35 +81,13 @@ pub fn create_render_pipeline( source: wgpu::ShaderSource::Naga(std::borrow::Cow::Owned(module)), }); - let push_constant_ranges: &[_] = if push_constants_supported { - &[ - // Viewport + Autotiles - wgpu::PushConstantRange { - stages: wgpu::ShaderStages::VERTEX, - range: 0..(64 + 48), - }, - // Fragment - wgpu::PushConstantRange { - stages: wgpu::ShaderStages::FRAGMENT, - range: (64 + 48)..(64 + 48 + 4), - }, - ] - } else { - &[] - }; - let label = if push_constants_supported { - "Tilemap Render Pipeline Layout (push constants)" - } else { - "Tilemap Render Pipeline Layout (uniforms)" - }; - let pipeline_layout = render_state .device .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some(label), + label: Some("Tilemap Render Pipeline Layout"), bind_group_layouts: &[&bind_group_layouts.tiles], - push_constant_ranges, + push_constant_ranges: &[], }); Ok(render_state diff --git a/crates/graphics/src/primitives/tiles/tilemap.wgsl b/crates/graphics/src/primitives/tiles/tilemap.wgsl index eb713247..1bcd8e4d 100644 --- a/crates/graphics/src/primitives/tiles/tilemap.wgsl +++ b/crates/graphics/src/primitives/tiles/tilemap.wgsl @@ -31,32 +31,18 @@ var atlas: texture_2d; @group(0) @binding(1) var atlas_sampler: sampler; -#if USE_PUSH_CONSTANTS == true -struct PushConstants { - viewport: Viewport, - autotiles: Autotiles, - opacity: f32, -} -var push_constants: PushConstants; -#else @group(0) @binding(2) var viewport: Viewport; @group(0) @binding(3) var autotiles: Autotiles; @group(0) @binding(4) var opacity: array, 1>; -#endif @vertex fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { var out: VertexOutput; out.layer = instance.layer; -#if USE_PUSH_CONSTANTS == true - let viewport = push_constants.viewport; - let autotiles = push_constants.autotiles; -#endif - if instance.tile_id < #AUTOTILE_ID_AMOUNT { return out; } @@ -96,11 +82,7 @@ fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { let autotile_type = instance.tile_id / #AUTOTILE_ID_AMOUNT - 1; // we get an error about non constant indexing without this. // not sure why -#if USE_PUSH_CONSTANTS == true - let frame_count = push_constants.autotiles.frame_counts[autotile_type / 4][autotile_type % 4]; -#else let frame_count = autotiles.frame_counts[autotile_type / 4][autotile_type % 4]; -#endif let frame = autotiles.animation_index % frame_count; atlas_tile_position.x += f32(frame * #AUTOTILE_FRAME_WIDTH); @@ -128,11 +110,7 @@ fn gamma_from_linear_rgba(linear_rgba: vec4) -> vec4 { fn fs_main(input: VertexOutput) -> @location(0) vec4 { var color = textureSample(atlas, atlas_sampler, input.tex_coords); -#if USE_PUSH_CONSTANTS == true - let layer_opacity = push_constants.opacity; -#else let layer_opacity = opacity[input.layer / 4u][input.layer % 4u]; -#endif color.a *= layer_opacity; if color.a <= 0.0 { diff --git a/src/main.rs b/src/main.rs index 192531bc..11046d1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -224,15 +224,8 @@ fn main() { wgpu_options: luminol_egui_wgpu::WgpuConfiguration { supported_backends: wgpu::util::backend_bits_from_env() .unwrap_or(wgpu::Backends::PRIMARY), - device_descriptor: std::sync::Arc::new(|_| wgpu::DeviceDescriptor { - label: Some("luminol device descriptor"), - required_features: wgpu::Features::PUSH_CONSTANTS, - required_limits: wgpu::Limits { - max_push_constant_size: 128, - ..wgpu::Limits::default() - }, - }), - power_preference: wgpu::util::power_preference_from_env().unwrap_or_default(), + power_preference: wgpu::util::power_preference_from_env() + .unwrap_or(wgpu::PowerPreference::LowPower), ..Default::default() }, persist_window: true, From 20bad7864ed2280a3f0ad440cf1fea718f53a270 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Sun, 9 Jun 2024 18:01:52 -0700 Subject: [PATCH 03/24] Use bytes_of and from_bytes --- crates/components/src/map_view.rs | 2 +- crates/data/src/rgss_structs.rs | 8 +- crates/graphics/src/data/viewport.rs | 10 +- .../graphics/src/primitives/collision/mod.rs | 196 +++++++++--------- .../graphics/src/primitives/sprite/graphic.rs | 10 +- .../src/primitives/tiles/autotiles.rs | 10 +- .../graphics/src/primitives/tiles/opacity.rs | 10 +- crates/ui/src/tabs/map/mod.rs | 4 +- 8 files changed, 122 insertions(+), 128 deletions(-) diff --git a/crates/components/src/map_view.rs b/crates/components/src/map_view.rs index 45b557ff..2a3df2b2 100644 --- a/crates/components/src/map_view.rs +++ b/crates/components/src/map_view.rs @@ -79,7 +79,7 @@ impl MapView { let tileset = &tilesets.data[map.tileset_id]; let mut passages = luminol_data::Table2::new(map.data.xsize(), map.data.ysize()); - luminol_graphics::primitives::collision::calculate_passages( + luminol_graphics::Collision::calculate_passages( &tileset.passages, &tileset.priorities, &map.data, diff --git a/crates/data/src/rgss_structs.rs b/crates/data/src/rgss_structs.rs index ba7607f1..c07b97c6 100644 --- a/crates/data/src/rgss_structs.rs +++ b/crates/data/src/rgss_structs.rs @@ -18,7 +18,7 @@ pub struct Color { impl From for Color { fn from(value: alox_48::Userdata) -> Self { - bytemuck::cast_slice(&value.data)[0] + *bytemuck::from_bytes(&value.data) } } @@ -26,7 +26,7 @@ impl From for alox_48::Userdata { fn from(value: Color) -> Self { alox_48::Userdata { class: "Color".into(), - data: bytemuck::cast_slice(&[value]).to_vec(), + data: bytemuck::bytes_of(&value).to_vec(), } } } @@ -67,7 +67,7 @@ pub struct Tone { impl From for Tone { fn from(value: alox_48::Userdata) -> Self { - bytemuck::cast_slice(&value.data)[0] + *bytemuck::from_bytes(&value.data) } } @@ -75,7 +75,7 @@ impl From for alox_48::Userdata { fn from(value: Tone) -> Self { alox_48::Userdata { class: "Tone".into(), - data: bytemuck::cast_slice(&[value]).to_vec(), + data: bytemuck::bytes_of(&value).to_vec(), } } } diff --git a/crates/graphics/src/data/viewport.rs b/crates/graphics/src/data/viewport.rs index 68195fc2..a8e82f70 100644 --- a/crates/graphics/src/data/viewport.rs +++ b/crates/graphics/src/data/viewport.rs @@ -38,7 +38,7 @@ impl Viewport { let uniform = graphics_state.render_state.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("tilemap viewport buffer"), - contents: bytemuck::cast_slice(&[proj]), + contents: bytemuck::bytes_of(&proj), usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, }, ); @@ -78,11 +78,9 @@ impl Viewport { } fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { - render_state.queue.write_buffer( - &self.uniform, - 0, - bytemuck::cast_slice(&[self.data.load()]), - ); + render_state + .queue + .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data.load())); } pub fn add_to_bind_group_layout( diff --git a/crates/graphics/src/primitives/collision/mod.rs b/crates/graphics/src/primitives/collision/mod.rs index 22ff3dcb..3b98046f 100644 --- a/crates/graphics/src/primitives/collision/mod.rs +++ b/crates/graphics/src/primitives/collision/mod.rs @@ -44,103 +44,6 @@ pub enum CollisionType { Tile, } -/// Determines the passage values for every position on the map, running `f(x, y, passage)` for -/// every position. -/// -/// `layers` should be an iterator over the enabled layer numbers of the map from top to bottom. -pub fn calculate_passages( - passages: &luminol_data::Table1, - priorities: &luminol_data::Table1, - tiles: &luminol_data::Table3, - events: Option<&luminol_data::OptionVec>, - layers: impl Iterator + Clone, - mut f: impl FnMut(usize, usize, i16), -) { - let tileset_size = passages.len().min(priorities.len()); - - let mut event_map = if let Some(events) = events { - events - .iter() - .filter_map(|(_, event)| { - let page = event.pages.first()?; - if page.through { - return None; - } - let tile_event = page - .graphic - .tile_id - .map_or((15, 1, CollisionType::Event), |id| { - let tile_id = id + 1; - if tile_id >= tileset_size { - (0, 0, CollisionType::Event) - } else { - (passages[tile_id], priorities[tile_id], CollisionType::Event) - } - }); - Some(((event.x as usize, event.y as usize), tile_event)) - }) - .collect() - } else { - std::collections::HashMap::new() - }; - - for (y, x) in (0..tiles.ysize()).cartesian_product(0..tiles.xsize()) { - let tile_event = event_map.remove(&(x, y)); - - f( - x, - y, - calculate_passage(tile_event.into_iter().chain(layers.clone().map(|z| { - let tile_id = tiles[(x, y, z)].try_into().unwrap_or_default(); - let collision_type = if tile_id < 48 { - CollisionType::BlankTile - } else { - CollisionType::Tile - }; - if tile_id >= tileset_size { - (0, 0, collision_type) - } else { - (passages[tile_id], priorities[tile_id], collision_type) - } - }))), - ); - } -} - -/// Determines the passage value for a position on the map given an iterator over the -/// `(passage, priority, collision_type)` values for the tiles in each layer on that position. -/// The iterator should iterate over the layers from top to bottom. -pub fn calculate_passage(layers: impl Iterator + Clone) -> i16 { - let mut computed_passage = 0; - - for direction in [1, 2, 4, 8] { - let mut at_least_one_layer_not_blank = false; - let mut layers = layers.clone().peekable(); - while let Some((passage, priority, collision_type)) = layers.next() { - if matches!( - collision_type, - CollisionType::Tile | CollisionType::BlankTile - ) { - if matches!(collision_type, CollisionType::BlankTile) - && (at_least_one_layer_not_blank || layers.peek().is_some()) - { - continue; - } else { - at_least_one_layer_not_blank = true; - } - } - if passage & direction != 0 { - computed_passage |= direction; - break; - } else if priority == 0 { - break; - } - } - } - - computed_passage -} - impl Collision { pub fn new( graphics_state: &GraphicsState, @@ -186,6 +89,105 @@ impl Collision { self.instances.draw(render_pass); render_pass.pop_debug_group(); } + + /// Determines the passage values for every position on the map, running `f(x, y, passage)` for + /// every position. + /// + /// `layers` should be an iterator over the enabled layer numbers of the map from top to bottom. + pub fn calculate_passages( + passages: &luminol_data::Table1, + priorities: &luminol_data::Table1, + tiles: &luminol_data::Table3, + events: Option<&luminol_data::OptionVec>, + layers: impl Iterator + Clone, + mut f: impl FnMut(usize, usize, i16), + ) { + let tileset_size = passages.len().min(priorities.len()); + + let mut event_map = if let Some(events) = events { + events + .iter() + .filter_map(|(_, event)| { + let page = event.pages.first()?; + if page.through { + return None; + } + let tile_event = + page.graphic + .tile_id + .map_or((15, 1, CollisionType::Event), |id| { + let tile_id = id + 1; + if tile_id >= tileset_size { + (0, 0, CollisionType::Event) + } else { + (passages[tile_id], priorities[tile_id], CollisionType::Event) + } + }); + Some(((event.x as usize, event.y as usize), tile_event)) + }) + .collect() + } else { + std::collections::HashMap::new() + }; + + for (y, x) in (0..tiles.ysize()).cartesian_product(0..tiles.xsize()) { + let tile_event = event_map.remove(&(x, y)); + + f( + x, + y, + Self::calculate_passage(tile_event.into_iter().chain(layers.clone().map(|z| { + let tile_id = tiles[(x, y, z)].try_into().unwrap_or_default(); + let collision_type = if tile_id < 48 { + CollisionType::BlankTile + } else { + CollisionType::Tile + }; + if tile_id >= tileset_size { + (0, 0, collision_type) + } else { + (passages[tile_id], priorities[tile_id], collision_type) + } + }))), + ); + } + } + + /// Determines the passage value for a position on the map given an iterator over the + /// `(passage, priority, collision_type)` values for the tiles in each layer on that position. + /// The iterator should iterate over the layers from top to bottom. + pub fn calculate_passage( + layers: impl Iterator + Clone, + ) -> i16 { + let mut computed_passage = 0; + + for direction in [1, 2, 4, 8] { + let mut at_least_one_layer_not_blank = false; + let mut layers = layers.clone().peekable(); + while let Some((passage, priority, collision_type)) = layers.next() { + if matches!( + collision_type, + CollisionType::Tile | CollisionType::BlankTile + ) { + if matches!(collision_type, CollisionType::BlankTile) + && (at_least_one_layer_not_blank || layers.peek().is_some()) + { + continue; + } else { + at_least_one_layer_not_blank = true; + } + } + if passage & direction != 0 { + computed_passage |= direction; + break; + } else if priority == 0 { + break; + } + } + } + + computed_passage + } } pub fn create_bind_group_layout( diff --git a/crates/graphics/src/primitives/sprite/graphic.rs b/crates/graphics/src/primitives/sprite/graphic.rs index 5f08bd36..453caf34 100644 --- a/crates/graphics/src/primitives/sprite/graphic.rs +++ b/crates/graphics/src/primitives/sprite/graphic.rs @@ -49,7 +49,7 @@ impl Graphic { let uniform = graphics_state.render_state.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("tilemap sprite graphic buffer"), - contents: bytemuck::cast_slice(&[data]), + contents: bytemuck::bytes_of(&data), usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, }, ); @@ -117,11 +117,9 @@ impl Graphic { } fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { - render_state.queue.write_buffer( - &self.uniform, - 0, - bytemuck::cast_slice(&[self.data.load()]), - ); + render_state + .queue + .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data.load())); } } diff --git a/crates/graphics/src/primitives/tiles/autotiles.rs b/crates/graphics/src/primitives/tiles/autotiles.rs index e2282ffa..459a7fa7 100644 --- a/crates/graphics/src/primitives/tiles/autotiles.rs +++ b/crates/graphics/src/primitives/tiles/autotiles.rs @@ -49,7 +49,7 @@ impl Autotiles { let uniform = graphics_state.render_state.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("tilemap autotile buffer"), - contents: bytemuck::cast_slice(&[autotiles]), + contents: bytemuck::bytes_of(&autotiles), usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, }, ); @@ -78,11 +78,9 @@ impl Autotiles { } fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { - render_state.queue.write_buffer( - &self.uniform, - 0, - bytemuck::cast_slice(&[self.data.load()]), - ); + render_state + .queue + .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data.load())); } } diff --git a/crates/graphics/src/primitives/tiles/opacity.rs b/crates/graphics/src/primitives/tiles/opacity.rs index db2b07d5..2f46007e 100644 --- a/crates/graphics/src/primitives/tiles/opacity.rs +++ b/crates/graphics/src/primitives/tiles/opacity.rs @@ -33,7 +33,7 @@ impl Opacity { let uniform = graphics_state.render_state.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("tilemap opacity buffer"), - contents: bytemuck::cast_slice(&[opacity]), + contents: bytemuck::bytes_of(&opacity), usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, }, ); @@ -67,11 +67,9 @@ impl Opacity { } fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { - render_state.queue.write_buffer( - &self.uniform, - 0, - bytemuck::cast_slice(&[self.data.load()]), - ); + render_state + .queue + .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data.load())); } } diff --git a/crates/ui/src/tabs/map/mod.rs b/crates/ui/src/tabs/map/mod.rs index cc1a9afe..a419315f 100644 --- a/crates/ui/src/tabs/map/mod.rs +++ b/crates/ui/src/tabs/map/mod.rs @@ -130,7 +130,7 @@ impl Tab { let tileset = &tilesets.data[map.tileset_id]; let mut passages = luminol_data::Table2::new(map.data.xsize(), map.data.ysize()); - luminol_graphics::primitives::collision::calculate_passages( + luminol_graphics::Collision::calculate_passages( &tileset.passages, &tileset.priorities, &map.data, @@ -647,7 +647,7 @@ impl luminol_core::Tab for Tab { } // Update the collision preview - luminol_graphics::primitives::collision::calculate_passages( + luminol_graphics::Collision::calculate_passages( &tileset.passages, &tileset.priorities, &map.data, From 706385ef382299c937f441672b39b80bd5cfa61e Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Mon, 10 Jun 2024 21:35:15 -0700 Subject: [PATCH 04/24] Use vec2 instead of vec3 --- Cargo.lock | 1 + crates/graphics/Cargo.toml | 1 + crates/graphics/src/data/quad.rs | 15 +- crates/graphics/src/data/vertex.rs | 4 +- crates/graphics/src/event.rs | 2 +- crates/graphics/src/plane.rs | 1 - .../src/primitives/collision/instance.rs | 20 +- .../src/primitives/collision/shader.rs | 2 +- .../src/primitives/collision/vertex.rs | 4 +- .../{collision => shaders}/collision.wgsl | 6 +- .../src/primitives/shaders/gamma.wgsl | 14 ++ .../graphics/src/primitives/shaders/hue.wgsl | 23 +++ .../src/primitives/shaders/sprite.wgsl | 66 +++++++ .../{tiles => shaders}/tilemap.wgsl | 46 +++-- .../graphics/src/primitives/sprite/shader.rs | 14 +- .../src/primitives/sprite/sprite.wgsl | 98 ---------- crates/graphics/src/primitives/tiles/atlas.rs | 1 - .../graphics/src/primitives/tiles/display.rs | 173 ++++++++++++++++++ .../graphics/src/primitives/tiles/instance.rs | 28 +-- crates/graphics/src/primitives/tiles/mod.rs | 30 ++- .../graphics/src/primitives/tiles/opacity.rs | 88 --------- .../graphics/src/primitives/tiles/shader.rs | 8 +- 22 files changed, 366 insertions(+), 279 deletions(-) rename crates/graphics/src/primitives/{collision => shaders}/collision.wgsl (81%) create mode 100644 crates/graphics/src/primitives/shaders/gamma.wgsl create mode 100644 crates/graphics/src/primitives/shaders/hue.wgsl create mode 100644 crates/graphics/src/primitives/shaders/sprite.wgsl rename crates/graphics/src/primitives/{tiles => shaders}/tilemap.wgsl (76%) delete mode 100644 crates/graphics/src/primitives/sprite/sprite.wgsl create mode 100644 crates/graphics/src/primitives/tiles/display.rs delete mode 100644 crates/graphics/src/primitives/tiles/opacity.rs diff --git a/Cargo.lock b/Cargo.lock index 1b6805be..83178e9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3186,6 +3186,7 @@ dependencies = [ "luminol-filesystem", "naga", "naga_oil", + "parking_lot", "wgpu", ] diff --git a/crates/graphics/Cargo.toml b/crates/graphics/Cargo.toml index 9efca02d..fa817f6c 100644 --- a/crates/graphics/Cargo.toml +++ b/crates/graphics/Cargo.toml @@ -29,6 +29,7 @@ naga = "0.19.0" crossbeam.workspace = true dashmap.workspace = true +parking_lot.workspace = true color-eyre.workspace = true diff --git a/crates/graphics/src/data/quad.rs b/crates/graphics/src/data/quad.rs index 79b9e3dc..61334bd0 100644 --- a/crates/graphics/src/data/quad.rs +++ b/crates/graphics/src/data/quad.rs @@ -21,12 +21,11 @@ use wgpu::util::DeviceExt; pub struct Quad { pub pos: egui::Rect, pub tex_coords: egui::Rect, - pub z: f32, } impl Quad { - pub const fn new(pos: egui::Rect, tex_coords: egui::Rect, z: f32) -> Self { - Self { pos, tex_coords, z } + pub const fn new(pos: egui::Rect, tex_coords: egui::Rect) -> Self { + Self { pos, tex_coords } } fn norm_tex_coords(self, extents: wgpu::Extent3d) -> Self { @@ -41,12 +40,12 @@ impl Quad { } fn into_corners(self) -> [Vertex; 4] { - let Self { pos, tex_coords, z } = self; + let Self { pos, tex_coords } = self; let top_left = { let position = pos.left_top(); let tex_coords = tex_coords.left_top(); Vertex { - position: glam::vec3(position.x, position.y, z), + position: glam::vec2(position.x, position.y), tex_coords: glam::vec2(tex_coords.x, tex_coords.y), } }; @@ -54,7 +53,7 @@ impl Quad { let position = pos.right_top(); let tex_coords = tex_coords.right_top(); Vertex { - position: glam::vec3(position.x, position.y, z), + position: glam::vec2(position.x, position.y), tex_coords: glam::vec2(tex_coords.x, tex_coords.y), } }; @@ -62,7 +61,7 @@ impl Quad { let position = pos.right_bottom(); let tex_coords = tex_coords.right_bottom(); Vertex { - position: glam::vec3(position.x, position.y, z), + position: glam::vec2(position.x, position.y), tex_coords: glam::vec2(tex_coords.x, tex_coords.y), } }; @@ -70,7 +69,7 @@ impl Quad { let position = pos.left_bottom(); let tex_coords = tex_coords.left_bottom(); Vertex { - position: glam::vec3(position.x, position.y, z), + position: glam::vec2(position.x, position.y), tex_coords: glam::vec2(tex_coords.x, tex_coords.y), } }; diff --git a/crates/graphics/src/data/vertex.rs b/crates/graphics/src/data/vertex.rs index efaf09c9..7c99b8f7 100644 --- a/crates/graphics/src/data/vertex.rs +++ b/crates/graphics/src/data/vertex.rs @@ -18,13 +18,13 @@ #[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable, PartialEq)] pub struct Vertex { - pub position: glam::Vec3, + pub position: glam::Vec2, pub tex_coords: glam::Vec2, } impl Vertex { const ATTRIBS: [wgpu::VertexAttribute; 2] = - wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x2]; + wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2]; pub const fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { wgpu::VertexBufferLayout { array_stride: std::mem::size_of::() as wgpu::BufferAddress, diff --git a/crates/graphics/src/event.rs b/crates/graphics/src/event.rs index da743432..87eb8628 100644 --- a/crates/graphics/src/event.rs +++ b/crates/graphics/src/event.rs @@ -151,7 +151,7 @@ impl Event { ), egui::vec2(cw - 0.02, ch - 0.02), ); - let quad = Quad::new(pos, tex_coords, 0.0); + let quad = Quad::new(pos, tex_coords); let viewport = Arc::new(Viewport::new(graphics_state, cw, ch)); diff --git a/crates/graphics/src/plane.rs b/crates/graphics/src/plane.rs index 04d019df..660ab69b 100644 --- a/crates/graphics/src/plane.rs +++ b/crates/graphics/src/plane.rs @@ -48,7 +48,6 @@ impl Plane { let quad = Quad::new( egui::Rect::from_min_size(egui::pos2(0.0, 0.0), egui::vec2(map_width, map_height)), tex_coords, - 0.0, ); let sprite = Sprite::new( diff --git a/crates/graphics/src/primitives/collision/instance.rs b/crates/graphics/src/primitives/collision/instance.rs index 48b4babe..50678bb3 100644 --- a/crates/graphics/src/primitives/collision/instance.rs +++ b/crates/graphics/src/primitives/collision/instance.rs @@ -31,7 +31,7 @@ pub struct Instances { #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] struct Instance { - position: [f32; 3], + position: [f32; 2], passage: u32, } @@ -81,7 +81,7 @@ impl Instances { &self.instance_buffer, offset as wgpu::BufferAddress, bytemuck::bytes_of(&Instance { - position: [position.0 as f32, position.1 as f32, 0.0], + position: [position.0 as f32, position.1 as f32], passage: passage as u32, }), ) @@ -99,11 +99,7 @@ impl Instances { let map_y = (index / passages.xsize()) % passages.ysize(); Instance { - position: [ - map_x as f32, - map_y as f32, - 0., // We don't do a depth buffer. z doesn't matter - ], + position: [map_x as f32, map_y as f32], passage: passage as u32, } }) @@ -112,11 +108,11 @@ impl Instances { fn calculate_vertices() -> [Vertex; 12] { let rect = egui::Rect::from_min_size(egui::pos2(0., 0.), egui::vec2(32., 32.)); - let center = glam::vec3(rect.center().x, rect.center().y, 0.); - let top_left = glam::vec3(rect.left_top().x, rect.left_top().y, 0.); - let top_right = glam::vec3(rect.right_top().x, rect.right_top().y, 0.); - let bottom_left = glam::vec3(rect.left_bottom().x, rect.left_bottom().y, 0.); - let bottom_right = glam::vec3(rect.right_bottom().x, rect.right_bottom().y, 0.); + let center = glam::vec2(rect.center().x, rect.center().y); + let top_left = glam::vec2(rect.left_top().x, rect.left_top().y); + let top_right = glam::vec2(rect.right_top().x, rect.right_top().y); + let bottom_left = glam::vec2(rect.left_bottom().x, rect.left_bottom().y); + let bottom_right = glam::vec2(rect.right_bottom().x, rect.right_bottom().y); [ Vertex { diff --git a/crates/graphics/src/primitives/collision/shader.rs b/crates/graphics/src/primitives/collision/shader.rs index eeb70557..51761582 100644 --- a/crates/graphics/src/primitives/collision/shader.rs +++ b/crates/graphics/src/primitives/collision/shader.rs @@ -24,7 +24,7 @@ pub fn create_render_pipeline( bind_group_layouts: &crate::primitives::BindGroupLayouts, ) -> Result { let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { - source: include_str!("collision.wgsl"), + source: include_str!("../shaders/collision.wgsl"), file_path: "collision.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, shader_defs: std::collections::HashMap::new(), diff --git a/crates/graphics/src/primitives/collision/vertex.rs b/crates/graphics/src/primitives/collision/vertex.rs index f40bd2e0..0d97251a 100644 --- a/crates/graphics/src/primitives/collision/vertex.rs +++ b/crates/graphics/src/primitives/collision/vertex.rs @@ -18,14 +18,14 @@ #[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable, PartialEq)] pub struct Vertex { - pub position: glam::Vec3, + pub position: glam::Vec2, /// 1: down, 2: left, 4: right, 8: up pub direction: u32, } impl Vertex { const ATTRIBS: [wgpu::VertexAttribute; 2] = - wgpu::vertex_attr_array![0 => Float32x3, 1 => Uint32]; + wgpu::vertex_attr_array![0 => Float32x2, 1 => Uint32]; pub const fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { wgpu::VertexBufferLayout { array_stride: std::mem::size_of::() as wgpu::BufferAddress, diff --git a/crates/graphics/src/primitives/collision/collision.wgsl b/crates/graphics/src/primitives/shaders/collision.wgsl similarity index 81% rename from crates/graphics/src/primitives/collision/collision.wgsl rename to crates/graphics/src/primitives/shaders/collision.wgsl index 7abeb3e7..cb063ff6 100644 --- a/crates/graphics/src/primitives/collision/collision.wgsl +++ b/crates/graphics/src/primitives/shaders/collision.wgsl @@ -1,10 +1,10 @@ struct VertexInput { - @location(0) position: vec3, + @location(0) position: vec2, @location(1) direction: u32, } struct InstanceInput { - @location(2) tile_position: vec3, + @location(2) tile_position: vec2, @location(3) passage: u32, } @@ -28,7 +28,7 @@ fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { } let position = viewport.proj * vec4(vertex.position.xy + (instance.tile_position.xy * 32.), 0.0, 1.0); - out.clip_position = vec4(position.xy, instance.tile_position.z, 1.0); + out.clip_position = vec4(position.xy, 0.0, 1.0); return out; } diff --git a/crates/graphics/src/primitives/shaders/gamma.wgsl b/crates/graphics/src/primitives/shaders/gamma.wgsl new file mode 100644 index 00000000..217c3217 --- /dev/null +++ b/crates/graphics/src/primitives/shaders/gamma.wgsl @@ -0,0 +1,14 @@ +#define_import_path luminol::gamma + +// 0-1 sRGB gamma from 0-1 linear +fn from_linear_rgb(rgb: vec3) -> vec3 { + let cutoff = rgb < vec3(0.0031308); + let lower = rgb * vec3(12.92); + let higher = vec3(1.055) * pow(rgb, vec3(1.0 / 2.4)) - vec3(0.055); + return select(higher, lower, cutoff); +} + +// 0-1 sRGBA gamma from 0-1 linear +fn from_linear_rgba(linear_rgba: vec4) -> vec4 { + return vec4(from_linear_rgb(linear_rgba.rgb), linear_rgba.a); +} \ No newline at end of file diff --git a/crates/graphics/src/primitives/shaders/hue.wgsl b/crates/graphics/src/primitives/shaders/hue.wgsl new file mode 100644 index 00000000..d45ccc3c --- /dev/null +++ b/crates/graphics/src/primitives/shaders/hue.wgsl @@ -0,0 +1,23 @@ +#define_import_path luminol::hue + +fn rgb_to_hsv(c: vec3) -> vec3 { + let K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + let p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + let q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + let d = q.x - min(q.w, q.y); + + // Avoid divide - by - zero situations by adding a very tiny delta. + // Since we always deal with underlying 8 - Bit color values, this + // should never mask a real value + let eps = 1.0e-10; + + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + eps)), d / (q.x + eps), q.x); +} + +fn hsv_to_rgb(c: vec3) -> vec3 { + let K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + let p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + + return c.z * mix(K.xxx, clamp(p - K.xxx, vec3(0.0), vec3(1.0)), c.y); +} \ No newline at end of file diff --git a/crates/graphics/src/primitives/shaders/sprite.wgsl b/crates/graphics/src/primitives/shaders/sprite.wgsl new file mode 100644 index 00000000..d09088ef --- /dev/null +++ b/crates/graphics/src/primitives/shaders/sprite.wgsl @@ -0,0 +1,66 @@ +#import luminol::gamma as Gamma +#import luminol::hue as Hue + +// Vertex shader +struct VertexInput { + @location(0) position: vec2, + @location(1) tex_coords: vec2, +} + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) tex_coords: vec2, +} + +struct Viewport { + proj: mat4x4, +} + +struct Graphic { + hue: f32, + opacity: f32, + opacity_multiplier: f32, + _padding: u32, +} + +@group(0) @binding(0) +var t_diffuse: texture_2d; +@group(0) @binding(1) +var s_diffuse: sampler; + +@group(0) @binding(2) +var viewport: Viewport; +@group(0) @binding(3) +var graphic: Graphic; + +@vertex +fn vs_main( + model: VertexInput, +) -> VertexOutput { + var out: VertexOutput; + out.tex_coords = model.tex_coords; + + var position = viewport.proj * vec4(model.position, 0.0, 1.0); + + out.clip_position = vec4(position.xy, 0.0, 1.0); + return out; +} + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + var tex_sample = textureSample(t_diffuse, s_diffuse, in.tex_coords); + + tex_sample.a *= graphic.opacity * graphic.opacity_multiplier; + if tex_sample.a <= 0. { + discard; + } + + if graphic.hue > 0.0 { + var hsv = Hue::rgb_to_hsv(tex_sample.rgb); + + hsv.x += graphic.hue; + tex_sample = vec4(Hue::hsv_to_rgb(hsv), tex_sample.a); + } + + return Gamma::from_linear_rgba(tex_sample); +} diff --git a/crates/graphics/src/primitives/tiles/tilemap.wgsl b/crates/graphics/src/primitives/shaders/tilemap.wgsl similarity index 76% rename from crates/graphics/src/primitives/tiles/tilemap.wgsl rename to crates/graphics/src/primitives/shaders/tilemap.wgsl index 1bcd8e4d..6553c611 100644 --- a/crates/graphics/src/primitives/tiles/tilemap.wgsl +++ b/crates/graphics/src/primitives/shaders/tilemap.wgsl @@ -1,18 +1,18 @@ +#import luminol::gamma as Gamma + struct VertexInput { - @location(0) position: vec3, + @location(0) position: vec2, @location(1) tex_coords: vec2, } struct InstanceInput { - @location(2) tile_position: vec3, - @location(3) tile_id: u32, - @location(4) layer: u32, + @location(2) tile_id: u32, + @builtin(instance_index) index: u32 } struct VertexOutput { @builtin(position) clip_position: vec4, @location(0) tex_coords: vec2, - @location(1) @interpolate(flat) layer: u32, // todo: look into using multiple textures? } @@ -31,24 +31,34 @@ var atlas: texture_2d; @group(0) @binding(1) var atlas_sampler: sampler; +struct Display { + opacity: f32, + map_size: vec2, +} + @group(0) @binding(2) var viewport: Viewport; @group(0) @binding(3) var autotiles: Autotiles; @group(0) @binding(4) -var opacity: array, 1>; +var display: Display; @vertex fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { var out: VertexOutput; - out.layer = instance.layer; if instance.tile_id < #AUTOTILE_ID_AMOUNT { return out; } - let position = viewport.proj * vec4(vertex.position.xy + (instance.tile_position.xy * f32(#TILE_SIZE)), 0.0, 1.0); - out.clip_position = vec4(position.xy, instance.tile_position.z, 1.0); + let layer_instance_index = instance.index % (display.map_size.x * display.map_size.y); + let tile_position = vec2( + f32(layer_instance_index % display.map_size.x), + f32(layer_instance_index / display.map_size.x) + ); + + let position = viewport.proj * vec4(vertex.position.xy + (tile_position * 32.), 0.0, 1.0); + out.clip_position = vec4(position.xy, 0.0, 1.0); // we don't set the z because we have no z buffer let is_autotile = instance.tile_id < #TOTAL_AUTOTILE_ID_AMOUNT; @@ -93,29 +103,15 @@ fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { return out; } -// 0-1 sRGB gamma from 0-1 linear -fn gamma_from_linear_rgb(rgb: vec3) -> vec3 { - let cutoff = rgb < vec3(0.0031308); - let lower = rgb * vec3(12.92); - let higher = vec3(1.055) * pow(rgb, vec3(1.0 / 2.4)) - vec3(0.055); - return select(higher, lower, cutoff); -} - -// 0-1 sRGBA gamma from 0-1 linear -fn gamma_from_linear_rgba(linear_rgba: vec4) -> vec4 { - return vec4(gamma_from_linear_rgb(linear_rgba.rgb), linear_rgba.a); -} - @fragment fn fs_main(input: VertexOutput) -> @location(0) vec4 { var color = textureSample(atlas, atlas_sampler, input.tex_coords); - let layer_opacity = opacity[input.layer / 4u][input.layer % 4u]; - color.a *= layer_opacity; + color.a *= display.opacity; if color.a <= 0.0 { discard; } - return gamma_from_linear_rgba(color); + return Gamma::from_linear_rgba(color); } diff --git a/crates/graphics/src/primitives/sprite/shader.rs b/crates/graphics/src/primitives/sprite/shader.rs index f921b8ac..51498f6a 100644 --- a/crates/graphics/src/primitives/sprite/shader.rs +++ b/crates/graphics/src/primitives/sprite/shader.rs @@ -27,8 +27,20 @@ fn create_shader( bind_group_layouts: &BindGroupLayouts, target: wgpu::BlendState, ) -> Result { + composer.add_composable_module(naga_oil::compose::ComposableModuleDescriptor { + source: include_str!("../shaders/hue.wgsl"), + file_path: "hue.wgsl", + ..Default::default() + })?; + + composer.add_composable_module(naga_oil::compose::ComposableModuleDescriptor { + source: include_str!("../shaders/gamma.wgsl"), + file_path: "gamma.wgsl", + ..Default::default() + })?; + let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { - source: include_str!("sprite.wgsl"), + source: include_str!("../shaders/sprite.wgsl"), file_path: "sprite.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, shader_defs: HashMap::new(), diff --git a/crates/graphics/src/primitives/sprite/sprite.wgsl b/crates/graphics/src/primitives/sprite/sprite.wgsl deleted file mode 100644 index aabae7b5..00000000 --- a/crates/graphics/src/primitives/sprite/sprite.wgsl +++ /dev/null @@ -1,98 +0,0 @@ -// Vertex shader -struct VertexInput { - @location(0) position: vec3, - @location(1) tex_coords: vec2, -} - -struct VertexOutput { - @builtin(position) clip_position: vec4, - @location(0) tex_coords: vec2, -} - -struct Viewport { - proj: mat4x4, -} - -struct Graphic { - hue: f32, - opacity: f32, - opacity_multiplier: f32, - _padding: u32, -} - -@group(0) @binding(0) -var t_diffuse: texture_2d; -@group(0) @binding(1) -var s_diffuse: sampler; - -@group(0) @binding(2) -var viewport: Viewport; -@group(0) @binding(3) -var graphic: Graphic; - -fn rgb_to_hsv(c: vec3) -> vec3 { - let K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - let p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - let q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - let d = q.x - min(q.w, q.y); - - // Avoid divide - by - zero situations by adding a very tiny delta. - // Since we always deal with underlying 8 - Bit color values, this - // should never mask a real value - let eps = 1.0e-10; - - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + eps)), d / (q.x + eps), q.x); -} - -fn hsv_to_rgb(c: vec3) -> vec3 { - let K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - let p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - - return c.z * mix(K.xxx, clamp(p - K.xxx, vec3(0.0), vec3(1.0)), c.y); -} - -@vertex -fn vs_main( - model: VertexInput, -) -> VertexOutput { - var out: VertexOutput; - out.tex_coords = model.tex_coords; - - var position = viewport.proj * vec4(model.position.xy, 0.0, 1.0); - - out.clip_position = vec4(position.xy, model.position.z, 1.0); - return out; -} - -// 0-1 sRGB gamma from 0-1 linear -fn gamma_from_linear_rgb(rgb: vec3) -> vec3 { - let cutoff = rgb < vec3(0.0031308); - let lower = rgb * vec3(12.92); - let higher = vec3(1.055) * pow(rgb, vec3(1.0 / 2.4)) - vec3(0.055); - return select(higher, lower, cutoff); -} - -// 0-1 sRGBA gamma from 0-1 linear -fn gamma_from_linear_rgba(linear_rgba: vec4) -> vec4 { - return vec4(gamma_from_linear_rgb(linear_rgba.rgb), linear_rgba.a); -} - -@fragment -fn fs_main(in: VertexOutput) -> @location(0) vec4 { - var tex_sample = textureSample(t_diffuse, s_diffuse, in.tex_coords); - - tex_sample.a *= graphic.opacity * graphic.opacity_multiplier; - if tex_sample.a <= 0. { - discard; - } - - if graphic.hue > 0.0 { - var hsv = rgb_to_hsv(tex_sample.rgb); - - hsv.x += graphic.hue; - tex_sample = vec4(hsv_to_rgb(hsv), tex_sample.a); - } - - return gamma_from_linear_rgba(tex_sample); -} diff --git a/crates/graphics/src/primitives/tiles/atlas.rs b/crates/graphics/src/primitives/tiles/atlas.rs index 06544149..0118e949 100644 --- a/crates/graphics/src/primitives/tiles/atlas.rs +++ b/crates/graphics/src/primitives/tiles/atlas.rs @@ -381,7 +381,6 @@ impl Atlas { atlas_tile_position + egui::vec2(0.01, 0.01), egui::vec2(TILE_SIZE as f32 - 0.02, TILE_SIZE as f32 - 0.02), ), - 0.0, ) } } diff --git a/crates/graphics/src/primitives/tiles/display.rs b/crates/graphics/src/primitives/tiles/display.rs new file mode 100644 index 00000000..a846f90f --- /dev/null +++ b/crates/graphics/src/primitives/tiles/display.rs @@ -0,0 +1,173 @@ +// Copyright (C) 2024 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . +// +// Additional permission under GNU GPL version 3 section 7 +// +// If you modify this Program, or any covered work, by linking or combining +// it with Steamworks API by Valve Corporation, containing parts covered by +// terms of the Steamworks API by Valve Corporation, the licensors of this +// Program grant you additional permission to convey the resulting work. + +use parking_lot::RwLock; +use wgpu::util::DeviceExt; + +use crate::{BindGroupLayoutBuilder, GraphicsState}; + +#[derive(Debug)] +pub struct Display { + data: RwLock, + uniform: wgpu::Buffer, +} + +#[derive(Debug)] +struct LayerData { + data: Vec, + min_alignment_size: u32, +} + +#[repr(C, align(16))] +#[derive(Copy, Clone, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] +pub struct Data { + opacity: f32, + _pad: [u8; 4], + map_size: [u32; 2], +} + +impl Data { + fn aligned_size_of(min_alignment_size: u32) -> usize { + wgpu::util::align_to(std::mem::size_of::(), min_alignment_size as usize) + } +} + +impl LayerData { + fn range_of_layer(&self, layer: usize) -> std::ops::Range { + let data_size = Data::aligned_size_of(self.min_alignment_size); + let start = layer * data_size; + let end = start + std::mem::size_of::(); + start..end + } + + fn bytes_of_layer(&self, layer: usize) -> &[u8] { + let range = self.range_of_layer(layer); + &self.data[range] + } + + fn bytes_of_layer_mut(&mut self, layer: usize) -> &mut [u8] { + let range = self.range_of_layer(layer); + &mut self.data[range] + } + + fn read_data_at(&self, layer: usize) -> &Data { + bytemuck::from_bytes(self.bytes_of_layer(layer)) + } + + fn read_data_at_mut(&mut self, layer: usize) -> &mut Data { + bytemuck::from_bytes_mut(self.bytes_of_layer_mut(layer)) + } +} + +impl Display { + pub fn new( + graphics_state: &GraphicsState, + map_width: u32, + map_height: u32, + layers: usize, + ) -> Self { + let limits = graphics_state.render_state.device.limits(); + let min_alignment_size = limits.min_uniform_buffer_offset_alignment; + + let data_size = Data::aligned_size_of(min_alignment_size); + let mut layer_data = LayerData { + data: vec![0; data_size * layers], + min_alignment_size, + }; + + for layer in 0..layers { + *layer_data.read_data_at_mut(layer) = Data { + opacity: 1.0, + _pad: [0; 4], + map_size: [map_width, map_height], + }; + } + + let uniform = graphics_state.render_state.device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("tilemap display buffer"), + contents: bytemuck::cast_slice(&layer_data.data), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, + }, + ); + + Self { + data: RwLock::new(layer_data), + uniform, + } + } + + pub fn as_buffer(&self) -> &wgpu::Buffer { + &self.uniform + } + + pub fn bytes_of_layer(&self, layer: usize) -> impl std::ops::Deref + '_ { + parking_lot::RwLockReadGuard::map(self.data.read(), |d| d.bytes_of_layer(layer)) + } + + pub fn opacity(&self, layer: usize) -> f32 { + self.data.read().read_data_at(layer).opacity + } + + pub fn set_opacity( + &self, + render_state: &luminol_egui_wgpu::RenderState, + opacity: f32, + layer: usize, + ) { + let mut data = self.data.write(); + let layer_data = data.read_data_at_mut(layer); + if layer_data.opacity != opacity { + layer_data.opacity = opacity; + self.regen_buffer(render_state, &data.data); + } + } + + pub fn aligned_layer_size(&self) -> usize { + let data = self.data.read(); + Data::aligned_size_of(data.min_alignment_size) + } + + pub fn layer_offset(&self, layer: usize) -> u32 { + self.data.read().range_of_layer(layer).start as u32 + } + + fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState, data: &[u8]) { + render_state.queue.write_buffer(self.as_buffer(), 0, data); + } +} + +pub fn add_to_bind_group_layout( + layout_builder: &mut BindGroupLayoutBuilder, +) -> &mut BindGroupLayoutBuilder { + layout_builder.append( + wgpu::ShaderStages::VERTEX_FRAGMENT, + wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: None, + }, + None, + ) +} diff --git a/crates/graphics/src/primitives/tiles/instance.rs b/crates/graphics/src/primitives/tiles/instance.rs index 1f7ef0b8..be8b5be5 100644 --- a/crates/graphics/src/primitives/tiles/instance.rs +++ b/crates/graphics/src/primitives/tiles/instance.rs @@ -32,16 +32,13 @@ pub struct Instances { #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] struct Instance { - position: [f32; 3], tile_id: u32, // force this to be an u32 to avoid padding issues - layer: u32, } const TILE_QUAD: Quad = Quad::new( egui::Rect::from_min_max(egui::pos2(0., 0.), egui::pos2(32., 32.0)), // slightly smaller than 32x32 to reduce bleeding from adjacent pixels in the atlas egui::Rect::from_min_max(egui::pos2(0.01, 0.01), egui::pos2(31.99, 31.99)), - 0.0, ); impl Instances { @@ -86,9 +83,7 @@ impl Instances { &self.instance_buffer, offset as wgpu::BufferAddress, bytemuck::bytes_of(&Instance { - position: [position.0 as f32, position.1 as f32, 0.0], tile_id: tile_id as u32, - layer: position.2 as u32, }), ) } @@ -97,27 +92,11 @@ impl Instances { map_data .iter() .copied() - .enumerate() // Previously we'd filter out tiles that would not display (anything < 48). // However, storing the entire map like this makes it easier to edit tiles without remaking the entire buffer. // It's a memory tradeoff for a lot of performance. - .map(|(index, tile_id)| { - // We reset the x every xsize elements. - let map_x = index % map_data.xsize(); - // We reset the y every ysize elements, but only increment it every xsize elements. - let map_y = (index / map_data.xsize()) % map_data.ysize(); - // We increment the z every xsize * ysize elements. - let map_z = index / (map_data.xsize() * map_data.ysize()); - - Instance { - position: [ - map_x as f32, - map_y as f32, - 0., // We don't do a depth buffer. z doesn't matter - ], - tile_id: tile_id as u32, - layer: map_z as u32, - } + .map(|tile_id| Instance { + tile_id: tile_id as u32, }) .collect_vec() } @@ -140,8 +119,7 @@ impl Instances { } pub const fn desc() -> wgpu::VertexBufferLayout<'static> { - const ARRAY: &[wgpu::VertexAttribute] = - &wgpu::vertex_attr_array![2 => Float32x3, 3 => Uint32, 4 => Uint32]; + const ARRAY: &[wgpu::VertexAttribute] = &wgpu::vertex_attr_array![2 => Uint32]; wgpu::VertexBufferLayout { array_stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Instance, diff --git a/crates/graphics/src/primitives/tiles/mod.rs b/crates/graphics/src/primitives/tiles/mod.rs index cae20349..e7563234 100644 --- a/crates/graphics/src/primitives/tiles/mod.rs +++ b/crates/graphics/src/primitives/tiles/mod.rs @@ -22,21 +22,21 @@ use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Viewport}; pub use atlas::Atlas; use autotiles::Autotiles; +use display::Display; use instance::Instances; -use opacity::Opacity; mod atlas; mod autotile_ids; pub(crate) mod autotiles; +pub(crate) mod display; mod instance; -pub(crate) mod opacity; pub(crate) mod shader; pub struct Tiles { pub autotiles: Autotiles, pub atlas: Atlas, pub instances: Instances, - pub opacity: Opacity, + pub display: Display, pub viewport: Arc, pub bind_group: wgpu::BindGroup, @@ -55,7 +55,12 @@ impl Tiles { tiles, atlas.atlas_texture.size(), ); - let opacity = Opacity::new(graphics_state); + let display = Display::new( + graphics_state, + tiles.xsize() as u32, + tiles.ysize() as u32, + tiles.zsize(), + ); let mut bind_group_builder = BindGroupBuilder::new(); bind_group_builder @@ -65,7 +70,7 @@ impl Tiles { bind_group_builder .append_buffer(viewport.as_buffer()) .append_buffer(autotiles.as_buffer()) - .append_buffer(opacity.as_buffer()); + .append_buffer_with_size(display.as_buffer(), display.aligned_layer_size() as u64); let bind_group = bind_group_builder.build( &graphics_state.render_state.device, @@ -77,7 +82,7 @@ impl Tiles { autotiles, atlas, instances, - opacity, + display, bind_group, viewport, @@ -109,7 +114,6 @@ impl Tiles { render_pass.push_debug_group("tilemap tiles renderer"); render_pass.set_pipeline(&graphics_state.pipelines.tiles); - render_pass.set_bind_group(0, &self.bind_group, &[]); for (layer, enabled) in enabled_layers.iter().copied().enumerate() { let opacity = if selected_layer.is_some_and(|s| s != layer) { @@ -118,8 +122,14 @@ impl Tiles { 1.0 }; if enabled { - self.opacity - .set_opacity(&graphics_state.render_state, layer, opacity); + self.display + .set_opacity(&graphics_state.render_state, opacity, layer); + + render_pass.set_bind_group( + 0, + &self.bind_group, + &[self.display.layer_offset(layer)], + ); self.instances.draw(render_pass, layer); } @@ -150,7 +160,7 @@ pub fn create_bind_group_layout( Viewport::add_to_bind_group_layout(&mut builder); autotiles::add_to_bind_group_layout(&mut builder); - opacity::add_to_bind_group_layout(&mut builder); + display::add_to_bind_group_layout(&mut builder); builder.build(&render_state.device, Some("tilemap bind group layout")) } diff --git a/crates/graphics/src/primitives/tiles/opacity.rs b/crates/graphics/src/primitives/tiles/opacity.rs deleted file mode 100644 index 2f46007e..00000000 --- a/crates/graphics/src/primitives/tiles/opacity.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 2023 Lily Lyons -// -// This file is part of Luminol. -// -// Luminol is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Luminol is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Luminol. If not, see . - -use crossbeam::atomic::AtomicCell; -use wgpu::util::DeviceExt; - -use crate::{BindGroupLayoutBuilder, GraphicsState}; - -#[derive(Debug)] -pub struct Opacity { - data: AtomicCell<[f32; 4]>, // length has to be a multiple of 4 - uniform: wgpu::Buffer, -} - -impl Opacity { - pub fn new(graphics_state: &GraphicsState) -> Self { - let opacity = [1.; 4]; - - let uniform = graphics_state.render_state.device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("tilemap opacity buffer"), - contents: bytemuck::bytes_of(&opacity), - usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, - }, - ); - - Self { - data: AtomicCell::new(opacity), - uniform, - } - } - - pub fn opacity(&self, layer: usize) -> f32 { - self.data.load()[layer] - } - - pub fn as_buffer(&self) -> &wgpu::Buffer { - &self.uniform - } - - pub fn set_opacity( - &self, - render_state: &luminol_egui_wgpu::RenderState, - layer: usize, - opacity: f32, - ) { - let mut data = self.data.load(); - if data[layer] != opacity { - data[layer] = opacity; - self.data.store(data); - self.regen_buffer(render_state); - } - } - - fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { - render_state - .queue - .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data.load())); - } -} - -pub fn add_to_bind_group_layout( - layout_builder: &mut BindGroupLayoutBuilder, -) -> &mut BindGroupLayoutBuilder { - layout_builder.append( - wgpu::ShaderStages::VERTEX_FRAGMENT, - wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - None, - ) -} diff --git a/crates/graphics/src/primitives/tiles/shader.rs b/crates/graphics/src/primitives/tiles/shader.rs index 2a178ae3..cbb883bb 100644 --- a/crates/graphics/src/primitives/tiles/shader.rs +++ b/crates/graphics/src/primitives/tiles/shader.rs @@ -23,8 +23,14 @@ pub fn create_render_pipeline( render_state: &luminol_egui_wgpu::RenderState, bind_group_layouts: &BindGroupLayouts, ) -> Result { + composer.add_composable_module(naga_oil::compose::ComposableModuleDescriptor { + source: include_str!("../shaders/gamma.wgsl"), + file_path: "gamma.wgsl", + ..Default::default() + })?; + let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { - source: include_str!("tilemap.wgsl"), + source: include_str!("../shaders/tilemap.wgsl"), file_path: "tilemap.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, shader_defs: std::collections::HashMap::from([ From 343f15d2dde9e9428e42f93e2ea0f847d37f55cf Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Mon, 10 Jun 2024 22:11:36 -0700 Subject: [PATCH 05/24] Move grid.wgsl --- crates/graphics/src/primitives/grid/shader.rs | 2 +- crates/graphics/src/primitives/{grid => shaders}/grid.wgsl | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename crates/graphics/src/primitives/{grid => shaders}/grid.wgsl (100%) diff --git a/crates/graphics/src/primitives/grid/shader.rs b/crates/graphics/src/primitives/grid/shader.rs index c09adce7..8d629582 100644 --- a/crates/graphics/src/primitives/grid/shader.rs +++ b/crates/graphics/src/primitives/grid/shader.rs @@ -24,7 +24,7 @@ pub fn create_render_pipeline( bind_group_layouts: &crate::primitives::BindGroupLayouts, ) -> Result { let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { - source: include_str!("grid.wgsl"), + source: include_str!("../shaders/grid.wgsl"), file_path: "grid.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, shader_defs: std::collections::HashMap::default(), diff --git a/crates/graphics/src/primitives/grid/grid.wgsl b/crates/graphics/src/primitives/shaders/grid.wgsl similarity index 100% rename from crates/graphics/src/primitives/grid/grid.wgsl rename to crates/graphics/src/primitives/shaders/grid.wgsl From 0e7c547bd4513ab97a42d1b4f6c4c4989f19ac38 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Mon, 10 Jun 2024 22:20:59 -0700 Subject: [PATCH 06/24] Don't use an instance buffer for the grid --- crates/components/src/tilepicker.rs | 4 +- crates/graphics/src/map.rs | 4 +- .../graphics/src/primitives/grid/display.rs | 8 ++- .../graphics/src/primitives/grid/instance.rs | 62 ++----------------- crates/graphics/src/primitives/grid/mod.rs | 6 +- crates/graphics/src/primitives/grid/shader.rs | 3 +- .../graphics/src/primitives/shaders/grid.wgsl | 15 +++-- 7 files changed, 28 insertions(+), 74 deletions(-) diff --git a/crates/components/src/tilepicker.rs b/crates/components/src/tilepicker.rs index 675e9dba..a50a25cb 100644 --- a/crates/components/src/tilepicker.rs +++ b/crates/components/src/tilepicker.rs @@ -153,8 +153,8 @@ impl Tilepicker { let grid = luminol_graphics::Grid::new( &update_state.graphics, viewport.clone(), - tilepicker_data.xsize(), - tilepicker_data.ysize(), + tilepicker_data.xsize() as u32, + tilepicker_data.ysize() as u32, ); let mut passages = diff --git a/crates/graphics/src/map.rs b/crates/graphics/src/map.rs index 4bb2a0c9..5b8d4f62 100644 --- a/crates/graphics/src/map.rs +++ b/crates/graphics/src/map.rs @@ -140,8 +140,8 @@ impl Map { let grid = Grid::new( graphics_state, viewport.clone(), - map.data.xsize(), - map.data.ysize(), + map.data.xsize() as u32, + map.data.ysize() as u32, ); let collision = Collision::new(graphics_state, viewport.clone(), passages); diff --git a/crates/graphics/src/primitives/grid/display.rs b/crates/graphics/src/primitives/grid/display.rs index 8cb72fb3..18af0ffe 100644 --- a/crates/graphics/src/primitives/grid/display.rs +++ b/crates/graphics/src/primitives/grid/display.rs @@ -32,14 +32,18 @@ pub struct Data { viewport_size_in_pixels: [f32; 2], pixels_per_point: f32, inner_thickness_in_points: f32, + map_size: [u32; 2], + _pad: [u8; 8], } impl Display { - pub fn new(graphics_state: &GraphicsState) -> Self { + pub fn new(graphics_state: &GraphicsState, map_width: u32, map_height: u32) -> Self { let display = Data { viewport_size_in_pixels: [0., 0.], pixels_per_point: 1., inner_thickness_in_points: 1., + map_size: [map_width, map_height], + _pad: [0; 8], }; let uniform = graphics_state.render_state.device.create_buffer_init( @@ -114,7 +118,7 @@ pub fn add_to_bind_group_layout( layout_builder: &mut BindGroupLayoutBuilder, ) -> &mut BindGroupLayoutBuilder { layout_builder.append( - wgpu::ShaderStages::FRAGMENT, + wgpu::ShaderStages::VERTEX_FRAGMENT, wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, diff --git a/crates/graphics/src/primitives/grid/instance.rs b/crates/graphics/src/primitives/grid/instance.rs index 8b401f29..1ab79247 100644 --- a/crates/graphics/src/primitives/grid/instance.rs +++ b/crates/graphics/src/primitives/grid/instance.rs @@ -16,40 +16,20 @@ // along with Luminol. If not, see . use super::Vertex; -use itertools::Itertools; use wgpu::util::DeviceExt; #[derive(Debug)] pub struct Instances { - instance_buffer: wgpu::Buffer, vertex_buffer: wgpu::Buffer, - - map_width: usize, - map_height: usize, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] -pub struct Instance { - position: [f32; 2], + map_size: u32, } impl Instances { pub fn new( render_state: &luminol_egui_wgpu::RenderState, - map_width: usize, - map_height: usize, + map_width: u32, + map_height: u32, ) -> Self { - let instances = Self::calculate_instances(map_width, map_height); - let instance_buffer = - render_state - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("tilemap grid instance buffer"), - contents: bytemuck::cast_slice(&instances), - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - }); - let vertices = Self::calculate_vertices(render_state); let vertex_buffer = render_state @@ -61,23 +41,11 @@ impl Instances { }); Self { - instance_buffer, vertex_buffer, - - map_width, - map_height, + map_size: map_width * map_height, } } - fn calculate_instances(map_width: usize, map_height: usize) -> Vec { - (0..map_height) - .cartesian_product(0..map_width) - .map(|(map_y, map_x)| Instance { - position: [map_x as f32, map_y as f32], - }) - .collect_vec() - } - fn calculate_vertices(render_state: &luminol_egui_wgpu::RenderState) -> [Vertex; 6] { // OpenGL and WebGL use the last vertex in each triangle as the provoking vertex, and // Direct3D, Metal, Vulkan and WebGPU use the first vertex in each triangle @@ -129,26 +97,6 @@ impl Instances { pub fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>) { render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); - // Calculate the start and end index of the buffer, as well as the amount of instances. - let start_index = 0; - let end_index = self.map_width * self.map_height; - let count = (end_index - start_index) as u32; - - // Convert the indexes into actual offsets. - let start = (start_index * std::mem::size_of::()) as wgpu::BufferAddress; - let end = (end_index * std::mem::size_of::()) as wgpu::BufferAddress; - - render_pass.set_vertex_buffer(1, self.instance_buffer.slice(start..end)); - - render_pass.draw(0..6, 0..count); - } - - pub const fn desc() -> wgpu::VertexBufferLayout<'static> { - const ARRAY: &[wgpu::VertexAttribute] = &wgpu::vertex_attr_array![1 => Float32x2]; - wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Instance, - attributes: ARRAY, - } + render_pass.draw(0..6, 0..self.map_size); } } diff --git a/crates/graphics/src/primitives/grid/mod.rs b/crates/graphics/src/primitives/grid/mod.rs index bcf8d3e6..eabb63a6 100644 --- a/crates/graphics/src/primitives/grid/mod.rs +++ b/crates/graphics/src/primitives/grid/mod.rs @@ -41,11 +41,11 @@ impl Grid { pub fn new( graphics_state: &GraphicsState, viewport: Arc, - map_width: usize, - map_height: usize, + map_width: u32, + map_height: u32, ) -> Self { let instances = Instances::new(&graphics_state.render_state, map_width, map_height); - let display = Display::new(graphics_state); + let display = Display::new(graphics_state, map_width, map_height); let mut bind_group_builder = BindGroupBuilder::new(); bind_group_builder.append_buffer(viewport.as_buffer()); diff --git a/crates/graphics/src/primitives/grid/shader.rs b/crates/graphics/src/primitives/grid/shader.rs index 8d629582..6b68c0bc 100644 --- a/crates/graphics/src/primitives/grid/shader.rs +++ b/crates/graphics/src/primitives/grid/shader.rs @@ -15,7 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use super::instance::Instances; use super::Vertex; pub fn create_render_pipeline( @@ -55,7 +54,7 @@ pub fn create_render_pipeline( vertex: wgpu::VertexState { module: &shader_module, entry_point: "vs_main", - buffers: &[Vertex::desc(), Instances::desc()], + buffers: &[Vertex::desc()], }, fragment: Some(wgpu::FragmentState { module: &shader_module, diff --git a/crates/graphics/src/primitives/shaders/grid.wgsl b/crates/graphics/src/primitives/shaders/grid.wgsl index c8bd220c..72bc0225 100644 --- a/crates/graphics/src/primitives/shaders/grid.wgsl +++ b/crates/graphics/src/primitives/shaders/grid.wgsl @@ -2,10 +2,6 @@ struct VertexInput { @location(0) position: vec2, } -struct InstanceInput { - @location(1) tile_position: vec2, -} - struct VertexOutput { @builtin(position) clip_position: vec4, @location(0) position: vec2, @@ -22,6 +18,8 @@ struct Display { viewport_size_in_pixels: vec2, pixels_per_point: f32, inner_thickness_in_points: f32, + // we need this size(16) because in webgl, buffers cannot have a size that is not a multiple of 16. + @size(16) map_size: vec2, } @group(0) @binding(0) @@ -30,10 +28,15 @@ var viewport: Viewport; var display: Display; @vertex -fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { +fn vs_main(vertex: VertexInput, @builtin(instance_index) instance_index: u32) -> VertexOutput { var out: VertexOutput; - out.position = (viewport.proj * vec4((vertex.position + instance.tile_position) * 32., 0., 1.)).xy; + let tile_position = vec2( + f32(instance_index % display.map_size.x), + f32(instance_index / display.map_size.x) + ); + + out.position = (viewport.proj * vec4((vertex.position + tile_position) * 32., 0., 1.)).xy; out.vertex_position = out.position; out.clip_position = vec4(out.position, 0., 1.); return out; From 81712828b8ca4adbf047e8b1123aac6d991175d0 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Mon, 10 Jun 2024 22:46:25 -0700 Subject: [PATCH 07/24] Move the tilepicker mostly into graphics --- crates/components/src/tilepicker.rs | 151 ++-------------------- crates/graphics/src/lib.rs | 2 + crates/graphics/src/tilepicker.rs | 188 ++++++++++++++++++++++++++++ crates/ui/src/tabs/map/mod.rs | 4 +- 4 files changed, 203 insertions(+), 142 deletions(-) create mode 100644 crates/graphics/src/tilepicker.rs diff --git a/crates/components/src/tilepicker.rs b/crates/components/src/tilepicker.rs index a50a25cb..f0d3c2b7 100644 --- a/crates/components/src/tilepicker.rs +++ b/crates/components/src/tilepicker.rs @@ -15,71 +15,22 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use fragile::Fragile; -use itertools::Itertools; -use std::sync::Arc; -use std::time::Duration; - pub struct Tilepicker { pub selected_tiles_left: i16, pub selected_tiles_top: i16, pub selected_tiles_right: i16, pub selected_tiles_bottom: i16, - pub coll_enabled: bool, - pub grid_enabled: bool, + pub view: luminol_graphics::Tilepicker, drag_origin: Option, - resources: Arc, - viewport: Arc, - ani_time: Option, - /// When true, brush tile ID randomization is enabled. pub brush_random: bool, /// Seed for the PRNG used for the brush when brush tile ID randomization is enabled. brush_seed: [u8; 16], } -struct Resources { - tiles: luminol_graphics::Tiles, - collision: luminol_graphics::Collision, - grid: luminol_graphics::Grid, -} - -// wgpu types are not Send + Sync on webassembly, so we use fragile to make sure we never access any wgpu resources across thread boundaries -struct Callback { - resources: Fragile>, - graphics_state: Fragile>, - - coll_enabled: bool, - grid_enabled: bool, -} - -impl luminol_egui_wgpu::CallbackTrait for Callback { - fn paint<'a>( - &'a self, - info: egui::PaintCallbackInfo, - render_pass: &mut wgpu::RenderPass<'a>, - _callback_resources: &'a luminol_egui_wgpu::CallbackResources, - ) { - let resources = self.resources.get(); - let graphics_state = self.graphics_state.get(); - - resources - .tiles - .draw(graphics_state, &[true], None, render_pass); - - if self.coll_enabled { - resources.collision.draw(graphics_state, render_pass); - } - - if self.grid_enabled { - resources.grid.draw(graphics_state, &info, render_pass); - } - } -} - #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub enum SelectedTile { Autotile(i16), @@ -120,62 +71,12 @@ impl Tilepicker { let tilesets = update_state.data.tilesets(); let tileset = &tilesets.data[map.tileset_id]; - let atlas = update_state.graphics.atlas_loader.load_atlas( + let view = luminol_graphics::Tilepicker::new( &update_state.graphics, - update_state.filesystem, tileset, + update_state.filesystem, )?; - let tilepicker_data = (47..(384 + 47)) - .step_by(48) - .chain(384..(atlas.tileset_height as i16 / 32 * 8 + 384)) - .collect_vec(); - let tilepicker_data = luminol_data::Table3::new_data( - 8, - 1 + (atlas.tileset_height / 32) as usize, - 1, - tilepicker_data, - ); - - let viewport = Arc::new(luminol_graphics::Viewport::new( - &update_state.graphics, - 256., - atlas.tileset_height as f32 + 32., - )); - - let tiles = luminol_graphics::Tiles::new( - &update_state.graphics, - viewport.clone(), - atlas, - &tilepicker_data, - ); - - let grid = luminol_graphics::Grid::new( - &update_state.graphics, - viewport.clone(), - tilepicker_data.xsize() as u32, - tilepicker_data.ysize() as u32, - ); - - let mut passages = - luminol_data::Table2::new(tilepicker_data.xsize(), tilepicker_data.ysize()); - for x in 0..8 { - passages[(x, 0)] = { - let tile_id = tilepicker_data[(x, 0, 0)].try_into().unwrap_or_default(); - if tile_id >= tileset.passages.len() { - 0 - } else { - tileset.passages[tile_id] - } - }; - } - let length = - (passages.len().saturating_sub(8)).min(tileset.passages.len().saturating_sub(384)); - passages.as_mut_slice()[8..8 + length] - .copy_from_slice(&tileset.passages.as_slice()[384..384 + length]); - let collision = - luminol_graphics::Collision::new(&update_state.graphics, viewport.clone(), &passages); - let mut brush_seed = [0u8; 16]; brush_seed[0..8].copy_from_slice( &update_state @@ -189,19 +90,13 @@ impl Tilepicker { brush_seed[8..16].copy_from_slice(&(map_id as u64).to_le_bytes()); Ok(Self { - resources: Arc::new(Resources { - tiles, - collision, - grid, - }), - viewport, - ani_time: None, + view, + selected_tiles_left: 0, selected_tiles_top: 0, selected_tiles_right: 0, selected_tiles_bottom: 0, - coll_enabled: false, - grid_enabled: true, + drag_origin: None, brush_seed, brush_random: false, @@ -255,26 +150,10 @@ impl Tilepicker { ) -> egui::Response { self.brush_random = update_state.toolbar.brush_random != ui.input(|i| i.modifiers.alt); - let time = ui.ctx().input(|i| i.time); let graphics_state = update_state.graphics.clone(); - if let Some(ani_time) = self.ani_time { - if time - ani_time >= 16. / 60. { - self.ani_time = Some(time); - self.resources - .tiles - .autotiles - .inc_ani_index(&graphics_state.render_state); - } - } else { - self.ani_time = Some(time); - } - - ui.ctx() - .request_repaint_after(Duration::from_secs_f64(16. / 60.)); - let (canvas_rect, response) = ui.allocate_exact_size( - egui::vec2(256., self.resources.tiles.atlas.tileset_height as f32 + 32.), + egui::vec2(256., self.view.atlas().tileset_height as f32 + 32.), egui::Sense::click_and_drag(), ); @@ -284,7 +163,7 @@ impl Tilepicker { .intersect(scroll_rect.translate(canvas_rect.min.to_vec2())); let scroll_rect = absolute_scroll_rect.translate(-canvas_rect.min.to_vec2()); - self.viewport.set_proj( + self.view.set_proj( &graphics_state.render_state, glam::Mat4::orthographic_rh( scroll_rect.left(), @@ -296,16 +175,8 @@ impl Tilepicker { ), ); // FIXME: move this into graphics - ui.painter() - .add(luminol_egui_wgpu::Callback::new_paint_callback( - absolute_scroll_rect, - Callback { - resources: Fragile::new(self.resources.clone()), - graphics_state: Fragile::new(graphics_state.clone()), - coll_enabled: self.coll_enabled, - grid_enabled: self.grid_enabled, - }, - )); + self.view + .paint(graphics_state, ui.painter(), absolute_scroll_rect); let rect = egui::Rect::from_x_y_ranges( (self.selected_tiles_left * 32) as f32..=((self.selected_tiles_right + 1) * 32) as f32, @@ -330,7 +201,7 @@ impl Tilepicker { pos }; let rect = egui::Rect::from_two_pos(drag_origin, pos); - let bottom = self.resources.tiles.atlas.tileset_height as i16 / 32; + let bottom = self.view.atlas().tileset_height as i16 / 32; self.selected_tiles_left = (rect.left() as i16).clamp(0, 7); self.selected_tiles_right = (rect.right() as i16).clamp(0, 7); self.selected_tiles_top = (rect.top() as i16).clamp(0, bottom); diff --git a/crates/graphics/src/lib.rs b/crates/graphics/src/lib.rs index 2340faa6..b15f89c9 100644 --- a/crates/graphics/src/lib.rs +++ b/crates/graphics/src/lib.rs @@ -34,10 +34,12 @@ pub use data::*; pub mod event; pub mod map; pub mod plane; +pub mod tilepicker; pub use event::Event; pub use map::Map; pub use plane::Plane; +pub use tilepicker::Tilepicker; pub struct GraphicsState { pub texture_loader: loaders::texture::Loader, diff --git a/crates/graphics/src/tilepicker.rs b/crates/graphics/src/tilepicker.rs new file mode 100644 index 00000000..eeda612c --- /dev/null +++ b/crates/graphics/src/tilepicker.rs @@ -0,0 +1,188 @@ +// Copyright (C) 2024 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . +// +// Additional permission under GNU GPL version 3 section 7 +// +// If you modify this Program, or any covered work, by linking or combining +// it with Steamworks API by Valve Corporation, containing parts covered by +// terms of the Steamworks API by Valve Corporation, the licensors of this +// Program grant you additional permission to convey the resulting work. + +use std::{sync::Arc, time::Duration}; + +use fragile::Fragile; +use itertools::Itertools; + +use crate::{Atlas, Collision, GraphicsState, Grid, Tiles, Viewport}; + +pub struct Tilepicker { + pub coll_enabled: bool, + pub grid_enabled: bool, + + resources: Arc, + viewport: Arc, + ani_time: Option, +} + +struct Resources { + tiles: Tiles, + collision: Collision, + grid: Grid, +} + +struct Callback { + resources: Fragile>, + graphics_state: Fragile>, + + coll_enabled: bool, + grid_enabled: bool, +} + +impl luminol_egui_wgpu::CallbackTrait for Callback { + fn paint<'a>( + &'a self, + info: egui::PaintCallbackInfo, + render_pass: &mut wgpu::RenderPass<'a>, + _callback_resources: &'a luminol_egui_wgpu::CallbackResources, + ) { + let resources = self.resources.get(); + let graphics_state = self.graphics_state.get(); + + resources + .tiles + .draw(graphics_state, &[true], None, render_pass); + + if self.coll_enabled { + resources.collision.draw(graphics_state, render_pass); + } + + if self.grid_enabled { + resources.grid.draw(graphics_state, &info, render_pass); + } + } +} + +impl Tilepicker { + pub fn new( + graphics_state: &GraphicsState, + tileset: &luminol_data::rpg::Tileset, + filesystem: &impl luminol_filesystem::FileSystem, + ) -> color_eyre::Result { + let atlas = graphics_state + .atlas_loader + .load_atlas(graphics_state, filesystem, tileset)?; + + let tilepicker_data = (47..(384 + 47)) + .step_by(48) + .chain(384..(atlas.tileset_height as i16 / 32 * 8 + 384)) + .collect_vec(); + let tilepicker_data = luminol_data::Table3::new_data( + 8, + 1 + (atlas.tileset_height / 32) as usize, + 1, + tilepicker_data, + ); + + let viewport = Arc::new(Viewport::new( + graphics_state, + 256., + atlas.tileset_height as f32 + 32., + )); + + let tiles = Tiles::new(graphics_state, viewport.clone(), atlas, &tilepicker_data); + + let grid = Grid::new( + graphics_state, + viewport.clone(), + tilepicker_data.xsize() as u32, + tilepicker_data.ysize() as u32, + ); + + let mut passages = + luminol_data::Table2::new(tilepicker_data.xsize(), tilepicker_data.ysize()); + for x in 0..8 { + passages[(x, 0)] = { + let tile_id = tilepicker_data[(x, 0, 0)].try_into().unwrap_or_default(); + if tile_id >= tileset.passages.len() { + 0 + } else { + tileset.passages[tile_id] + } + }; + } + let length = + (passages.len().saturating_sub(8)).min(tileset.passages.len().saturating_sub(384)); + passages.as_mut_slice()[8..8 + length] + .copy_from_slice(&tileset.passages.as_slice()[384..384 + length]); + let collision = Collision::new(graphics_state, viewport.clone(), &passages); + + Ok(Self { + resources: Arc::new(Resources { + tiles, + collision, + grid, + }), + viewport, + coll_enabled: false, + grid_enabled: false, + ani_time: None, + }) + } + + pub fn paint( + &mut self, + graphics_state: Arc, + painter: &egui::Painter, + rect: egui::Rect, + ) { + let time = painter.ctx().input(|i| i.time); + if let Some(ani_time) = self.ani_time { + if time - ani_time >= 16. / 60. { + self.ani_time = Some(time); + self.resources + .tiles + .autotiles + .inc_ani_index(&graphics_state.render_state); + } + } else { + self.ani_time = Some(time); + } + + painter + .ctx() + .request_repaint_after(Duration::from_secs_f64(16. / 60.)); + + painter.add(luminol_egui_wgpu::Callback::new_paint_callback( + rect, + Callback { + resources: Fragile::new(self.resources.clone()), + graphics_state: Fragile::new(graphics_state), + + coll_enabled: self.coll_enabled, + grid_enabled: self.grid_enabled, + }, + )); + } + + pub fn set_proj(&self, render_state: &luminol_egui_wgpu::RenderState, proj: glam::Mat4) { + self.viewport.set_proj(render_state, proj); + } + + pub fn atlas(&self) -> &Atlas { + &self.resources.tiles.atlas + } +} diff --git a/crates/ui/src/tabs/map/mod.rs b/crates/ui/src/tabs/map/mod.rs index a419315f..f048710d 100644 --- a/crates/ui/src/tabs/map/mod.rs +++ b/crates/ui/src/tabs/map/mod.rs @@ -335,8 +335,8 @@ impl luminol_core::Tab for Tab { .persistence_id, ) .show_viewport(ui, |ui, rect| { - self.tilepicker.coll_enabled = self.view.map.coll_enabled; - self.tilepicker.grid_enabled = self.view.map.grid_enabled; + self.tilepicker.view.coll_enabled = self.view.map.coll_enabled; + self.tilepicker.view.grid_enabled = self.view.map.grid_enabled; self.tilepicker.ui(update_state, ui, rect); ui.separator(); }); From 7d4901baa909555a665dc22f0ee5887fa837a867 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Mon, 10 Jun 2024 22:54:31 -0700 Subject: [PATCH 08/24] Fix collision shader --- crates/graphics/src/primitives/collision/instance.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/graphics/src/primitives/collision/instance.rs b/crates/graphics/src/primitives/collision/instance.rs index 50678bb3..8a934e1a 100644 --- a/crates/graphics/src/primitives/collision/instance.rs +++ b/crates/graphics/src/primitives/collision/instance.rs @@ -185,7 +185,7 @@ impl Instances { pub const fn desc() -> wgpu::VertexBufferLayout<'static> { const ARRAY: &[wgpu::VertexAttribute] = - &wgpu::vertex_attr_array![2 => Float32x3, 3 => Uint32]; + &wgpu::vertex_attr_array![2 => Float32x2, 3 => Uint32]; wgpu::VertexBufferLayout { array_stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Instance, From 16280c6a6c69151d5ab5a5f1730929deea3c5faa Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Tue, 11 Jun 2024 14:08:24 -0700 Subject: [PATCH 09/24] Remove vertex buffer from grid --- .../graphics/src/primitives/grid/instance.rs | 71 +------------------ crates/graphics/src/primitives/grid/mod.rs | 4 +- crates/graphics/src/primitives/grid/shader.rs | 15 ++-- crates/graphics/src/primitives/grid/vertex.rs | 33 --------- .../graphics/src/primitives/shaders/grid.wgsl | 34 +++++++-- src/main.rs | 3 +- 6 files changed, 42 insertions(+), 118 deletions(-) delete mode 100644 crates/graphics/src/primitives/grid/vertex.rs diff --git a/crates/graphics/src/primitives/grid/instance.rs b/crates/graphics/src/primitives/grid/instance.rs index 1ab79247..b63bd15a 100644 --- a/crates/graphics/src/primitives/grid/instance.rs +++ b/crates/graphics/src/primitives/grid/instance.rs @@ -15,88 +15,19 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use super::Vertex; -use wgpu::util::DeviceExt; - #[derive(Debug)] pub struct Instances { - vertex_buffer: wgpu::Buffer, map_size: u32, } impl Instances { - pub fn new( - render_state: &luminol_egui_wgpu::RenderState, - map_width: u32, - map_height: u32, - ) -> Self { - let vertices = Self::calculate_vertices(render_state); - let vertex_buffer = - render_state - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("tilemap grid vertex buffer"), - contents: bytemuck::cast_slice(&vertices), - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - }); - + pub fn new(map_width: u32, map_height: u32) -> Self { Self { - vertex_buffer, map_size: map_width * map_height, } } - fn calculate_vertices(render_state: &luminol_egui_wgpu::RenderState) -> [Vertex; 6] { - // OpenGL and WebGL use the last vertex in each triangle as the provoking vertex, and - // Direct3D, Metal, Vulkan and WebGPU use the first vertex in each triangle - if render_state.adapter.get_info().backend == wgpu::Backend::Gl { - [ - Vertex { - position: glam::vec2(1., 0.), - }, - Vertex { - position: glam::vec2(0., 1.), - }, - Vertex { - position: glam::vec2(0., 0.), // Provoking vertex - }, - Vertex { - position: glam::vec2(0., 1.), - }, - Vertex { - position: glam::vec2(1., 0.), - }, - Vertex { - position: glam::vec2(1., 1.), // Provoking vertex - }, - ] - } else { - [ - Vertex { - position: glam::vec2(0., 0.), // Provoking vertex - }, - Vertex { - position: glam::vec2(1., 0.), - }, - Vertex { - position: glam::vec2(0., 1.), - }, - Vertex { - position: glam::vec2(1., 1.), // Provoking vertex - }, - Vertex { - position: glam::vec2(0., 1.), - }, - Vertex { - position: glam::vec2(1., 0.), - }, - ] - } - } - pub fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>) { - render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); - render_pass.draw(0..6, 0..self.map_size); } } diff --git a/crates/graphics/src/primitives/grid/mod.rs b/crates/graphics/src/primitives/grid/mod.rs index eabb63a6..08411d49 100644 --- a/crates/graphics/src/primitives/grid/mod.rs +++ b/crates/graphics/src/primitives/grid/mod.rs @@ -21,12 +21,10 @@ use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Viewport}; use display::Display; use instance::Instances; -use vertex::Vertex; pub mod display; mod instance; pub(crate) mod shader; -mod vertex; #[derive(Debug)] pub struct Grid { @@ -44,7 +42,7 @@ impl Grid { map_width: u32, map_height: u32, ) -> Self { - let instances = Instances::new(&graphics_state.render_state, map_width, map_height); + let instances = Instances::new(map_width, map_height); let display = Display::new(graphics_state, map_width, map_height); let mut bind_group_builder = BindGroupBuilder::new(); diff --git a/crates/graphics/src/primitives/grid/shader.rs b/crates/graphics/src/primitives/grid/shader.rs index 6b68c0bc..fce64da4 100644 --- a/crates/graphics/src/primitives/grid/shader.rs +++ b/crates/graphics/src/primitives/grid/shader.rs @@ -15,18 +15,25 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use super::Vertex; - pub fn create_render_pipeline( composer: &mut naga_oil::compose::Composer, render_state: &luminol_egui_wgpu::RenderState, bind_group_layouts: &crate::primitives::BindGroupLayouts, ) -> Result { + let shader_defs = if render_state.adapter.get_info().backend == wgpu::Backend::Gl { + std::collections::HashMap::from([( + "LUMINOL_BACKEND_GL".to_string(), + naga_oil::compose::ShaderDefValue::Bool(true), + )]) + } else { + std::collections::HashMap::default() + }; + let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { source: include_str!("../shaders/grid.wgsl"), file_path: "grid.wgsl", shader_type: naga_oil::compose::ShaderType::Wgsl, - shader_defs: std::collections::HashMap::default(), + shader_defs, additional_imports: &[], })?; @@ -54,7 +61,7 @@ pub fn create_render_pipeline( vertex: wgpu::VertexState { module: &shader_module, entry_point: "vs_main", - buffers: &[Vertex::desc()], + buffers: &[], }, fragment: Some(wgpu::FragmentState { module: &shader_module, diff --git a/crates/graphics/src/primitives/grid/vertex.rs b/crates/graphics/src/primitives/grid/vertex.rs deleted file mode 100644 index 28f5c2a3..00000000 --- a/crates/graphics/src/primitives/grid/vertex.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2023 Lily Lyons -// -// This file is part of Luminol. -// -// Luminol is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Luminol is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Luminol. If not, see . - -#[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable, PartialEq)] -pub struct Vertex { - pub position: glam::Vec2, -} - -impl Vertex { - const ATTRIBS: [wgpu::VertexAttribute; 1] = wgpu::vertex_attr_array![0 => Float32x2]; - pub const fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &Self::ATTRIBS, - } - } -} diff --git a/crates/graphics/src/primitives/shaders/grid.wgsl b/crates/graphics/src/primitives/shaders/grid.wgsl index 72bc0225..ba39320f 100644 --- a/crates/graphics/src/primitives/shaders/grid.wgsl +++ b/crates/graphics/src/primitives/shaders/grid.wgsl @@ -1,7 +1,3 @@ -struct VertexInput { - @location(0) position: vec2, -} - struct VertexOutput { @builtin(position) clip_position: vec4, @location(0) position: vec2, @@ -27,16 +23,42 @@ var viewport: Viewport; @group(0) @binding(1) var display: Display; +// OpenGL and WebGL use the last vertex in each triangle as the provoking vertex, and +// Direct3D, Metal, Vulkan and WebGPU use the first vertex in each triangle +#ifdef LUMINOL_BACKEND_GL +const QUAD_VERTICES: array = array( + vec2f(1., 0.), + vec2f(0., 1.), + vec2f(0., 0.), // Provoking vertex + + vec2f(0., 1.), + vec2f(1., 0.), + vec2f(1., 1.), // Provoking vertex +); +#else +const QUAD_VERTICES: array, 6> = array, 6>( + vec2(0., 0.), + vec2(1., 0.), + vec2(0., 1.), // Provoking vertex + + vec2(1., 1.), + vec2(0., 1.), + vec2(1., 0.), // Provoking vertex +); +#endif + @vertex -fn vs_main(vertex: VertexInput, @builtin(instance_index) instance_index: u32) -> VertexOutput { +fn vs_main(@builtin(vertex_index) vertex_index: u32, @builtin(instance_index) instance_index: u32) -> VertexOutput { var out: VertexOutput; + var quad_vertices = QUAD_VERTICES; + let vertex_position = quad_vertices[vertex_index % 6u]; let tile_position = vec2( f32(instance_index % display.map_size.x), f32(instance_index / display.map_size.x) ); - out.position = (viewport.proj * vec4((vertex.position + tile_position) * 32., 0., 1.)).xy; + out.position = (viewport.proj * vec4((vertex_position + tile_position) * 32., 0., 1.)).xy; out.vertex_position = out.position; out.clip_position = vec4(out.position, 0., 1.); return out; diff --git a/src/main.rs b/src/main.rs index 11046d1c..6ed6708e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -214,7 +214,6 @@ fn main() { let native_options = luminol_eframe::NativeOptions { viewport: egui::ViewportBuilder::default() .with_drag_and_drop(true) - .with_transparent(true) .with_icon(egui::IconData { width: image.width(), height: image.height(), @@ -223,7 +222,7 @@ fn main() { .with_app_id("astrabit.luminol"), wgpu_options: luminol_egui_wgpu::WgpuConfiguration { supported_backends: wgpu::util::backend_bits_from_env() - .unwrap_or(wgpu::Backends::PRIMARY), + .unwrap_or(wgpu::Backends::PRIMARY | wgpu::Backends::SECONDARY), power_preference: wgpu::util::power_preference_from_env() .unwrap_or(wgpu::PowerPreference::LowPower), ..Default::default() From 79e31a7b4c14e1a985a09ed20a1dacd9e5ce43dd Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Tue, 11 Jun 2024 14:28:31 -0700 Subject: [PATCH 10/24] Remove tilemap vertex buffer --- .../graphics/src/primitives/shaders/grid.wgsl | 2 +- .../src/primitives/shaders/tilemap.wgsl | 42 ++++++++++++++----- .../graphics/src/primitives/tiles/instance.rs | 19 +-------- crates/graphics/src/primitives/tiles/mod.rs | 6 +-- .../graphics/src/primitives/tiles/shader.rs | 4 +- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/crates/graphics/src/primitives/shaders/grid.wgsl b/crates/graphics/src/primitives/shaders/grid.wgsl index ba39320f..1d2a5b11 100644 --- a/crates/graphics/src/primitives/shaders/grid.wgsl +++ b/crates/graphics/src/primitives/shaders/grid.wgsl @@ -52,7 +52,7 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, @builtin(instance_index) in var out: VertexOutput; var quad_vertices = QUAD_VERTICES; - let vertex_position = quad_vertices[vertex_index % 6u]; + let vertex_position = quad_vertices[vertex_index]; let tile_position = vec2( f32(instance_index % display.map_size.x), f32(instance_index / display.map_size.x) diff --git a/crates/graphics/src/primitives/shaders/tilemap.wgsl b/crates/graphics/src/primitives/shaders/tilemap.wgsl index 6553c611..19557ec2 100644 --- a/crates/graphics/src/primitives/shaders/tilemap.wgsl +++ b/crates/graphics/src/primitives/shaders/tilemap.wgsl @@ -1,12 +1,7 @@ #import luminol::gamma as Gamma -struct VertexInput { - @location(0) position: vec2, - @location(1) tex_coords: vec2, -} - struct InstanceInput { - @location(2) tile_id: u32, + @location(0) tile_id: u32, @builtin(instance_index) index: u32 } @@ -43,8 +38,28 @@ var autotiles: Autotiles; @group(0) @binding(4) var display: Display; +const VERTEX_POSITIONS = array( + vec2f(0.0, 0.0), + vec2f(32.0, 0.0), + vec2f(0.0, 32.0), + + vec2f(32.0, 0.0), + vec2f(0.0, 32.0), + vec2f(32.0, 32.0), +); +const TEX_COORDS = array( + // slightly smaller than 32x32 to reduce bleeding from adjacent pixels in the atlas + vec2f(0.01, 0.01), + vec2f(31.99, 0.01), + vec2f(0.01, 31.99), + + vec2f(31.99, 0.01), + vec2f(0.01, 31.99), + vec2f(31.99, 31.99), +); + @vertex -fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { +fn vs_main(@builtin(vertex_index) vertex_index: u32, instance: InstanceInput) -> VertexOutput { var out: VertexOutput; if instance.tile_id < #AUTOTILE_ID_AMOUNT { @@ -57,7 +72,10 @@ fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { f32(layer_instance_index / display.map_size.x) ); - let position = viewport.proj * vec4(vertex.position.xy + (tile_position * 32.), 0.0, 1.0); + var vertex_positions = VERTEX_POSITIONS; + let vertex_position = vertex_positions[vertex_index]; + + let position = viewport.proj * vec4(vertex_position + (tile_position * 32.), 0.0, 1.0); out.clip_position = vec4(position.xy, 0.0, 1.0); // we don't set the z because we have no z buffer let is_autotile = instance.tile_id < #TOTAL_AUTOTILE_ID_AMOUNT; @@ -90,15 +108,17 @@ fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { if is_autotile { let autotile_type = instance.tile_id / #AUTOTILE_ID_AMOUNT - 1; -// we get an error about non constant indexing without this. -// not sure why let frame_count = autotiles.frame_counts[autotile_type / 4][autotile_type % 4]; let frame = autotiles.animation_index % frame_count; atlas_tile_position.x += f32(frame * #AUTOTILE_FRAME_WIDTH); } + let tex_size = vec2(textureDimensions(atlas)); - out.tex_coords = vertex.tex_coords + (atlas_tile_position / tex_size); + var vertex_tex_coords = TEX_COORDS; + let vertex_tex_coord = vertex_tex_coords[vertex_index] / tex_size; + + out.tex_coords = vertex_tex_coord + (atlas_tile_position / tex_size); return out; } diff --git a/crates/graphics/src/primitives/tiles/instance.rs b/crates/graphics/src/primitives/tiles/instance.rs index be8b5be5..3ccc5f74 100644 --- a/crates/graphics/src/primitives/tiles/instance.rs +++ b/crates/graphics/src/primitives/tiles/instance.rs @@ -18,12 +18,9 @@ use itertools::Itertools; use wgpu::util::DeviceExt; -use crate::Quad; - #[derive(Debug)] pub struct Instances { instance_buffer: wgpu::Buffer, - vertex_buffer: wgpu::Buffer, map_width: usize, map_height: usize, @@ -35,17 +32,10 @@ struct Instance { tile_id: u32, // force this to be an u32 to avoid padding issues } -const TILE_QUAD: Quad = Quad::new( - egui::Rect::from_min_max(egui::pos2(0., 0.), egui::pos2(32., 32.0)), - // slightly smaller than 32x32 to reduce bleeding from adjacent pixels in the atlas - egui::Rect::from_min_max(egui::pos2(0.01, 0.01), egui::pos2(31.99, 31.99)), -); - impl Instances { pub fn new( render_state: &luminol_egui_wgpu::RenderState, map_data: &luminol_data::Table3, - atlas_size: wgpu::Extent3d, ) -> Self { let instances = Self::calculate_instances(map_data); let instance_buffer = @@ -57,11 +47,8 @@ impl Instances { usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, }); - let (vertex_buffer, _) = Quad::into_buffer(render_state, &[TILE_QUAD], atlas_size); - Self { instance_buffer, - vertex_buffer, map_width: map_data.xsize(), map_height: map_data.ysize(), @@ -102,8 +89,6 @@ impl Instances { } pub fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>, layer: usize) { - render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); - // Calculate the start and end index of the buffer, as well as the amount of instances. let start_index = layer * self.map_width * self.map_height; let end_index = (layer + 1) * self.map_width * self.map_height; @@ -113,13 +98,13 @@ impl Instances { let start = (start_index * std::mem::size_of::()) as wgpu::BufferAddress; let end = (end_index * std::mem::size_of::()) as wgpu::BufferAddress; - render_pass.set_vertex_buffer(1, self.instance_buffer.slice(start..end)); + render_pass.set_vertex_buffer(0, self.instance_buffer.slice(start..end)); render_pass.draw(0..6, 0..count); } pub const fn desc() -> wgpu::VertexBufferLayout<'static> { - const ARRAY: &[wgpu::VertexAttribute] = &wgpu::vertex_attr_array![2 => Uint32]; + const ARRAY: &[wgpu::VertexAttribute] = &wgpu::vertex_attr_array![0 => Uint32]; wgpu::VertexBufferLayout { array_stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Instance, diff --git a/crates/graphics/src/primitives/tiles/mod.rs b/crates/graphics/src/primitives/tiles/mod.rs index e7563234..980d2ca1 100644 --- a/crates/graphics/src/primitives/tiles/mod.rs +++ b/crates/graphics/src/primitives/tiles/mod.rs @@ -50,11 +50,7 @@ impl Tiles { tiles: &luminol_data::Table3, ) -> Self { let autotiles = Autotiles::new(graphics_state, &atlas); - let instances = Instances::new( - &graphics_state.render_state, - tiles, - atlas.atlas_texture.size(), - ); + let instances = Instances::new(&graphics_state.render_state, tiles); let display = Display::new( graphics_state, tiles.xsize() as u32, diff --git a/crates/graphics/src/primitives/tiles/shader.rs b/crates/graphics/src/primitives/tiles/shader.rs index cbb883bb..4a79f0ac 100644 --- a/crates/graphics/src/primitives/tiles/shader.rs +++ b/crates/graphics/src/primitives/tiles/shader.rs @@ -16,7 +16,7 @@ // along with Luminol. If not, see . use super::instance::Instances; -use crate::{primitives::BindGroupLayouts, Vertex}; +use crate::primitives::BindGroupLayouts; pub fn create_render_pipeline( composer: &mut naga_oil::compose::Composer, @@ -104,7 +104,7 @@ pub fn create_render_pipeline( vertex: wgpu::VertexState { module: &shader_module, entry_point: "vs_main", - buffers: &[Vertex::desc(), Instances::desc()], + buffers: &[Instances::desc()], }, fragment: Some(wgpu::FragmentState { module: &shader_module, From 49621a963f704c08f8b2d1e5b756d80d0c49a014 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Tue, 11 Jun 2024 14:42:19 -0700 Subject: [PATCH 11/24] Remove vertex buffer from collision --- .../src/primitives/collision/instance.rs | 79 +------------------ .../graphics/src/primitives/collision/mod.rs | 2 - .../src/primitives/collision/shader.rs | 3 +- .../src/primitives/collision/vertex.rs | 36 --------- .../src/primitives/shaders/collision.wgsl | 46 ++++++++--- 5 files changed, 39 insertions(+), 127 deletions(-) delete mode 100644 crates/graphics/src/primitives/collision/vertex.rs diff --git a/crates/graphics/src/primitives/collision/instance.rs b/crates/graphics/src/primitives/collision/instance.rs index 8a934e1a..f09b9092 100644 --- a/crates/graphics/src/primitives/collision/instance.rs +++ b/crates/graphics/src/primitives/collision/instance.rs @@ -15,14 +15,12 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use super::Vertex; use itertools::Itertools; use wgpu::util::DeviceExt; #[derive(Debug)] pub struct Instances { instance_buffer: wgpu::Buffer, - vertex_buffer: wgpu::Buffer, map_width: usize, map_height: usize, @@ -50,19 +48,8 @@ impl Instances { usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, }); - let vertices = Self::calculate_vertices(); - let vertex_buffer = - render_state - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("tilemap collision vertex buffer"), - contents: bytemuck::cast_slice(&vertices), - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - }); - Self { instance_buffer, - vertex_buffer, map_width: passages.xsize(), map_height: passages.ysize(), @@ -106,69 +93,7 @@ impl Instances { .collect_vec() } - fn calculate_vertices() -> [Vertex; 12] { - let rect = egui::Rect::from_min_size(egui::pos2(0., 0.), egui::vec2(32., 32.)); - let center = glam::vec2(rect.center().x, rect.center().y); - let top_left = glam::vec2(rect.left_top().x, rect.left_top().y); - let top_right = glam::vec2(rect.right_top().x, rect.right_top().y); - let bottom_left = glam::vec2(rect.left_bottom().x, rect.left_bottom().y); - let bottom_right = glam::vec2(rect.right_bottom().x, rect.right_bottom().y); - - [ - Vertex { - position: center, - direction: 1, - }, - Vertex { - position: bottom_left, - direction: 1, - }, - Vertex { - position: bottom_right, - direction: 1, - }, - Vertex { - position: center, - direction: 2, - }, - Vertex { - position: top_left, - direction: 2, - }, - Vertex { - position: bottom_left, - direction: 2, - }, - Vertex { - position: center, - direction: 4, - }, - Vertex { - position: bottom_right, - direction: 4, - }, - Vertex { - position: top_right, - direction: 4, - }, - Vertex { - position: center, - direction: 8, - }, - Vertex { - position: top_right, - direction: 8, - }, - Vertex { - position: top_left, - direction: 8, - }, - ] - } - pub fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>) { - render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); - // Calculate the start and end index of the buffer, as well as the amount of instances. let start_index = 0; let end_index = self.map_width * self.map_height; @@ -178,14 +103,14 @@ impl Instances { let start = (start_index * std::mem::size_of::()) as wgpu::BufferAddress; let end = (end_index * std::mem::size_of::()) as wgpu::BufferAddress; - render_pass.set_vertex_buffer(1, self.instance_buffer.slice(start..end)); + render_pass.set_vertex_buffer(0, self.instance_buffer.slice(start..end)); render_pass.draw(0..12, 0..count); } pub const fn desc() -> wgpu::VertexBufferLayout<'static> { const ARRAY: &[wgpu::VertexAttribute] = - &wgpu::vertex_attr_array![2 => Float32x2, 3 => Uint32]; + &wgpu::vertex_attr_array![0 => Float32x2, 1 => Uint32]; wgpu::VertexBufferLayout { array_stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Instance, diff --git a/crates/graphics/src/primitives/collision/mod.rs b/crates/graphics/src/primitives/collision/mod.rs index 3b98046f..7a330917 100644 --- a/crates/graphics/src/primitives/collision/mod.rs +++ b/crates/graphics/src/primitives/collision/mod.rs @@ -21,11 +21,9 @@ use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Viewport}; use instance::Instances; use itertools::Itertools; -use vertex::Vertex; mod instance; pub(crate) mod shader; -mod vertex; #[derive(Debug)] pub struct Collision { diff --git a/crates/graphics/src/primitives/collision/shader.rs b/crates/graphics/src/primitives/collision/shader.rs index 51761582..da1ccbd9 100644 --- a/crates/graphics/src/primitives/collision/shader.rs +++ b/crates/graphics/src/primitives/collision/shader.rs @@ -16,7 +16,6 @@ // along with Luminol. If not, see . use super::instance::Instances; -use super::Vertex; pub fn create_render_pipeline( composer: &mut naga_oil::compose::Composer, @@ -55,7 +54,7 @@ pub fn create_render_pipeline( vertex: wgpu::VertexState { module: &shader_module, entry_point: "vs_main", - buffers: &[Vertex::desc(), Instances::desc()], + buffers: &[Instances::desc()], }, fragment: Some(wgpu::FragmentState { module: &shader_module, diff --git a/crates/graphics/src/primitives/collision/vertex.rs b/crates/graphics/src/primitives/collision/vertex.rs deleted file mode 100644 index 0d97251a..00000000 --- a/crates/graphics/src/primitives/collision/vertex.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2023 Lily Lyons -// -// This file is part of Luminol. -// -// Luminol is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Luminol is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Luminol. If not, see . - -#[repr(C)] -#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable, PartialEq)] -pub struct Vertex { - pub position: glam::Vec2, - /// 1: down, 2: left, 4: right, 8: up - pub direction: u32, -} - -impl Vertex { - const ATTRIBS: [wgpu::VertexAttribute; 2] = - wgpu::vertex_attr_array![0 => Float32x2, 1 => Uint32]; - pub const fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &Self::ATTRIBS, - } - } -} diff --git a/crates/graphics/src/primitives/shaders/collision.wgsl b/crates/graphics/src/primitives/shaders/collision.wgsl index cb063ff6..0b22f22c 100644 --- a/crates/graphics/src/primitives/shaders/collision.wgsl +++ b/crates/graphics/src/primitives/shaders/collision.wgsl @@ -1,11 +1,6 @@ -struct VertexInput { - @location(0) position: vec2, - @location(1) direction: u32, -} - struct InstanceInput { - @location(2) tile_position: vec2, - @location(3) passage: u32, + @location(0) tile_position: vec2, + @location(1) passage: u32, } struct VertexOutput { @@ -19,15 +14,46 @@ struct Viewport { @group(0) @binding(0) var viewport: Viewport; +const VERTEX_POSITIONS = array( + vec2f(16., 16.), + vec2f(0., 32.), + vec2f(32., 32.), + + vec2f(16., 16.), + vec2f(0., 0.), + vec2f(0., 32.), + + vec2f(16., 16.), + vec2f(32., 32.), + vec2f(32., 0.), + + vec2f(16., 16.), + vec2f(32., 0.), + vec2f(0., 0.), +); + +const VERTEX_DIRECTIONS = array( + 1, + 2, + 4, + 8, +); + @vertex -fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VertexOutput { +fn vs_main(@builtin(vertex_index) vertex_index: u32, instance: InstanceInput) -> VertexOutput { var out: VertexOutput; - if (instance.passage & vertex.direction) == 0u { + var vertex_directions = VERTEX_DIRECTIONS; + let vertex_direction = vertex_directions[vertex_index / 3]; + + if (instance.passage & vertex_direction) == 0u { return out; } - let position = viewport.proj * vec4(vertex.position.xy + (instance.tile_position.xy * 32.), 0.0, 1.0); + var vertex_positions = VERTEX_POSITIONS; + let vertex_position = vertex_positions[vertex_index]; + + let position = viewport.proj * vec4(vertex_position + (instance.tile_position.xy * 32.), 0.0, 1.0); out.clip_position = vec4(position.xy, 0.0, 1.0); return out; From 9e709e281d43ab6bcc3c05d103c26425f1e34e36 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Tue, 11 Jun 2024 21:49:27 -0700 Subject: [PATCH 12/24] Use shared placeholder png --- Cargo.lock | 1 + .../graphics/data => assets}/placeholder.png | Bin crates/graphics/Cargo.toml | 1 + crates/graphics/src/event.rs | 57 ++------ crates/graphics/src/lib.rs | 6 - crates/graphics/src/loaders/texture.rs | 133 ++++++++++++++---- crates/graphics/src/map.rs | 87 +----------- crates/graphics/src/primitives/tiles/atlas.rs | 66 ++------- crates/graphics/src/primitives/tiles/mod.rs | 2 +- 9 files changed, 135 insertions(+), 218 deletions(-) rename {crates/graphics/data => assets}/placeholder.png (100%) diff --git a/Cargo.lock b/Cargo.lock index 83178e9a..91cc04eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3184,6 +3184,7 @@ dependencies = [ "luminol-data", "luminol-egui-wgpu", "luminol-filesystem", + "luminol-macros", "naga", "naga_oil", "parking_lot", diff --git a/crates/graphics/data/placeholder.png b/assets/placeholder.png similarity index 100% rename from crates/graphics/data/placeholder.png rename to assets/placeholder.png diff --git a/crates/graphics/Cargo.toml b/crates/graphics/Cargo.toml index fa817f6c..b3385645 100644 --- a/crates/graphics/Cargo.toml +++ b/crates/graphics/Cargo.toml @@ -41,5 +41,6 @@ camino.workspace = true luminol-data.workspace = true luminol-filesystem.workspace = true +luminol-macros.workspace = true fragile.workspace = true diff --git a/crates/graphics/src/event.rs b/crates/graphics/src/event.rs index 87eb8628..d90a8b64 100644 --- a/crates/graphics/src/event.rs +++ b/crates/graphics/src/event.rs @@ -16,9 +16,6 @@ // along with Luminol. If not, see . use color_eyre::eyre::Context; -use image::EncodableLayout; -use itertools::Itertools; -use wgpu::util::DeviceExt; use std::sync::Arc; @@ -64,6 +61,7 @@ impl Event { color_eyre::eyre::bail!("event does not have first page"); }; + let mut is_placeholder = false; let texture = if let Some(ref filename) = page.graphic.character_name { let texture = graphics_state .texture_loader @@ -73,50 +71,8 @@ impl Event { Ok(t) => t, Err(e) => { graphics_state.send_texture_error(e); - - let placeholder_char_texture = graphics_state - .texture_loader - .get("placeholder_char_texture") - .unwrap_or_else(|| { - let placeholder_img = graphics_state.placeholder_img(); - - graphics_state.texture_loader.register_texture( - "placeholder_char_texture", - graphics_state.render_state.device.create_texture_with_data( - &graphics_state.render_state.queue, - &wgpu::TextureDescriptor { - label: Some("placeholder_char_texture"), - size: wgpu::Extent3d { - width: 128, - height: 128, - depth_or_array_layers: 1, - }, - dimension: wgpu::TextureDimension::D2, - mip_level_count: 1, - sample_count: 1, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::COPY_SRC - | wgpu::TextureUsages::COPY_DST - | wgpu::TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }, - wgpu::util::TextureDataOrder::LayerMajor, - &itertools::iproduct!(0..128, 0..128, 0..4) - .map(|(y, x, c)| { - // Tile the placeholder image - placeholder_img.as_bytes()[(c - + (x % placeholder_img.width()) * 4 - + (y % placeholder_img.height()) - * 4 - * placeholder_img.width()) - as usize] - }) - .collect_vec(), - ), - ) - }); - - placeholder_char_texture + is_placeholder = true; + graphics_state.texture_loader.placeholder_texture() } } } else if page.graphic.tile_id.is_some() { @@ -131,6 +87,13 @@ impl Event { let viewport = Arc::new(Viewport::new(graphics_state, 32., 32.)); + (quad, viewport, egui::vec2(32., 32.)) + } else if is_placeholder { + let rect = egui::Rect::from_min_size(egui::pos2(0.0, 0.0), egui::vec2(32., 32.0)); + let quad = Quad::new(rect, rect); + + let viewport = Arc::new(Viewport::new(graphics_state, 32., 32.)); + (quad, viewport, egui::vec2(32., 32.)) } else { let cw = texture.width() as f32 / 4.; diff --git a/crates/graphics/src/lib.rs b/crates/graphics/src/lib.rs index b15f89c9..cebd8403 100644 --- a/crates/graphics/src/lib.rs +++ b/crates/graphics/src/lib.rs @@ -100,10 +100,4 @@ impl GraphicsState { pub fn texture_errors(&self) -> impl Iterator + '_ { self.texture_error_rx.try_iter() } - - pub fn placeholder_img(&self) -> image::RgbaImage { - image::load_from_memory(include_bytes!("../data/placeholder.png")) - .expect("assets/placeholder.png is not a valid image") - .to_rgba8() - } } diff --git a/crates/graphics/src/loaders/texture.rs b/crates/graphics/src/loaders/texture.rs index 35d0e613..e330f4db 100644 --- a/crates/graphics/src/loaders/texture.rs +++ b/crates/graphics/src/loaders/texture.rs @@ -31,6 +31,10 @@ use wgpu::util::DeviceExt; pub struct Loader { loaded_textures: DashMap>, + placeholder_texture: Arc, + blank_autotile_texture: Arc, + placeholder_image: image::RgbaImage, + render_state: luminol_egui_wgpu::RenderState, } @@ -58,13 +62,27 @@ fn load_wgpu_texture_from_path( let file = filesystem.read(path)?; let texture_data = image::load_from_memory(&file)?.to_rgba8(); - Ok(device.create_texture_with_data( + Ok(load_wgpu_texture_from_image( + &texture_data, + device, + queue, + Some(path), + )) +} + +fn load_wgpu_texture_from_image( + image: &image::RgbaImage, + device: &wgpu::Device, + queue: &wgpu::Queue, + label: Option<&str>, +) -> wgpu::Texture { + device.create_texture_with_data( queue, &wgpu::TextureDescriptor { - label: Some(path), + label, size: wgpu::Extent3d { - width: texture_data.width(), - height: texture_data.height(), + width: image.width(), + height: image.height(), depth_or_array_layers: 1, }, dimension: wgpu::TextureDimension::D2, @@ -77,8 +95,30 @@ fn load_wgpu_texture_from_path( view_formats: &[], }, wgpu::util::TextureDataOrder::LayerMajor, - &texture_data, - )) + image, + ) +} + +fn register_native_texture( + render_state: luminol_egui_wgpu::RenderState, + texture: wgpu::Texture, + label: Option<&str>, +) -> Arc { + let view = texture.create_view(&wgpu::TextureViewDescriptor { + label, + ..Default::default() + }); + let texture_id = render_state.renderer.write().register_native_texture( + &render_state.device, + &view, + wgpu::FilterMode::Nearest, + ); + Arc::new(Texture { + texture, + view, + texture_id, + render_state, + }) } impl Texture { @@ -101,9 +141,56 @@ impl Texture { impl Loader { pub fn new(render_state: luminol_egui_wgpu::RenderState) -> Self { + let placeholder_image = + image::load_from_memory(luminol_macros::include_asset!("assets/placeholder.png")) + .expect("assets/placeholder.png is not a valid image") + .to_rgba8(); + + let placeholder_texture = load_wgpu_texture_from_image( + &placeholder_image, + &render_state.device, + &render_state.queue, + Some("assets/placeholder.png"), + ); + let placeholder_texture = register_native_texture( + render_state.clone(), + placeholder_texture, + Some("placeholder texture"), + ); + + let blank_autotile_texture = render_state + .device + .create_texture(&wgpu::TextureDescriptor { + label: Some("blank autotile texture"), + size: wgpu::Extent3d { + width: crate::primitives::tiles::AUTOTILE_FRAME_COLS + * crate::primitives::tiles::TILE_SIZE, + height: crate::primitives::tiles::AUTOTILE_ROWS + * crate::primitives::tiles::TILE_SIZE, + depth_or_array_layers: 1, + }, + dimension: wgpu::TextureDimension::D2, + mip_level_count: 1, + sample_count: 1, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::COPY_SRC + | wgpu::TextureUsages::COPY_DST + | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + let blank_autotile_texture = register_native_texture( + render_state.clone(), + blank_autotile_texture, + Some("blank autotile texture"), + ); + Self { loaded_textures: DashMap::with_capacity(64), + placeholder_texture, + blank_autotile_texture, + placeholder_image, + render_state, } } @@ -142,26 +229,8 @@ impl Loader { ) -> Arc { let path = path.into(); - let view = texture.create_view(&wgpu::TextureViewDescriptor { - label: Some(path.as_str()), - ..Default::default() - }); - - // todo maybe use custom sampler descriptor? - // would allow for better texture names in debuggers - let texture_id = self.render_state.renderer.write().register_native_texture( - &self.render_state.device, - &view, - wgpu::FilterMode::Nearest, - ); - - let texture = Arc::new(Texture { - texture, - view, - texture_id, - - render_state: self.render_state.clone(), - }); + let texture = + register_native_texture(self.render_state.clone(), texture, Some(path.as_str())); self.loaded_textures.insert(path, texture.clone()); texture } @@ -179,4 +248,16 @@ impl Loader { pub fn clear(&self) { self.loaded_textures.clear(); } + + pub fn placeholder_texture(&self) -> Arc { + self.placeholder_texture.clone() + } + + pub fn blank_autotile_texture(&self) -> Arc { + self.blank_autotile_texture.clone() + } + + pub fn placeholder_image(&self) -> &image::RgbaImage { + &self.placeholder_image + } } diff --git a/crates/graphics/src/map.rs b/crates/graphics/src/map.rs index 5b8d4f62..25065d95 100644 --- a/crates/graphics/src/map.rs +++ b/crates/graphics/src/map.rs @@ -16,9 +16,6 @@ // along with Luminol. If not, see . use color_eyre::eyre::Context; -use image::EncodableLayout; -use itertools::Itertools; -use wgpu::util::DeviceExt; use std::sync::Arc; @@ -153,47 +150,7 @@ impl Map { .unwrap_or_else(|e| { graphics_state.send_texture_error(e); - graphics_state - .texture_loader - .get("placeholder_tile_texture") - .unwrap_or_else(|| { - let placeholder_img = graphics_state.placeholder_img(); - - graphics_state.texture_loader.register_texture( - "placeholder_tile_texture", - graphics_state.render_state.device.create_texture_with_data( - &graphics_state.render_state.queue, - &wgpu::TextureDescriptor { - label: Some("placeholder_tile_texture"), - size: wgpu::Extent3d { - width: 32, - height: 32, - depth_or_array_layers: 1, - }, - dimension: wgpu::TextureDimension::D2, - mip_level_count: 1, - sample_count: 1, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::COPY_SRC - | wgpu::TextureUsages::COPY_DST - | wgpu::TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }, - wgpu::util::TextureDataOrder::LayerMajor, - &itertools::iproduct!(0..32, 0..32, 0..4) - .map(|(y, x, c)| { - // Tile the placeholder image - placeholder_img.as_bytes()[(c - + (x % placeholder_img.width()) * 4 - + (y % placeholder_img.height()) - * 4 - * placeholder_img.width()) - as usize] - }) - .collect_vec(), - ), - ) - }) + graphics_state.texture_loader.placeholder_texture() }); Some(Plane::new( @@ -218,47 +175,7 @@ impl Map { .unwrap_or_else(|e| { graphics_state.send_texture_error(e); - graphics_state - .texture_loader - .get("placeholder_tile_texture") - .unwrap_or_else(|| { - let placeholder_img = graphics_state.placeholder_img(); - - graphics_state.texture_loader.register_texture( - "placeholder_tile_texture", - graphics_state.render_state.device.create_texture_with_data( - &graphics_state.render_state.queue, - &wgpu::TextureDescriptor { - label: Some("placeholder_tile_texture"), - size: wgpu::Extent3d { - width: 32, - height: 32, - depth_or_array_layers: 1, - }, - dimension: wgpu::TextureDimension::D2, - mip_level_count: 1, - sample_count: 1, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::COPY_SRC - | wgpu::TextureUsages::COPY_DST - | wgpu::TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }, - wgpu::util::TextureDataOrder::LayerMajor, - &itertools::iproduct!(0..32, 0..32, 0..4) - .map(|(y, x, c)| { - // Tile the placeholder image - placeholder_img.as_bytes()[(c - + (x % placeholder_img.width()) * 4 - + (y % placeholder_img.height()) - * 4 - * placeholder_img.width()) - as usize] - }) - .collect_vec(), - ), - ) - }) + graphics_state.texture_loader.placeholder_texture() }); Some(Plane::new( diff --git a/crates/graphics/src/primitives/tiles/atlas.rs b/crates/graphics/src/primitives/tiles/atlas.rs index 0118e949..ea4dcf45 100644 --- a/crates/graphics/src/primitives/tiles/atlas.rs +++ b/crates/graphics/src/primitives/tiles/atlas.rs @@ -60,33 +60,19 @@ impl Atlas { filesystem: &impl luminol_filesystem::FileSystem, tileset: &luminol_data::rpg::Tileset, ) -> Atlas { - let tileset_img = tileset.tileset_name.as_ref().map(|tileset_name| { - filesystem + let tileset_img = tileset.tileset_name.as_ref().and_then(|tileset_name| { + let result = filesystem .read(camino::Utf8Path::new("Graphics/Tilesets").join(tileset_name)) .and_then(|file| image::load_from_memory(&file).map_err(|e| e.into())) - .wrap_err_with(|| format!("Error loading atlas tileset {tileset_name:?}")) - .unwrap_or_else(|e| { + .wrap_err_with(|| format!("Error loading atlas tileset {tileset_name:?}")); + // we don't actually need to unwrap this to a placeholder image because we fill in the atlas texture with the placeholder image. + match result { + Ok(img) => Some(img.into_rgba8()), + Err(e) => { graphics_state.send_texture_error(e); - let width = 256; - let height = 256; - let placeholder_img = graphics_state.placeholder_img(); - image::RgbaImage::from_raw( - width, - height, - itertools::iproduct!(0..height, 0..width, 0..4) - .map(|(y, x, c)| { - // Tile the placeholder image - placeholder_img.as_bytes()[(c - + (x % placeholder_img.width()) * 4 - + (y % placeholder_img.height()) * 4 * placeholder_img.width()) - as usize] - }) - .collect_vec(), - ) - .unwrap() - .into() - }) - .to_rgba8() + None + } + } }); let tileset_height = tileset_img @@ -99,33 +85,7 @@ impl Atlas { .iter() .map(|s| { if s.is_empty() { - let blank_autotile_texture = graphics_state - .texture_loader - .get("blank_autotile_texture") - .unwrap_or_else(|| { - graphics_state.texture_loader.register_texture( - "blank_autotile_texture", - graphics_state.render_state.device.create_texture( - &wgpu::TextureDescriptor { - label: Some("blank_autotile_texture"), - size: wgpu::Extent3d { - width: AUTOTILE_FRAME_COLS * TILE_SIZE, - height: AUTOTILE_ROWS * TILE_SIZE, - depth_or_array_layers: 1, - }, - dimension: wgpu::TextureDimension::D2, - mip_level_count: 1, - sample_count: 1, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::COPY_SRC - | wgpu::TextureUsages::COPY_DST - | wgpu::TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }, - ), - ) - }); - Some(blank_autotile_texture) + Some(graphics_state.texture_loader.blank_autotile_texture()) } else { graphics_state .texture_loader @@ -193,8 +153,7 @@ impl Atlas { height = MAX_SIZE; } - let placeholder_img = graphics_state.placeholder_img(); - + let placeholder_img = graphics_state.texture_loader.placeholder_image(); let atlas_texture = graphics_state.render_state.device.create_texture_with_data( &graphics_state.render_state.queue, &wgpu::TextureDescriptor { @@ -214,6 +173,7 @@ impl Atlas { view_formats: &[], }, wgpu::util::TextureDataOrder::LayerMajor, + // we can avoid this collect_vec() by mapping a buffer and then copying that to a texture. it'd also allow us to copy everything easier too. do we want to do this? &itertools::iproduct!(0..height, 0..width, 0..4) .map(|(y, x, c)| { // Tile the placeholder image to fill the atlas diff --git a/crates/graphics/src/primitives/tiles/mod.rs b/crates/graphics/src/primitives/tiles/mod.rs index 980d2ca1..1e27d0c9 100644 --- a/crates/graphics/src/primitives/tiles/mod.rs +++ b/crates/graphics/src/primitives/tiles/mod.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Viewport}; -pub use atlas::Atlas; +pub use atlas::*; use autotiles::Autotiles; use display::Display; From 5d3027f2ab1ee4bde1661f15103db1b1371a48db Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Wed, 12 Jun 2024 22:56:24 -0700 Subject: [PATCH 13/24] Update alox-48 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b6805be..1625debd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,9 +175,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alox-48" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b95702b328de6416c5504d9d7d74438362f06ec907579d3be0f28ee94084dc" +checksum = "6934ee23d65ca3613e4656d66ab8b20082b52eafd7c84c3529c3719b820c16b8" dependencies = [ "alox-48-derive", "enum-as-inner", diff --git a/Cargo.toml b/Cargo.toml index f04a3547..a3a5e2c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,7 +82,7 @@ image = "0.24.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_yml = "0.0.10" -alox-48 = { version = "0.5.0" } +alox-48 = { version = "0.6.0" } ron = "0.8.1" rust-ini = "0.20.0" From 923068cf0c82a65eae49f0a54c036237f0c88e85 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Thu, 13 Jun 2024 02:06:08 -0700 Subject: [PATCH 14/24] Massively refactor things (still broken) - Splits viewport into a transform and screen size - Fundamentally changes how things are drawn - Ditches Arc everywhere, so now there are practically 0 multithreading primitives - Breaks things --- crates/components/src/map_view.rs | 172 ++--------- crates/components/src/tilepicker.rs | 31 +- crates/graphics/src/data/mod.rs | 3 + crates/graphics/src/data/transform.rs | 101 ++++++ crates/graphics/src/data/viewport.rs | 45 +-- crates/graphics/src/event.rs | 80 ++--- crates/graphics/src/lib.rs | 32 ++ crates/graphics/src/map.rs | 289 ++++++++---------- crates/graphics/src/plane.rs | 28 +- .../graphics/src/primitives/collision/mod.rs | 71 +++-- .../src/primitives/collision/shader.rs | 6 + .../graphics/src/primitives/grid/display.rs | 56 +--- .../graphics/src/primitives/grid/instance.rs | 4 +- crates/graphics/src/primitives/grid/mod.rs | 59 ++-- crates/graphics/src/primitives/grid/shader.rs | 6 + .../src/primitives/shaders/collision.wgsl | 16 +- .../graphics/src/primitives/shaders/grid.wgsl | 35 ++- .../src/primitives/shaders/sprite.wgsl | 12 +- .../src/primitives/shaders/tilemap.wgsl | 17 +- .../src/primitives/shaders/translation.wgsl | 16 + .../graphics/src/primitives/sprite/graphic.rs | 45 +-- crates/graphics/src/primitives/sprite/mod.rs | 74 +++-- .../graphics/src/primitives/sprite/shader.rs | 6 + .../src/primitives/tiles/autotiles.rs | 26 +- .../graphics/src/primitives/tiles/display.rs | 29 +- crates/graphics/src/primitives/tiles/mod.rs | 113 ++++--- .../graphics/src/primitives/tiles/shader.rs | 6 + crates/graphics/src/tilepicker.rs | 163 +++++----- crates/ui/src/tabs/map/mod.rs | 24 +- 29 files changed, 772 insertions(+), 793 deletions(-) create mode 100644 crates/graphics/src/data/transform.rs create mode 100644 crates/graphics/src/primitives/shaders/translation.wgsl diff --git a/crates/components/src/map_view.rs b/crates/components/src/map_view.rs index 2a3df2b2..8d6d9966 100644 --- a/crates/components/src/map_view.rs +++ b/crates/components/src/map_view.rs @@ -15,7 +15,8 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use itertools::Itertools; +use luminol_graphics::Renderable; +use std::collections::HashMap; pub struct MapView { /// Toggle to display the visible region in-game. @@ -28,13 +29,13 @@ pub struct MapView { /// The first sprite is for drawing on the tilemap, /// and the second sprite is for the hover preview. - pub events: luminol_data::OptionVec<(luminol_graphics::Event, luminol_graphics::Event)>, + pub preview_events: HashMap, + pub last_events: HashMap, pub map: luminol_graphics::Map, pub selected_layer: SelectedLayer, pub selected_event_id: Option, pub cursor_pos: egui::Pos2, - pub event_enabled: bool, pub snap_to_grid: bool, /// The map coordinates of the tile being hovered over @@ -88,36 +89,6 @@ impl MapView { |x, y, passage| passages[(x, y)] = passage, ); - let atlas = update_state.graphics.atlas_loader.load_atlas( - &update_state.graphics, - update_state.filesystem, - tileset, - )?; - let events = map - .events - .iter() - .map(|(id, e)| -> color_eyre::Result<_> { - let sprite = luminol_graphics::Event::new( - &update_state.graphics, - update_state.filesystem, - e, - &atlas, - )?; - let preview_sprite = luminol_graphics::Event::new( - &update_state.graphics, - update_state.filesystem, - e, - &atlas, - )?; - - Ok(if let Some(sprite) = sprite { - preview_sprite.map(|preview_sprite| (id, (sprite, preview_sprite))) - } else { - None - }) - }) - .flatten_ok() - .try_collect()?; let map = luminol_graphics::Map::new( &update_state.graphics, update_state.filesystem, @@ -149,13 +120,13 @@ impl MapView { pan, inter_tile_pan, - events, + preview_events: HashMap::new(), + last_events: HashMap::new(), map, selected_layer: SelectedLayer::default(), selected_event_id: None, cursor_pos, - event_enabled: true, snap_to_grid: false, darken_unselected_layers: true, @@ -196,11 +167,6 @@ impl MapView { let mut response = ui.allocate_rect(canvas_rect, egui::Sense::click_and_drag()); - let min_clip = (ui.ctx().screen_rect().min - canvas_rect.min).max(Default::default()); - let max_clip = (canvas_rect.max - ui.ctx().screen_rect().max).max(Default::default()); - let clip_offset = (max_clip - min_clip) / 2.; - let canvas_rect = ui.ctx().screen_rect().intersect(canvas_rect); - self.cursor_pos = self.cursor_pos.clamp( egui::Pos2::ZERO, egui::pos2( @@ -235,6 +201,10 @@ impl MapView { self.previous_scale = self.scale; let grid_inner_thickness = if self.scale >= 50. { 1. } else { 0. }; + self.map + .grid + .display + .set_inner_thickness(&graphics_state.render_state, grid_inner_thickness); let ctrl_drag = ui.input(|i| { if is_focused { @@ -314,36 +284,20 @@ impl MapView { max: canvas_pos + pos, }; - let proj_center_x = width2 * 32. - (self.pan.x + clip_offset.x) / scale; - let proj_center_y = height2 * 32. - (self.pan.y + clip_offset.y) / scale; - let proj_width2 = canvas_rect.width() / scale / 2.; - let proj_height2 = canvas_rect.height() / scale / 2.; - let graphics_state = graphics_state.clone(); - self.map.set_proj( - &graphics_state.render_state, - glam::Mat4::orthographic_rh( - proj_center_x - proj_width2, - proj_center_x + proj_width2, - proj_center_y + proj_height2, - proj_center_y - proj_height2, - -1., - 1., - ), - ); - self.map.paint( - graphics_state.clone(), - ui.painter(), - match self.selected_layer { - SelectedLayer::Events => None, - SelectedLayer::Tiles(selected_layer) if self.darken_unselected_layers => { - Some(selected_layer) - } - SelectedLayer::Tiles(_) => None, - }, - canvas_rect, - ); + self.map.tiles.selected_layer = match self.selected_layer { + SelectedLayer::Events => None, + SelectedLayer::Tiles(selected_layer) if self.darken_unselected_layers => { + Some(selected_layer) + } + SelectedLayer::Tiles(_) => None, + }; + let painter = luminol_graphics::Painter::new(self.map.prepare(&graphics_state)); + ui.painter() + .add(luminol_egui_wgpu::Callback::new_paint_callback( + map_rect, painter, + )); ui.painter().rect_stroke( map_rect, @@ -373,25 +327,26 @@ impl MapView { ) .intersect(map_rect); - if !self.event_enabled || !matches!(self.selected_layer, SelectedLayer::Events) { + if !self.map.event_enabled || !matches!(self.selected_layer, SelectedLayer::Events) { self.selected_event_id = None; } self.selected_event_is_hovered = false; - if self.event_enabled { + if self.map.event_enabled { let mut selected_event = None; let mut selected_event_rect = None; for (_, event) in map.events.iter() { - let sprites = self.events.get(event.id); - let event_size = sprites - .map(|e| e.0.sprite_size) + let sprite = self.map.events.get_mut(event.id); + let event_size = sprite + .as_ref() + .map(|e| e.sprite_size) .unwrap_or(egui::vec2(32., 32.)); let scaled_event_size = event_size * scale; // Darken the graphic if required - if let Some((sprite, _)) = sprites { - sprite.sprite().graphic.set_opacity_multiplier( + if let Some(sprite) = sprite { + sprite.sprite.graphic.set_opacity_multiplier( &graphics_state.render_state, if self.darken_unselected_layers && !matches!(self.selected_layer, SelectedLayer::Events) @@ -412,25 +367,6 @@ impl MapView { scaled_event_size, ); - if let Some((sprite, _)) = sprites { - if canvas_rect.intersects(box_rect) { - let x = event.x as f32 * 32. + (32. - event_size.x) / 2.; - let y = event.y as f32 * 32. + (32. - event_size.y); - sprite.set_proj( - &graphics_state.render_state, - glam::Mat4::orthographic_rh( - proj_center_x - proj_width2 - x, - proj_center_x + proj_width2 - x, - proj_center_y + proj_height2 - y, - proj_center_y - proj_height2 - y, - -1., - 1., - ), - ); - sprite.paint(graphics_state.clone(), ui.painter(), canvas_rect); - } - } - if matches!(self.selected_layer, SelectedLayer::Events) && ui.input(|i| !i.modifiers.shift) { @@ -471,39 +407,11 @@ impl MapView { response = response.on_hover_ui_at_pointer(|ui| { ui.label(format!("Event {:0>3}: {:?}", event.id, event.name)); - let (response, painter) = ui.allocate_painter( + let (response, _painter) = ui.allocate_painter( event_size * ui.ctx().pixels_per_point(), egui::Sense::click(), ); - if let Some((_, preview_sprite)) = sprites { - if response.rect.is_positive() { - let clipped_rect = - ui.ctx().screen_rect().intersect(response.rect); - let proj_rect = egui::Rect::from_min_size( - (ui.ctx().screen_rect().min - response.rect.min) - .max(Default::default()) - .to_pos2(), - preview_sprite.sprite_size * clipped_rect.size() - / response.rect.size(), - ); - preview_sprite.set_proj( - &graphics_state.render_state, - glam::Mat4::orthographic_rh( - proj_rect.left(), - proj_rect.right(), - proj_rect.bottom(), - proj_rect.top(), - -1., - 1., - ), - ); - preview_sprite.paint( - graphics_state.clone(), - &painter, - clipped_rect, - ); - } - } + match self.selected_event_id { Some(id) if id == event.id => ui.painter().rect_stroke( response.rect, @@ -578,14 +486,6 @@ impl MapView { self.selected_event_id = selected_event.map(|e| e.id); - // Draw the fog and collision layers - self.map.paint_overlay( - graphics_state.clone(), - ui.painter(), - grid_inner_thickness, - canvas_rect, - ); - // Draw white rectangles on the border of all events while let Some(rect) = self.event_rects.pop() { ui.painter() @@ -606,14 +506,6 @@ impl MapView { } } } - } else { - // Draw the fog and collision layers - self.map.paint_overlay( - graphics_state.clone(), - ui.painter(), - grid_inner_thickness, - canvas_rect, - ); } // FIXME: If we want to be fast, we should be rendering all the tile ids to a texture once and then just rendering that texture here diff --git a/crates/components/src/tilepicker.rs b/crates/components/src/tilepicker.rs index f0d3c2b7..9bba9d68 100644 --- a/crates/components/src/tilepicker.rs +++ b/crates/components/src/tilepicker.rs @@ -15,6 +15,8 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . +use luminol_graphics::Renderable; + pub struct Tilepicker { pub selected_tiles_left: i16, pub selected_tiles_top: i16, @@ -150,10 +152,8 @@ impl Tilepicker { ) -> egui::Response { self.brush_random = update_state.toolbar.brush_random != ui.input(|i| i.modifiers.alt); - let graphics_state = update_state.graphics.clone(); - let (canvas_rect, response) = ui.allocate_exact_size( - egui::vec2(256., self.view.atlas().tileset_height as f32 + 32.), + egui::vec2(256., self.view.atlas.tileset_height as f32 + 32.), egui::Sense::click_and_drag(), ); @@ -163,20 +163,17 @@ impl Tilepicker { .intersect(scroll_rect.translate(canvas_rect.min.to_vec2())); let scroll_rect = absolute_scroll_rect.translate(-canvas_rect.min.to_vec2()); - self.view.set_proj( - &graphics_state.render_state, - glam::Mat4::orthographic_rh( - scroll_rect.left(), - scroll_rect.right(), - scroll_rect.bottom(), - scroll_rect.top(), - -1., - 1., - ), + self.view.set_position( + &update_state.graphics.render_state, + glam::vec2(scroll_rect.left(), scroll_rect.top()), ); - // FIXME: move this into graphics - self.view - .paint(graphics_state, ui.painter(), absolute_scroll_rect); + + let painter = luminol_graphics::Painter::new(self.view.prepare(&update_state.graphics)); + ui.painter() + .add(luminol_egui_wgpu::Callback::new_paint_callback( + absolute_scroll_rect, + painter, + )); let rect = egui::Rect::from_x_y_ranges( (self.selected_tiles_left * 32) as f32..=((self.selected_tiles_right + 1) * 32) as f32, @@ -201,7 +198,7 @@ impl Tilepicker { pos }; let rect = egui::Rect::from_two_pos(drag_origin, pos); - let bottom = self.view.atlas().tileset_height as i16 / 32; + let bottom = self.view.atlas.tileset_height as i16 / 32; self.selected_tiles_left = (rect.left() as i16).clamp(0, 7); self.selected_tiles_right = (rect.right() as i16).clamp(0, 7); self.selected_tiles_top = (rect.top() as i16).clamp(0, bottom); diff --git a/crates/graphics/src/data/mod.rs b/crates/graphics/src/data/mod.rs index 4e8c829e..c545c84f 100644 --- a/crates/graphics/src/data/mod.rs +++ b/crates/graphics/src/data/mod.rs @@ -30,3 +30,6 @@ pub use vertex::Vertex; mod viewport; pub use viewport::Viewport; + +mod transform; +pub use transform::Transform; diff --git a/crates/graphics/src/data/transform.rs b/crates/graphics/src/data/transform.rs new file mode 100644 index 00000000..70caf2c9 --- /dev/null +++ b/crates/graphics/src/data/transform.rs @@ -0,0 +1,101 @@ +// Copyright (C) 2024 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . +// +// Additional permission under GNU GPL version 3 section 7 +// +// If you modify this Program, or any covered work, by linking or combining +// it with Steamworks API by Valve Corporation, containing parts covered by +// terms of the Steamworks API by Valve Corporation, the licensors of this +// Program grant you additional permission to convey the resulting work. + +use wgpu::util::DeviceExt; + +use crate::{BindGroupLayoutBuilder, GraphicsState}; + +#[derive(Debug)] +pub struct Transform { + data: Data, + uniform: wgpu::Buffer, +} + +#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[derive(bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +struct Data { + position: glam::Vec2, + scale: glam::Vec2, +} + +impl Transform { + pub fn new(graphics_state: &GraphicsState, position: glam::Vec2, scale: glam::Vec2) -> Self { + let data = Data { position, scale }; + + let uniform = graphics_state.render_state.device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("transform buffer"), + contents: bytemuck::bytes_of(&data), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, + }, + ); + + Self { data, uniform } + } + + pub fn new_position(graphics_state: &GraphicsState, position: glam::Vec2) -> Self { + Self::new(graphics_state, position, glam::Vec2::ONE) + } + + pub fn unit(graphics_state: &GraphicsState) -> Self { + Self::new(graphics_state, glam::Vec2::ZERO, glam::Vec2::ONE) + } + + pub fn set_position( + &mut self, + render_state: &luminol_egui_wgpu::RenderState, + position: glam::Vec2, + ) { + self.data.position = position; + self.regen_buffer(render_state); + } + + pub fn set_scale(&mut self, render_state: &luminol_egui_wgpu::RenderState, scale: glam::Vec2) { + self.data.scale = scale; + self.regen_buffer(render_state); + } + + fn regen_buffer(&mut self, render_state: &luminol_egui_wgpu::RenderState) { + render_state + .queue + .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data)); + } + + pub fn as_buffer(&self) -> &wgpu::Buffer { + &self.uniform + } + + pub fn add_to_bind_group_layout(layout_builder: &mut BindGroupLayoutBuilder) { + layout_builder.append( + wgpu::ShaderStages::VERTEX_FRAGMENT, + wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + None, + ); + } +} diff --git a/crates/graphics/src/data/viewport.rs b/crates/graphics/src/data/viewport.rs index a8e82f70..b227b7e7 100644 --- a/crates/graphics/src/data/viewport.rs +++ b/crates/graphics/src/data/viewport.rs @@ -15,62 +15,39 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use crossbeam::atomic::AtomicCell; use wgpu::util::DeviceExt; use crate::{BindGroupLayoutBuilder, GraphicsState}; #[derive(Debug)] pub struct Viewport { - data: AtomicCell, + size: glam::Vec2, uniform: wgpu::Buffer, } impl Viewport { - pub fn new(graphics_state: &GraphicsState, width: f32, height: f32) -> Self { - Self::new_proj( - graphics_state, - glam::Mat4::orthographic_rh(0.0, width, height, 0.0, -1.0, 1.0), - ) - } - - pub fn new_proj(graphics_state: &GraphicsState, proj: glam::Mat4) -> Self { + pub fn new(graphics_state: &GraphicsState, screen_size: glam::Vec2) -> Self { let uniform = graphics_state.render_state.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { - label: Some("tilemap viewport buffer"), - contents: bytemuck::bytes_of(&proj), + label: Some("viewport buffer"), + contents: bytemuck::bytes_of(&screen_size), usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, }, ); Self { - data: AtomicCell::new(proj), + size: screen_size, uniform, } } - pub fn set_width_height( - &self, + pub fn set_size( + &mut self, render_state: &luminol_egui_wgpu::RenderState, - width: f32, - height: f32, + screen_size: glam::Vec2, ) { - self.set_proj( - render_state, - glam::Mat4::orthographic_rh(0.0, width, height, 0.0, -1.0, 1.0), - ) - } - - pub fn set_proj(&self, render_state: &luminol_egui_wgpu::RenderState, proj: glam::Mat4) { - let data = self.data.load(); - if data != proj { - self.data.store(proj); - self.regen_buffer(render_state); - } - } - - pub fn as_bytes(&self) -> [u8; std::mem::size_of::()] { - bytemuck::cast(self.data.load()) + self.size = screen_size; + self.regen_buffer(render_state); } pub fn as_buffer(&self) -> &wgpu::Buffer { @@ -80,7 +57,7 @@ impl Viewport { fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { render_state .queue - .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data.load())); + .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.size)); } pub fn add_to_bind_group_layout( diff --git a/crates/graphics/src/event.rs b/crates/graphics/src/event.rs index d90a8b64..e8634e60 100644 --- a/crates/graphics/src/event.rs +++ b/crates/graphics/src/event.rs @@ -17,43 +17,20 @@ use color_eyre::eyre::Context; -use std::sync::Arc; - -use fragile::Fragile; - -use crate::{Atlas, GraphicsState, Quad, Sprite, Viewport}; +use crate::{Atlas, GraphicsState, Quad, Renderable, Sprite, Transform, Viewport}; pub struct Event { - sprite: Arc, - viewport: Arc, + pub sprite: Sprite, pub sprite_size: egui::Vec2, } -// wgpu types are not Send + Sync on webassembly, so we use fragile to make sure we never access any wgpu resources across thread boundaries -struct Callback { - sprite: Fragile>, - graphics_state: Fragile>, -} - -impl luminol_egui_wgpu::CallbackTrait for Callback { - fn paint<'a>( - &'a self, - _info: egui::PaintCallbackInfo, - render_pass: &mut wgpu::RenderPass<'a>, - _callback_resources: &'a luminol_egui_wgpu::CallbackResources, - ) { - let sprite = self.sprite.get(); - let graphics_state = self.graphics_state.get(); - - sprite.draw(graphics_state, render_pass); - } -} - impl Event { // code smell, fix pub fn new( graphics_state: &GraphicsState, filesystem: &impl luminol_filesystem::FileSystem, + viewport: &Viewport, + transform: Transform, event: &luminol_data::rpg::Event, atlas: &Atlas, ) -> color_eyre::Result> { @@ -81,20 +58,16 @@ impl Event { return Ok(None); }; - let (quads, viewport, sprite_size) = if let Some(id) = page.graphic.tile_id { + let (quad, sprite_size) = if let Some(id) = page.graphic.tile_id { // Why does this have to be + 1? let quad = atlas.calc_quad((id + 1) as i16); - let viewport = Arc::new(Viewport::new(graphics_state, 32., 32.)); - - (quad, viewport, egui::vec2(32., 32.)) + (quad, egui::vec2(32., 32.)) } else if is_placeholder { let rect = egui::Rect::from_min_size(egui::pos2(0.0, 0.0), egui::vec2(32., 32.0)); let quad = Quad::new(rect, rect); - let viewport = Arc::new(Viewport::new(graphics_state, 32., 32.)); - - (quad, viewport, egui::vec2(32., 32.)) + (quad, egui::vec2(32., 32.)) } else { let cw = texture.width() as f32 / 4.; let ch = texture.height() as f32 / 4.; @@ -116,24 +89,22 @@ impl Event { ); let quad = Quad::new(pos, tex_coords); - let viewport = Arc::new(Viewport::new(graphics_state, cw, ch)); - - (quad, viewport, egui::vec2(cw, ch)) + (quad, egui::vec2(cw, ch)) }; - let sprite = Arc::new(Sprite::new( + let sprite = Sprite::new( graphics_state, - viewport.clone(), - quads, - texture, - page.graphic.blend_type, + quad, page.graphic.character_hue, page.graphic.opacity, - )); + page.graphic.blend_type, + &texture, + viewport, + transform, + ); Ok(Some(Self { sprite, - viewport, sprite_size, })) } @@ -141,23 +112,12 @@ impl Event { pub fn sprite(&self) -> &Sprite { &self.sprite } +} - pub fn set_proj(&self, render_state: &luminol_egui_wgpu::RenderState, proj: glam::Mat4) { - self.viewport.set_proj(render_state, proj); - } +impl Renderable for Event { + type Prepared = ::Prepared; - pub fn paint( - &self, - graphics_state: Arc, - painter: &egui::Painter, - rect: egui::Rect, - ) { - painter.add(luminol_egui_wgpu::Callback::new_paint_callback( - rect, - Callback { - sprite: Fragile::new(self.sprite.clone()), - graphics_state: Fragile::new(graphics_state), - }, - )); + fn prepare(&mut self, graphics_state: &std::sync::Arc) -> Self::Prepared { + self.sprite.prepare(graphics_state) } } diff --git a/crates/graphics/src/lib.rs b/crates/graphics/src/lib.rs index cebd8403..0d873176 100644 --- a/crates/graphics/src/lib.rs +++ b/crates/graphics/src/lib.rs @@ -101,3 +101,35 @@ impl GraphicsState { self.texture_error_rx.try_iter() } } + +pub trait Renderable { + type Prepared: Drawable; + fn prepare(&mut self, graphics_state: &std::sync::Arc) -> Self::Prepared; +} + +pub trait Drawable { + fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>); +} + +pub struct Painter { + prepared: fragile::Fragile, +} + +impl Painter { + pub fn new(prepared: T) -> Self { + Self { + prepared: fragile::Fragile::new(prepared), + } + } +} + +impl luminol_egui_wgpu::CallbackTrait for Painter { + fn paint<'a>( + &'a self, + _: egui::PaintCallbackInfo, + render_pass: &mut wgpu::RenderPass<'a>, + _: &'a luminol_egui_wgpu::CallbackResources, + ) { + self.prepared.get().draw(render_pass); + } +} diff --git a/crates/graphics/src/map.rs b/crates/graphics/src/map.rs index 25065d95..3010fc57 100644 --- a/crates/graphics/src/map.rs +++ b/crates/graphics/src/map.rs @@ -16,103 +16,28 @@ // along with Luminol. If not, see . use color_eyre::eyre::Context; +use itertools::Itertools; -use std::sync::Arc; - -use std::time::Duration; - -use fragile::Fragile; - -use crate::{Collision, GraphicsState, Grid, Plane, Tiles, Viewport}; +use crate::{ + Collision, Drawable, Event, GraphicsState, Grid, Plane, Renderable, Tiles, Transform, Viewport, +}; pub struct Map { - resources: Arc, - viewport: Arc, + pub tiles: Tiles, + pub panorama: Option, + pub fog: Option, + pub collision: Collision, + pub grid: Grid, + pub events: luminol_data::OptionVec, + + pub viewport: Viewport, ani_time: Option, pub fog_enabled: bool, pub pano_enabled: bool, pub coll_enabled: bool, pub grid_enabled: bool, - pub enabled_layers: Vec, -} - -struct Resources { - tiles: Tiles, - panorama: Option, - fog: Option, - collision: Collision, - grid: Grid, -} - -// wgpu types are not Send + Sync on webassembly, so we use fragile to make sure we never access any wgpu resources across thread boundaries -struct Callback { - resources: Fragile>, - graphics_state: Fragile>, - - pano_enabled: bool, - enabled_layers: Vec, - selected_layer: Option, -} - -struct OverlayCallback { - resources: Fragile>, - graphics_state: Fragile>, - - fog_enabled: bool, - coll_enabled: bool, - grid_enabled: bool, -} - -impl luminol_egui_wgpu::CallbackTrait for Callback { - fn paint<'a>( - &'a self, - _info: egui::PaintCallbackInfo, - render_pass: &mut wgpu::RenderPass<'a>, - _callback_resources: &'a luminol_egui_wgpu::CallbackResources, - ) { - let resources = self.resources.get(); - let graphics_state = self.graphics_state.get(); - - if self.pano_enabled { - if let Some(panorama) = &resources.panorama { - panorama.draw(graphics_state, render_pass); - } - } - - resources.tiles.draw( - graphics_state, - &self.enabled_layers, - self.selected_layer, - render_pass, - ); - } -} - -impl luminol_egui_wgpu::CallbackTrait for OverlayCallback { - fn paint<'a>( - &'a self, - info: egui::PaintCallbackInfo, - render_pass: &mut wgpu::RenderPass<'a>, - _callback_resources: &'a luminol_egui_wgpu::CallbackResources, - ) { - let resources = self.resources.get(); - let graphics_state = self.graphics_state.get(); - - if self.fog_enabled { - if let Some(fog) = &resources.fog { - fog.draw(graphics_state, render_pass); - } - } - - if self.coll_enabled { - resources.collision.draw(graphics_state, render_pass); - } - - if self.grid_enabled { - resources.grid.draw(graphics_state, &info, render_pass); - } - } + pub event_enabled: bool, } impl Map { @@ -127,20 +52,31 @@ impl Map { .atlas_loader .load_atlas(graphics_state, filesystem, tileset)?; - let viewport = Arc::new(Viewport::new( + let viewport = Viewport::new( graphics_state, - map.width as f32 * 32., - map.height as f32 * 32., - )); + glam::vec2(map.width as f32 * 32., map.height as f32 * 32.), + ); - let tiles = Tiles::new(graphics_state, viewport.clone(), atlas, &map.data); + let tiles = Tiles::new( + graphics_state, + &map.data, + &atlas, + &viewport, + Transform::unit(graphics_state), + ); let grid = Grid::new( graphics_state, - viewport.clone(), + &viewport, + Transform::unit(graphics_state), map.data.xsize() as u32, map.data.ysize() as u32, ); - let collision = Collision::new(graphics_state, viewport.clone(), passages); + let collision = Collision::new( + graphics_state, + &viewport, + Transform::unit(graphics_state), + passages, + ); let panorama = if let Some(ref panorama_name) = tileset.panorama_name { let texture = graphics_state @@ -155,8 +91,8 @@ impl Map { Some(Plane::new( graphics_state, - viewport.clone(), - texture, + &viewport, + &texture, tileset.panorama_hue, 100, luminol_data::BlendMode::Normal, @@ -180,8 +116,8 @@ impl Map { Some(Plane::new( graphics_state, - viewport.clone(), - texture, + &viewport, + &texture, tileset.fog_hue, tileset.fog_zoom, tileset.fog_blend_type, @@ -193,14 +129,33 @@ impl Map { None }; + let events = map + .events + .iter() + .map(|(id, event)| { + Event::new( + graphics_state, + filesystem, + &viewport, + Transform::new_position( + graphics_state, + glam::vec2(event.x as f32 * 32., event.y as f32 * 32.), + ), + event, + &atlas, + ) + .map(|opt_e| opt_e.map(|e| (id, e))) + }) + .flatten_ok() + .try_collect()?; + Ok(Self { - resources: std::sync::Arc::new(Resources { - tiles, - panorama, - fog, - collision, - grid, - }), + tiles, + panorama, + fog, + collision, + grid, + events, viewport, ani_time: None, @@ -209,7 +164,7 @@ impl Map { pano_enabled: true, coll_enabled: false, grid_enabled: true, - enabled_layers: vec![true; map.data.zsize()], + event_enabled: true, }) } @@ -219,9 +174,7 @@ impl Map { tile_id: i16, position: (usize, usize, usize), ) { - self.resources - .tiles - .set_tile(render_state, tile_id, position); + self.tiles.set_tile(render_state, tile_id, position); } pub fn set_passage( @@ -230,74 +183,84 @@ impl Map { passage: i16, position: (usize, usize), ) { - self.resources - .collision - .set_passage(render_state, passage, position); + self.collision.set_passage(render_state, passage, position); } - pub fn set_proj(&self, render_state: &luminol_egui_wgpu::RenderState, proj: glam::Mat4) { - self.viewport.set_proj(render_state, proj); - } - - pub fn paint( - &mut self, - graphics_state: Arc, - painter: &egui::Painter, - selected_layer: Option, - rect: egui::Rect, - ) { - let time = painter.ctx().input(|i| i.time); + pub fn update_animation(&mut self, render_state: &luminol_egui_wgpu::RenderState, time: f64) { if let Some(ani_time) = self.ani_time { if time - ani_time >= 16. / 60. { self.ani_time = Some(time); - self.resources - .tiles - .autotiles - .inc_ani_index(&graphics_state.render_state); + self.tiles.autotiles.inc_ani_index(render_state); } } else { self.ani_time = Some(time); } + } +} - painter - .ctx() - .request_repaint_after(Duration::from_secs_f64(16. / 60.)); +pub struct Prepared { + tiles: ::Prepared, + panorama: Option<::Prepared>, + fog: Option<::Prepared>, + collision: Option<::Prepared>, + events: Vec<::Prepared>, +} - painter.add(luminol_egui_wgpu::Callback::new_paint_callback( - rect, - Callback { - resources: Fragile::new(self.resources.clone()), - graphics_state: Fragile::new(graphics_state), +impl Renderable for Map { + type Prepared = Prepared; + + fn prepare(&mut self, graphics_state: &std::sync::Arc) -> Self::Prepared { + let tiles = self.tiles.prepare(graphics_state); + let panorama = self + .panorama + .as_mut() + .filter(|_| self.pano_enabled) + .map(|pano| pano.prepare(graphics_state)); + let fog = self + .fog + .as_mut() + .filter(|_| self.fog_enabled) + .map(|fog| fog.prepare(graphics_state)); + let collision = self + .coll_enabled + .then(|| self.collision.prepare(graphics_state)); + let events = if self.event_enabled { + self.events + .iter_mut() + .map(|(_, event)| event.prepare(graphics_state)) + .collect() + } else { + vec![] + }; - pano_enabled: self.pano_enabled, - enabled_layers: self.enabled_layers.clone(), - selected_layer, - }, - )); + Prepared { + tiles, + panorama, + fog, + collision, + events, + } } +} - pub fn paint_overlay( - &mut self, - graphics_state: Arc, - painter: &egui::Painter, - grid_inner_thickness: f32, - rect: egui::Rect, - ) { - self.resources - .grid - .display - .set_inner_thickness(&graphics_state.render_state, grid_inner_thickness); +impl Drawable for Prepared { + fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>) { + self.tiles.draw(render_pass); - painter.add(luminol_egui_wgpu::Callback::new_paint_callback( - rect, - OverlayCallback { - resources: Fragile::new(self.resources.clone()), - graphics_state: Fragile::new(graphics_state), + if let Some(ref pano) = self.panorama { + pano.draw(render_pass); + } + + for event in &self.events { + event.draw(render_pass); + } - fog_enabled: self.fog_enabled, - coll_enabled: self.coll_enabled, - grid_enabled: self.grid_enabled, - }, - )); + if let Some(ref fog) = self.fog { + fog.draw(render_pass); + } + + if let Some(ref collision) = self.collision { + collision.draw(render_pass); + } } } diff --git a/crates/graphics/src/plane.rs b/crates/graphics/src/plane.rs index 660ab69b..69b948f1 100644 --- a/crates/graphics/src/plane.rs +++ b/crates/graphics/src/plane.rs @@ -15,11 +15,10 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use crate::{GraphicsState, Quad, Sprite, Texture, Viewport}; -use std::sync::Arc; +use crate::{GraphicsState, Quad, Renderable, Sprite, Texture, Transform, Viewport}; pub struct Plane { - sprite: Sprite, + pub sprite: Sprite, } impl Plane { @@ -27,8 +26,8 @@ impl Plane { #[allow(clippy::too_many_arguments)] pub fn new( graphics_state: &GraphicsState, - viewport: Arc, - texture: Arc, + viewport: &Viewport, + texture: &Texture, hue: i32, zoom: i32, blend_mode: luminol_data::BlendMode, @@ -52,22 +51,23 @@ impl Plane { let sprite = Sprite::new( graphics_state, - viewport, quad, - texture, - blend_mode, hue, opacity, + blend_mode, + texture, + viewport, + Transform::unit(graphics_state), ); Self { sprite } } +} + +impl Renderable for Plane { + type Prepared = ::Prepared; - pub fn draw<'rpass>( - &'rpass self, - graphics_state: &'rpass GraphicsState, - render_pass: &mut wgpu::RenderPass<'rpass>, - ) { - self.sprite.draw(graphics_state, render_pass); + fn prepare(&mut self, graphics_state: &std::sync::Arc) -> Self::Prepared { + self.sprite.prepare(graphics_state) } } diff --git a/crates/graphics/src/primitives/collision/mod.rs b/crates/graphics/src/primitives/collision/mod.rs index 7a330917..ea3ff51d 100644 --- a/crates/graphics/src/primitives/collision/mod.rs +++ b/crates/graphics/src/primitives/collision/mod.rs @@ -17,7 +17,10 @@ use std::sync::Arc; -use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Viewport}; +use crate::{ + BindGroupBuilder, BindGroupLayoutBuilder, Drawable, GraphicsState, Renderable, Transform, + Viewport, +}; use instance::Instances; use itertools::Itertools; @@ -27,9 +30,10 @@ pub(crate) mod shader; #[derive(Debug)] pub struct Collision { - pub instances: Instances, - pub viewport: Arc, - pub bind_group: wgpu::BindGroup, + pub transform: Transform, + // in an Arc so we can use it in rendering + instances: Arc, + bind_group: Arc, } #[derive(Debug, Clone)] @@ -45,13 +49,15 @@ pub enum CollisionType { impl Collision { pub fn new( graphics_state: &GraphicsState, - viewport: Arc, + viewport: &Viewport, + transform: Transform, passages: &luminol_data::Table2, ) -> Self { let instances = Instances::new(&graphics_state.render_state, passages); let mut bind_group_builder = BindGroupBuilder::new(); bind_group_builder.append_buffer(viewport.as_buffer()); + bind_group_builder.append_buffer(transform.as_buffer()); let bind_group = bind_group_builder.build( &graphics_state.render_state.device, Some("collision bind group"), @@ -59,9 +65,9 @@ impl Collision { ); Self { - instances, - viewport, - bind_group, + transform, + instances: Arc::new(instances), + bind_group: Arc::new(bind_group), } } @@ -74,20 +80,6 @@ impl Collision { self.instances.set_passage(render_state, passage, position) } - pub fn draw<'rpass>( - &'rpass self, - graphics_state: &'rpass GraphicsState, - render_pass: &mut wgpu::RenderPass<'rpass>, - ) { - render_pass.push_debug_group("tilemap collision renderer"); - render_pass.set_pipeline(&graphics_state.pipelines.collision); - - render_pass.set_bind_group(0, &self.bind_group, &[]); - - self.instances.draw(render_pass); - render_pass.pop_debug_group(); - } - /// Determines the passage values for every position on the map, running `f(x, y, passage)` for /// every position. /// @@ -188,12 +180,47 @@ impl Collision { } } +pub struct Prepared { + bind_group: Arc, + instances: Arc, + graphics_state: Arc, +} + +impl Renderable for Collision { + type Prepared = Prepared; + + fn prepare(&mut self, graphics_state: &Arc) -> Self::Prepared { + let bind_group = Arc::clone(&self.bind_group); + let graphics_state = Arc::clone(graphics_state); + let instances = Arc::clone(&self.instances); + + Prepared { + bind_group, + instances, + graphics_state, + } + } +} + +impl Drawable for Prepared { + fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>) { + render_pass.push_debug_group("tilemap collision renderer"); + render_pass.set_pipeline(&self.graphics_state.pipelines.collision); + + render_pass.set_bind_group(0, &self.bind_group, &[]); + + self.instances.draw(render_pass); + render_pass.pop_debug_group(); + } +} + pub fn create_bind_group_layout( render_state: &luminol_egui_wgpu::RenderState, ) -> wgpu::BindGroupLayout { let mut builder = BindGroupLayoutBuilder::new(); Viewport::add_to_bind_group_layout(&mut builder); + Transform::add_to_bind_group_layout(&mut builder); builder.build(&render_state.device, Some("collision bind group layout")) } diff --git a/crates/graphics/src/primitives/collision/shader.rs b/crates/graphics/src/primitives/collision/shader.rs index da1ccbd9..8b4bbf7f 100644 --- a/crates/graphics/src/primitives/collision/shader.rs +++ b/crates/graphics/src/primitives/collision/shader.rs @@ -22,6 +22,12 @@ pub fn create_render_pipeline( render_state: &luminol_egui_wgpu::RenderState, bind_group_layouts: &crate::primitives::BindGroupLayouts, ) -> Result { + composer.add_composable_module(naga_oil::compose::ComposableModuleDescriptor { + source: include_str!("../shaders/translation.wgsl"), + file_path: "translation.wgsl", + ..Default::default() + })?; + let module = composer.make_naga_module(naga_oil::compose::NagaModuleDescriptor { source: include_str!("../shaders/collision.wgsl"), file_path: "collision.wgsl", diff --git a/crates/graphics/src/primitives/grid/display.rs b/crates/graphics/src/primitives/grid/display.rs index 18af0ffe..fe2bdeaa 100644 --- a/crates/graphics/src/primitives/grid/display.rs +++ b/crates/graphics/src/primitives/grid/display.rs @@ -15,53 +15,41 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use crossbeam::atomic::AtomicCell; use wgpu::util::DeviceExt; use crate::{BindGroupLayoutBuilder, GraphicsState}; #[derive(Debug)] pub struct Display { - data: AtomicCell, + data: Data, uniform: wgpu::Buffer, } #[repr(C, align(16))] #[derive(Copy, Clone, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] pub struct Data { - viewport_size_in_pixels: [f32; 2], pixels_per_point: f32, inner_thickness_in_points: f32, map_size: [u32; 2], - _pad: [u8; 8], } impl Display { pub fn new(graphics_state: &GraphicsState, map_width: u32, map_height: u32) -> Self { - let display = Data { - viewport_size_in_pixels: [0., 0.], + let data = Data { pixels_per_point: 1., inner_thickness_in_points: 1., map_size: [map_width, map_height], - _pad: [0; 8], }; let uniform = graphics_state.render_state.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("grid display buffer"), - contents: bytemuck::bytes_of(&display), + contents: bytemuck::bytes_of(&data), usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, }, ); - Display { - data: AtomicCell::new(display), - uniform, - } - } - - pub fn as_bytes(&self) -> [u8; std::mem::size_of::()] { - bytemuck::cast(self.data.load()) + Display { data, uniform } } pub fn as_buffer(&self) -> &wgpu::Buffer { @@ -69,40 +57,12 @@ impl Display { } pub fn set_inner_thickness( - &self, + &mut self, render_state: &luminol_egui_wgpu::RenderState, inner_thickness_in_points: f32, ) { - let data = self.data.load(); - if data.inner_thickness_in_points != inner_thickness_in_points { - self.data.store(Data { - inner_thickness_in_points, - ..data - }); - self.regen_buffer(render_state); - } - } - - pub(super) fn update_viewport_size( - &self, - render_state: &luminol_egui_wgpu::RenderState, - info: &egui::PaintCallbackInfo, - ) { - let viewport_size = info.viewport_in_pixels(); - let viewport_size = [ - viewport_size.width_px as f32, - viewport_size.height_px as f32, - ]; - let pixels_per_point = info.pixels_per_point.max(1.).floor(); - let data = self.data.load(); - if data.viewport_size_in_pixels != viewport_size - || data.pixels_per_point != pixels_per_point - { - self.data.store(Data { - viewport_size_in_pixels: viewport_size, - pixels_per_point, - ..data - }); + if self.data.inner_thickness_in_points != inner_thickness_in_points { + self.data.inner_thickness_in_points = inner_thickness_in_points; self.regen_buffer(render_state); } } @@ -110,7 +70,7 @@ impl Display { fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { render_state .queue - .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data.load())); + .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data)); } } diff --git a/crates/graphics/src/primitives/grid/instance.rs b/crates/graphics/src/primitives/grid/instance.rs index b63bd15a..6e2686da 100644 --- a/crates/graphics/src/primitives/grid/instance.rs +++ b/crates/graphics/src/primitives/grid/instance.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct Instances { map_size: u32, } @@ -27,7 +27,7 @@ impl Instances { } } - pub fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>) { + pub fn draw(self, render_pass: &mut wgpu::RenderPass<'_>) { render_pass.draw(0..6, 0..self.map_size); } } diff --git a/crates/graphics/src/primitives/grid/mod.rs b/crates/graphics/src/primitives/grid/mod.rs index 08411d49..97af2ea2 100644 --- a/crates/graphics/src/primitives/grid/mod.rs +++ b/crates/graphics/src/primitives/grid/mod.rs @@ -17,7 +17,10 @@ use std::sync::Arc; -use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Viewport}; +use crate::{ + BindGroupBuilder, BindGroupLayoutBuilder, Drawable, GraphicsState, Renderable, Transform, + Viewport, +}; use display::Display; use instance::Instances; @@ -30,15 +33,16 @@ pub(crate) mod shader; pub struct Grid { pub instances: Instances, pub display: display::Display, - pub viewport: Arc, - - pub bind_group: wgpu::BindGroup, + pub transform: Transform, + // in an Arc so we can use it in rendering + pub bind_group: Arc, } impl Grid { pub fn new( graphics_state: &GraphicsState, - viewport: Arc, + viewport: &Viewport, + transform: Transform, map_width: u32, map_height: u32, ) -> Self { @@ -47,6 +51,7 @@ impl Grid { let mut bind_group_builder = BindGroupBuilder::new(); bind_group_builder.append_buffer(viewport.as_buffer()); + bind_group_builder.append_buffer(transform.as_buffer()); bind_group_builder.append_buffer(display.as_buffer()); let bind_group = bind_group_builder.build( &graphics_state.render_state.device, @@ -57,32 +62,41 @@ impl Grid { Self { instances, display, - viewport, - bind_group, + transform, + bind_group: Arc::new(bind_group), } } +} + +pub struct Prepared { + bind_group: Arc, + instances: Instances, + graphics_state: Arc, +} + +impl Renderable for Grid { + type Prepared = Prepared; - pub fn draw<'rpass>( - &'rpass self, - graphics_state: &'rpass GraphicsState, - info: &egui::PaintCallbackInfo, - render_pass: &mut wgpu::RenderPass<'rpass>, - ) { - #[repr(C)] - #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] - struct VertexPushConstant { - viewport: [u8; 64], - display: [u8; 16], + fn prepare(&mut self, graphics_state: &Arc) -> Self::Prepared { + let bind_group = Arc::clone(&self.bind_group); + let graphics_state = Arc::clone(graphics_state); + let instances = self.instances; + + Prepared { + bind_group, + instances, + graphics_state, } + } +} +impl Drawable for Prepared { + fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>) { render_pass.push_debug_group("tilemap grid renderer"); - render_pass.set_pipeline(&graphics_state.pipelines.grid); + render_pass.set_pipeline(&self.graphics_state.pipelines.grid); render_pass.set_bind_group(0, &self.bind_group, &[]); - self.display - .update_viewport_size(&graphics_state.render_state, info); - self.instances.draw(render_pass); render_pass.pop_debug_group(); } @@ -94,6 +108,7 @@ pub fn create_bind_group_layout( let mut builder = BindGroupLayoutBuilder::new(); Viewport::add_to_bind_group_layout(&mut builder); + Transform::add_to_bind_group_layout(&mut builder); display::add_to_bind_group_layout(&mut builder); builder.build(&render_state.device, Some("grid bind group layout")) diff --git a/crates/graphics/src/primitives/grid/shader.rs b/crates/graphics/src/primitives/grid/shader.rs index fce64da4..ca64f8aa 100644 --- a/crates/graphics/src/primitives/grid/shader.rs +++ b/crates/graphics/src/primitives/grid/shader.rs @@ -20,6 +20,12 @@ pub fn create_render_pipeline( render_state: &luminol_egui_wgpu::RenderState, bind_group_layouts: &crate::primitives::BindGroupLayouts, ) -> Result { + composer.add_composable_module(naga_oil::compose::ComposableModuleDescriptor { + source: include_str!("../shaders/translation.wgsl"), + file_path: "translation.wgsl", + ..Default::default() + })?; + let shader_defs = if render_state.adapter.get_info().backend == wgpu::Backend::Gl { std::collections::HashMap::from([( "LUMINOL_BACKEND_GL".to_string(), diff --git a/crates/graphics/src/primitives/shaders/collision.wgsl b/crates/graphics/src/primitives/shaders/collision.wgsl index 0b22f22c..49134567 100644 --- a/crates/graphics/src/primitives/shaders/collision.wgsl +++ b/crates/graphics/src/primitives/shaders/collision.wgsl @@ -1,3 +1,5 @@ +#import luminol::translation as Trans // 🏳️‍⚧️ + struct InstanceInput { @location(0) tile_position: vec2, @location(1) passage: u32, @@ -7,12 +9,10 @@ struct VertexOutput { @builtin(position) clip_position: vec4, } -struct Viewport { - proj: mat4x4, -} - @group(0) @binding(0) -var viewport: Viewport; +var viewport: Trans::Viewport; +@group(0) @binding(1) +var transform: Trans::Transform; const VERTEX_POSITIONS = array( vec2f(16., 16.), @@ -51,10 +51,10 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, instance: InstanceInput) -> } var vertex_positions = VERTEX_POSITIONS; - let vertex_position = vertex_positions[vertex_index]; + let vertex_position = (vertex_positions[vertex_index] + instance.tile_position) * 32.; + let normalized_pos = Trans::translate_vertex(vertex_position, viewport, transform); - let position = viewport.proj * vec4(vertex_position + (instance.tile_position.xy * 32.), 0.0, 1.0); - out.clip_position = vec4(position.xy, 0.0, 1.0); + out.clip_position = vec4(normalized_pos, 0.0, 1.0); return out; } diff --git a/crates/graphics/src/primitives/shaders/grid.wgsl b/crates/graphics/src/primitives/shaders/grid.wgsl index 1d2a5b11..6ddcccc3 100644 --- a/crates/graphics/src/primitives/shaders/grid.wgsl +++ b/crates/graphics/src/primitives/shaders/grid.wgsl @@ -1,3 +1,5 @@ +#import luminol::translation as Trans // 🏳️‍⚧️ + struct VertexOutput { @builtin(position) clip_position: vec4, @location(0) position: vec2, @@ -6,23 +8,12 @@ struct VertexOutput { @location(1) @interpolate(flat) vertex_position: vec2, } -struct Viewport { - proj: mat4x4, -} - struct Display { - viewport_size_in_pixels: vec2, pixels_per_point: f32, inner_thickness_in_points: f32, - // we need this size(16) because in webgl, buffers cannot have a size that is not a multiple of 16. - @size(16) map_size: vec2, + map_size: vec2, } -@group(0) @binding(0) -var viewport: Viewport; -@group(0) @binding(1) -var display: Display; - // OpenGL and WebGL use the last vertex in each triangle as the provoking vertex, and // Direct3D, Metal, Vulkan and WebGPU use the first vertex in each triangle #ifdef LUMINOL_BACKEND_GL @@ -47,33 +38,41 @@ const QUAD_VERTICES: array, 6> = array, 6>( ); #endif +@group(0) @binding(0) +var viewport: Trans::Viewport; +@group(0) @binding(1) +var transform: Trans::Transform; +@group(0) @binding(2) +var display: Display; + @vertex fn vs_main(@builtin(vertex_index) vertex_index: u32, @builtin(instance_index) instance_index: u32) -> VertexOutput { var out: VertexOutput; var quad_vertices = QUAD_VERTICES; - let vertex_position = quad_vertices[vertex_index]; let tile_position = vec2( f32(instance_index % display.map_size.x), f32(instance_index / display.map_size.x) ); + let vertex_position = (quad_vertices[vertex_index] + tile_position) * 32.; + let normalized_pos = Trans::translate_vertex(vertex_position, viewport, transform); - out.position = (viewport.proj * vec4((vertex_position + tile_position) * 32., 0., 1.)).xy; - out.vertex_position = out.position; - out.clip_position = vec4(out.position, 0., 1.); + out.position = normalized_pos; + out.vertex_position = normalized_pos; + out.clip_position = vec4(normalized_pos, 0., 1.); return out; } @fragment fn fs_main(input: VertexOutput) -> @location(0) vec4 { - if display.viewport_size_in_pixels.x == 0. || display.viewport_size_in_pixels.y == 0. { + if viewport.screen_size.x == 0. || viewport.screen_size.y == 0. { discard; } var color: f32; var alpha: f32; - let diff = abs(input.position - input.vertex_position) * (display.viewport_size_in_pixels / 2.); + let diff = abs(input.position - input.vertex_position) * (viewport.screen_size / 2.); let adjusted_outer_thickness = 1.001 * display.pixels_per_point; let adjusted_inner_thickness = display.inner_thickness_in_points * adjusted_outer_thickness; diff --git a/crates/graphics/src/primitives/shaders/sprite.wgsl b/crates/graphics/src/primitives/shaders/sprite.wgsl index d09088ef..42cc4331 100644 --- a/crates/graphics/src/primitives/shaders/sprite.wgsl +++ b/crates/graphics/src/primitives/shaders/sprite.wgsl @@ -1,5 +1,6 @@ #import luminol::gamma as Gamma #import luminol::hue as Hue +#import luminol::translation as Trans // 🏳️‍⚧️ // Vertex shader struct VertexInput { @@ -12,10 +13,6 @@ struct VertexOutput { @location(0) tex_coords: vec2, } -struct Viewport { - proj: mat4x4, -} - struct Graphic { hue: f32, opacity: f32, @@ -29,8 +26,10 @@ var t_diffuse: texture_2d; var s_diffuse: sampler; @group(0) @binding(2) -var viewport: Viewport; +var viewport: Trans::Viewport; @group(0) @binding(3) +var transform: Trans::Transform; +@group(0) @binding(4) var graphic: Graphic; @vertex @@ -40,9 +39,8 @@ fn vs_main( var out: VertexOutput; out.tex_coords = model.tex_coords; - var position = viewport.proj * vec4(model.position, 0.0, 1.0); + out.clip_position = vec4(Trans::translate_vertex(model.position, viewport, transform), 0.0, 1.0); - out.clip_position = vec4(position.xy, 0.0, 1.0); return out; } diff --git a/crates/graphics/src/primitives/shaders/tilemap.wgsl b/crates/graphics/src/primitives/shaders/tilemap.wgsl index 19557ec2..5583dc70 100644 --- a/crates/graphics/src/primitives/shaders/tilemap.wgsl +++ b/crates/graphics/src/primitives/shaders/tilemap.wgsl @@ -1,4 +1,5 @@ #import luminol::gamma as Gamma +#import luminol::translation as Trans // 🏳️‍⚧️ struct InstanceInput { @location(0) tile_id: u32, @@ -11,10 +12,6 @@ struct VertexOutput { // todo: look into using multiple textures? } -struct Viewport { - proj: mat4x4, -} - struct Autotiles { frame_counts: array, 2>, animation_index: u32, @@ -32,10 +29,12 @@ struct Display { } @group(0) @binding(2) -var viewport: Viewport; +var viewport: Trans::Viewport; @group(0) @binding(3) -var autotiles: Autotiles; +var transform: Trans::Transform; @group(0) @binding(4) +var autotiles: Autotiles; +@group(0) @binding(5) var display: Display; const VERTEX_POSITIONS = array( @@ -73,10 +72,10 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, instance: InstanceInput) -> ); var vertex_positions = VERTEX_POSITIONS; - let vertex_position = vertex_positions[vertex_index]; + let vertex_position = vertex_positions[vertex_index] + (tile_position * 32.0); + let normalized_pos = Trans::translate_vertex(vertex_position, viewport, transform); - let position = viewport.proj * vec4(vertex_position + (tile_position * 32.), 0.0, 1.0); - out.clip_position = vec4(position.xy, 0.0, 1.0); // we don't set the z because we have no z buffer + out.clip_position = vec4(normalized_pos, 0.0, 1.0); // we don't set the z because we have no z buffer let is_autotile = instance.tile_id < #TOTAL_AUTOTILE_ID_AMOUNT; diff --git a/crates/graphics/src/primitives/shaders/translation.wgsl b/crates/graphics/src/primitives/shaders/translation.wgsl new file mode 100644 index 00000000..6728d041 --- /dev/null +++ b/crates/graphics/src/primitives/shaders/translation.wgsl @@ -0,0 +1,16 @@ +#define_import_path luminol::translation + +struct Transform { + position: vec2f, + scale: vec2f, +} + +struct Viewport { + screen_size: vec2f, +} + +fn translate_vertex(position: vec2f, viewport: Viewport, transform: Transform) -> vec2f { + let position_px = position * transform.scale + transform.position; + let position_norm = position_px / viewport.screen_size * 2.0 - 1.0; // convert to normalized device coordinates + return position_norm; +} \ No newline at end of file diff --git a/crates/graphics/src/primitives/sprite/graphic.rs b/crates/graphics/src/primitives/sprite/graphic.rs index 453caf34..aca6d955 100644 --- a/crates/graphics/src/primitives/sprite/graphic.rs +++ b/crates/graphics/src/primitives/sprite/graphic.rs @@ -15,14 +15,13 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use crossbeam::atomic::AtomicCell; use wgpu::util::DeviceExt; use crate::{BindGroupLayoutBuilder, GraphicsState}; #[derive(Debug)] pub struct Graphic { - data: AtomicCell, + data: Data, uniform: wgpu::Buffer, } @@ -54,64 +53,50 @@ impl Graphic { }, ); - Self { - data: AtomicCell::new(data), - uniform, - } + Self { data, uniform } } pub fn hue(&self) -> i32 { - (self.data.load().hue * 360.) as i32 + (self.data.hue * 360.) as i32 } - pub fn set_hue(&self, render_state: &luminol_egui_wgpu::RenderState, hue: i32) { + pub fn set_hue(&mut self, render_state: &luminol_egui_wgpu::RenderState, hue: i32) { let hue = (hue % 360) as f32 / 360.0; - let data = self.data.load(); - if data.hue != hue { - self.data.store(Data { hue, ..data }); + if self.data.hue != hue { + self.data.hue = hue; self.regen_buffer(render_state); } } pub fn opacity(&self) -> i32 { - (self.data.load().opacity * 255.) as i32 + (self.data.opacity * 255.) as i32 } - pub fn set_opacity(&self, render_state: &luminol_egui_wgpu::RenderState, opacity: i32) { + pub fn set_opacity(&mut self, render_state: &luminol_egui_wgpu::RenderState, opacity: i32) { let opacity = opacity as f32 / 255.0; - let data = self.data.load(); - if data.opacity != opacity { - self.data.store(Data { opacity, ..data }); + if self.data.opacity != opacity { + self.data.opacity = opacity; self.regen_buffer(render_state); } } pub fn opacity_multiplier(&self) -> f32 { - self.data.load().opacity_multiplier + self.data.opacity_multiplier } pub fn set_opacity_multiplier( - &self, + &mut self, render_state: &luminol_egui_wgpu::RenderState, opacity_multiplier: f32, ) { - let data = self.data.load(); - - if data.opacity_multiplier != opacity_multiplier { - self.data.store(Data { - opacity_multiplier, - ..data - }); + if self.data.opacity_multiplier != opacity_multiplier { + self.data.opacity_multiplier = opacity_multiplier; self.regen_buffer(render_state); } } - pub fn as_bytes(&self) -> [u8; std::mem::size_of::()] { - bytemuck::cast(self.data.load()) - } - pub fn as_buffer(&self) -> &wgpu::Buffer { &self.uniform } @@ -119,7 +104,7 @@ impl Graphic { fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { render_state .queue - .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data.load())); + .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data)); } } diff --git a/crates/graphics/src/primitives/sprite/mod.rs b/crates/graphics/src/primitives/sprite/mod.rs index 53f5718c..00cd25fb 100644 --- a/crates/graphics/src/primitives/sprite/mod.rs +++ b/crates/graphics/src/primitives/sprite/mod.rs @@ -16,31 +16,37 @@ // along with Luminol. If not, see . use std::sync::Arc; -use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Quad, Texture, Viewport}; +use crate::{ + BindGroupBuilder, BindGroupLayoutBuilder, Drawable, GraphicsState, Quad, Renderable, Texture, + Transform, Viewport, +}; pub(crate) mod graphic; pub(crate) mod shader; mod vertices; pub struct Sprite { - pub texture: Arc, pub graphic: graphic::Graphic, - pub vertices: vertices::Vertices, + pub transform: Transform, pub blend_mode: luminol_data::BlendMode, - pub viewport: Arc, - pub bind_group: wgpu::BindGroup, + // stored in an Arc so we can use it in rendering + vertices: Arc, + bind_group: Arc, } impl Sprite { + #[allow(clippy::too_many_arguments)] pub fn new( graphics_state: &GraphicsState, - viewport: Arc, quad: Quad, - texture: Arc, - blend_mode: luminol_data::BlendMode, hue: i32, opacity: i32, + blend_mode: luminol_data::BlendMode, + // arranged in order of use in bind group + texture: &Texture, + viewport: &Viewport, + transform: Transform, ) -> Self { let vertices = vertices::Vertices::from_quads(&graphics_state.render_state, &[quad], texture.size()); @@ -49,10 +55,9 @@ impl Sprite { let mut bind_group_builder = BindGroupBuilder::new(); bind_group_builder .append_texture_view(&texture.view) - .append_sampler(&graphics_state.nearest_sampler); - - bind_group_builder + .append_sampler(&graphics_state.nearest_sampler) .append_buffer(viewport.as_buffer()) + .append_buffer(transform.as_buffer()) .append_buffer(graphic.as_buffer()); let bind_group = bind_group_builder.build( @@ -62,32 +67,44 @@ impl Sprite { ); Self { - texture, graphic, - vertices, blend_mode, - viewport, + transform, - bind_group, + vertices: Arc::new(vertices), + bind_group: Arc::new(bind_group), } } +} - pub fn reupload_verts(&self, render_state: &luminol_egui_wgpu::RenderState, quads: &[Quad]) { - let vertices = Quad::into_vertices(quads, self.texture.size()); - render_state.queue.write_buffer( - &self.vertices.vertex_buffer, - 0, - bytemuck::cast_slice(&vertices), - ); +pub struct Prepared { + bind_group: Arc, + vertices: Arc, + graphics_state: Arc, + blend_mode: luminol_data::BlendMode, +} + +impl Renderable for Sprite { + type Prepared = Prepared; + + fn prepare(&mut self, graphics_state: &Arc) -> Self::Prepared { + let bind_group = Arc::clone(&self.bind_group); + let graphics_state = Arc::clone(graphics_state); + let vertices = Arc::clone(&self.vertices); + + Prepared { + bind_group, + vertices, + graphics_state, + blend_mode: self.blend_mode, + } } +} - pub fn draw<'rpass>( - &'rpass self, - graphics_state: &'rpass GraphicsState, - render_pass: &mut wgpu::RenderPass<'rpass>, - ) { +impl Drawable for Prepared { + fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>) { render_pass.push_debug_group("sprite render"); - render_pass.set_pipeline(&graphics_state.pipelines.sprites[&self.blend_mode]); + render_pass.set_pipeline(&self.graphics_state.pipelines.sprites[&self.blend_mode]); render_pass.set_bind_group(0, &self.bind_group, &[]); self.vertices.draw(render_pass); @@ -116,6 +133,7 @@ pub fn create_bind_group_layout( ); Viewport::add_to_bind_group_layout(&mut builder); + Transform::add_to_bind_group_layout(&mut builder); graphic::add_to_bind_group_layout(&mut builder); builder.build(&render_state.device, Some("sprite bind group layout")) diff --git a/crates/graphics/src/primitives/sprite/shader.rs b/crates/graphics/src/primitives/sprite/shader.rs index 51498f6a..a7369377 100644 --- a/crates/graphics/src/primitives/sprite/shader.rs +++ b/crates/graphics/src/primitives/sprite/shader.rs @@ -27,6 +27,12 @@ fn create_shader( bind_group_layouts: &BindGroupLayouts, target: wgpu::BlendState, ) -> Result { + composer.add_composable_module(naga_oil::compose::ComposableModuleDescriptor { + source: include_str!("../shaders/translation.wgsl"), + file_path: "translation.wgsl", + ..Default::default() + })?; + composer.add_composable_module(naga_oil::compose::ComposableModuleDescriptor { source: include_str!("../shaders/hue.wgsl"), file_path: "hue.wgsl", diff --git a/crates/graphics/src/primitives/tiles/autotiles.rs b/crates/graphics/src/primitives/tiles/autotiles.rs index 459a7fa7..d4d0701c 100644 --- a/crates/graphics/src/primitives/tiles/autotiles.rs +++ b/crates/graphics/src/primitives/tiles/autotiles.rs @@ -15,14 +15,13 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use crossbeam::atomic::AtomicCell; use wgpu::util::DeviceExt; use crate::{BindGroupLayoutBuilder, GraphicsState}; #[derive(Debug)] pub struct Autotiles { - data: AtomicCell, + data: Data, uniform: wgpu::Buffer, } @@ -38,7 +37,7 @@ struct Data { impl Autotiles { pub fn new(graphics_state: &GraphicsState, atlas: &super::Atlas) -> Self { - let autotiles = Data { + let data = Data { autotile_frames: atlas.autotile_frames, max_frame_count: atlas.autotile_width / super::atlas::AUTOTILE_FRAME_WIDTH, ani_index: 0, @@ -49,30 +48,19 @@ impl Autotiles { let uniform = graphics_state.render_state.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("tilemap autotile buffer"), - contents: bytemuck::bytes_of(&autotiles), + contents: bytemuck::bytes_of(&data), usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, }, ); - Autotiles { - data: AtomicCell::new(autotiles), - uniform, - } + Autotiles { data, uniform } } - pub fn inc_ani_index(&self, render_state: &luminol_egui_wgpu::RenderState) { - let data = self.data.load(); - self.data.store(Data { - ani_index: data.ani_index.wrapping_add(1), - ..data - }); + pub fn inc_ani_index(&mut self, render_state: &luminol_egui_wgpu::RenderState) { + self.data.ani_index = self.data.ani_index.wrapping_add(1); self.regen_buffer(render_state); } - pub fn as_bytes(&self) -> [u8; std::mem::size_of::()] { - bytemuck::cast(self.data.load()) - } - pub fn as_buffer(&self) -> &wgpu::Buffer { &self.uniform } @@ -80,7 +68,7 @@ impl Autotiles { fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { render_state .queue - .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data.load())); + .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data)); } } diff --git a/crates/graphics/src/primitives/tiles/display.rs b/crates/graphics/src/primitives/tiles/display.rs index a846f90f..60b46975 100644 --- a/crates/graphics/src/primitives/tiles/display.rs +++ b/crates/graphics/src/primitives/tiles/display.rs @@ -22,14 +22,13 @@ // terms of the Steamworks API by Valve Corporation, the licensors of this // Program grant you additional permission to convey the resulting work. -use parking_lot::RwLock; use wgpu::util::DeviceExt; use crate::{BindGroupLayoutBuilder, GraphicsState}; #[derive(Debug)] pub struct Display { - data: RwLock, + data: LayerData, uniform: wgpu::Buffer, } @@ -113,7 +112,7 @@ impl Display { ); Self { - data: RwLock::new(layer_data), + data: layer_data, uniform, } } @@ -122,35 +121,39 @@ impl Display { &self.uniform } - pub fn bytes_of_layer(&self, layer: usize) -> impl std::ops::Deref + '_ { - parking_lot::RwLockReadGuard::map(self.data.read(), |d| d.bytes_of_layer(layer)) + pub fn bytes_of_layer(&self, layer: usize) -> &[u8] { + self.data.bytes_of_layer(layer) } pub fn opacity(&self, layer: usize) -> f32 { - self.data.read().read_data_at(layer).opacity + self.data.read_data_at(layer).opacity } pub fn set_opacity( - &self, + &mut self, render_state: &luminol_egui_wgpu::RenderState, opacity: f32, layer: usize, ) { - let mut data = self.data.write(); - let layer_data = data.read_data_at_mut(layer); + let layer_data = self.data.read_data_at_mut(layer); if layer_data.opacity != opacity { layer_data.opacity = opacity; - self.regen_buffer(render_state, &data.data); + self.regen_buffer(render_state, &self.data.data); } } pub fn aligned_layer_size(&self) -> usize { - let data = self.data.read(); - Data::aligned_size_of(data.min_alignment_size) + Data::aligned_size_of(self.data.min_alignment_size) + } + + pub fn layer_offsets(&self) -> Vec { + (0..self.data.data.len() / self.aligned_layer_size()) + .map(|layer| self.layer_offset(layer)) + .collect() } pub fn layer_offset(&self, layer: usize) -> u32 { - self.data.read().range_of_layer(layer).start as u32 + self.data.range_of_layer(layer).start as u32 } fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState, data: &[u8]) { diff --git a/crates/graphics/src/primitives/tiles/mod.rs b/crates/graphics/src/primitives/tiles/mod.rs index 1e27d0c9..fcb50e59 100644 --- a/crates/graphics/src/primitives/tiles/mod.rs +++ b/crates/graphics/src/primitives/tiles/mod.rs @@ -17,7 +17,10 @@ use std::sync::Arc; -use crate::{BindGroupBuilder, BindGroupLayoutBuilder, GraphicsState, Viewport}; +use crate::{ + BindGroupBuilder, BindGroupLayoutBuilder, Drawable, GraphicsState, Renderable, Transform, + Viewport, +}; pub use atlas::*; @@ -34,22 +37,25 @@ pub(crate) mod shader; pub struct Tiles { pub autotiles: Autotiles, - pub atlas: Atlas, - pub instances: Instances, pub display: Display, - pub viewport: Arc, + pub transform: Transform, + pub enabled_layers: Vec, + pub selected_layer: Option, - pub bind_group: wgpu::BindGroup, + instances: Arc, + bind_group: Arc, } impl Tiles { pub fn new( graphics_state: &GraphicsState, - viewport: Arc, - atlas: Atlas, tiles: &luminol_data::Table3, + // in order of use in bind group + atlas: &Atlas, + viewport: &Viewport, + transform: Transform, ) -> Self { - let autotiles = Autotiles::new(graphics_state, &atlas); + let autotiles = Autotiles::new(graphics_state, atlas); let instances = Instances::new(&graphics_state.render_state, tiles); let display = Display::new( graphics_state, @@ -61,10 +67,9 @@ impl Tiles { let mut bind_group_builder = BindGroupBuilder::new(); bind_group_builder .append_texture_view(&atlas.atlas_texture.view) - .append_sampler(&graphics_state.nearest_sampler); - - bind_group_builder + .append_sampler(&graphics_state.nearest_sampler) .append_buffer(viewport.as_buffer()) + .append_buffer(transform.as_buffer()) .append_buffer(autotiles.as_buffer()) .append_buffer_with_size(display.as_buffer(), display.aligned_layer_size() as u64); @@ -76,12 +81,13 @@ impl Tiles { Self { autotiles, - atlas, - instances, display, + transform, + enabled_layers: vec![true; tiles.zsize()], + selected_layer: None, - bind_group, - viewport, + instances: Arc::new(instances), + bind_group: Arc::new(bind_group), } } @@ -93,42 +99,60 @@ impl Tiles { ) { self.instances.set_tile(render_state, tile_id, position) } +} - pub fn draw<'rpass>( - &'rpass self, - graphics_state: &'rpass GraphicsState, - enabled_layers: &[bool], - selected_layer: Option, - render_pass: &mut wgpu::RenderPass<'rpass>, - ) { - #[repr(C)] - #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] - struct VertexPushConstant { - viewport: [u8; 64], - autotiles: [u8; 48], - } +pub struct Prepared { + bind_group: Arc, + instances: Arc, + graphics_state: Arc, - render_pass.push_debug_group("tilemap tiles renderer"); - render_pass.set_pipeline(&graphics_state.pipelines.tiles); + layer_offsets: Vec, + enabled_layers: Vec, +} - for (layer, enabled) in enabled_layers.iter().copied().enumerate() { - let opacity = if selected_layer.is_some_and(|s| s != layer) { +impl Renderable for Tiles { + type Prepared = Prepared; + + fn prepare(&mut self, graphics_state: &Arc) -> Self::Prepared { + let bind_group = Arc::clone(&self.bind_group); + let graphics_state = Arc::clone(graphics_state); + let instances = Arc::clone(&self.instances); + + for layer in 0..self.enabled_layers.len() { + let opacity = if self.selected_layer.is_some_and(|s| s != layer) { 0.5 } else { 1.0 }; - if enabled { - self.display - .set_opacity(&graphics_state.render_state, opacity, layer); - - render_pass.set_bind_group( - 0, - &self.bind_group, - &[self.display.layer_offset(layer)], - ); - - self.instances.draw(render_pass, layer); - } + self.display + .set_opacity(&graphics_state.render_state, opacity, layer); + } + + Prepared { + bind_group, + instances, + graphics_state, + + layer_offsets: self.display.layer_offsets(), + enabled_layers: self.enabled_layers.clone(), + } + } +} + +impl Drawable for Prepared { + fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>) { + render_pass.push_debug_group("tilemap tiles renderer"); + render_pass.set_pipeline(&self.graphics_state.pipelines.tiles); + + for layer in self + .enabled_layers + .iter() + .enumerate() + .filter_map(|(layer, enabled)| enabled.then_some(layer)) + { + render_pass.set_bind_group(0, &self.bind_group, &[self.layer_offsets[layer]]); + + self.instances.draw(render_pass, layer); } render_pass.pop_debug_group(); } @@ -155,6 +179,7 @@ pub fn create_bind_group_layout( ); Viewport::add_to_bind_group_layout(&mut builder); + Transform::add_to_bind_group_layout(&mut builder); autotiles::add_to_bind_group_layout(&mut builder); display::add_to_bind_group_layout(&mut builder); diff --git a/crates/graphics/src/primitives/tiles/shader.rs b/crates/graphics/src/primitives/tiles/shader.rs index 4a79f0ac..a391f187 100644 --- a/crates/graphics/src/primitives/tiles/shader.rs +++ b/crates/graphics/src/primitives/tiles/shader.rs @@ -23,6 +23,12 @@ pub fn create_render_pipeline( render_state: &luminol_egui_wgpu::RenderState, bind_group_layouts: &BindGroupLayouts, ) -> Result { + composer.add_composable_module(naga_oil::compose::ComposableModuleDescriptor { + source: include_str!("../shaders/translation.wgsl"), + file_path: "translation.wgsl", + ..Default::default() + })?; + composer.add_composable_module(naga_oil::compose::ComposableModuleDescriptor { source: include_str!("../shaders/gamma.wgsl"), file_path: "gamma.wgsl", diff --git a/crates/graphics/src/tilepicker.rs b/crates/graphics/src/tilepicker.rs index eeda612c..fa0e3ba3 100644 --- a/crates/graphics/src/tilepicker.rs +++ b/crates/graphics/src/tilepicker.rs @@ -22,58 +22,25 @@ // terms of the Steamworks API by Valve Corporation, the licensors of this // Program grant you additional permission to convey the resulting work. -use std::{sync::Arc, time::Duration}; +use std::sync::Arc; -use fragile::Fragile; use itertools::Itertools; -use crate::{Atlas, Collision, GraphicsState, Grid, Tiles, Viewport}; +use crate::{ + Atlas, Collision, Drawable, GraphicsState, Grid, Renderable, Tiles, Transform, Viewport, +}; pub struct Tilepicker { pub coll_enabled: bool, pub grid_enabled: bool, - resources: Arc, - viewport: Arc, - ani_time: Option, -} - -struct Resources { - tiles: Tiles, - collision: Collision, - grid: Grid, -} - -struct Callback { - resources: Fragile>, - graphics_state: Fragile>, - - coll_enabled: bool, - grid_enabled: bool, -} - -impl luminol_egui_wgpu::CallbackTrait for Callback { - fn paint<'a>( - &'a self, - info: egui::PaintCallbackInfo, - render_pass: &mut wgpu::RenderPass<'a>, - _callback_resources: &'a luminol_egui_wgpu::CallbackResources, - ) { - let resources = self.resources.get(); - let graphics_state = self.graphics_state.get(); - - resources - .tiles - .draw(graphics_state, &[true], None, render_pass); + pub tiles: Tiles, + pub collision: Collision, + pub grid: Grid, + pub atlas: Atlas, - if self.coll_enabled { - resources.collision.draw(graphics_state, render_pass); - } - - if self.grid_enabled { - resources.grid.draw(graphics_state, &info, render_pass); - } - } + pub viewport: Viewport, + ani_time: Option, } impl Tilepicker { @@ -97,17 +64,23 @@ impl Tilepicker { tilepicker_data, ); - let viewport = Arc::new(Viewport::new( + let viewport = Viewport::new( graphics_state, - 256., - atlas.tileset_height as f32 + 32., - )); + glam::vec2(256., atlas.tileset_height as f32 + 32.), + ); - let tiles = Tiles::new(graphics_state, viewport.clone(), atlas, &tilepicker_data); + let tiles = Tiles::new( + graphics_state, + &tilepicker_data, + &atlas, + &viewport, + Transform::unit(graphics_state), + ); let grid = Grid::new( graphics_state, - viewport.clone(), + &viewport, + Transform::unit(graphics_state), tilepicker_data.xsize() as u32, tilepicker_data.ysize() as u32, ); @@ -128,61 +101,85 @@ impl Tilepicker { (passages.len().saturating_sub(8)).min(tileset.passages.len().saturating_sub(384)); passages.as_mut_slice()[8..8 + length] .copy_from_slice(&tileset.passages.as_slice()[384..384 + length]); - let collision = Collision::new(graphics_state, viewport.clone(), &passages); + let collision = Collision::new( + graphics_state, + &viewport, + Transform::unit(graphics_state), + &passages, + ); Ok(Self { - resources: Arc::new(Resources { - tiles, - collision, - grid, - }), + tiles, + collision, + grid, + atlas, + viewport, + coll_enabled: false, grid_enabled: false, ani_time: None, }) } - pub fn paint( - &mut self, - graphics_state: Arc, - painter: &egui::Painter, - rect: egui::Rect, - ) { - let time = painter.ctx().input(|i| i.time); + pub fn update_animation(&mut self, render_state: &luminol_egui_wgpu::RenderState, time: f64) { if let Some(ani_time) = self.ani_time { if time - ani_time >= 16. / 60. { self.ani_time = Some(time); - self.resources - .tiles - .autotiles - .inc_ani_index(&graphics_state.render_state); + self.tiles.autotiles.inc_ani_index(render_state); } } else { self.ani_time = Some(time); } + } - painter - .ctx() - .request_repaint_after(Duration::from_secs_f64(16. / 60.)); + pub fn set_position( + &mut self, + render_state: &luminol_egui_wgpu::RenderState, + position: glam::Vec2, + ) { + self.tiles.transform.set_position(render_state, position); + self.collision + .transform + .set_position(render_state, position); + self.grid.transform.set_position(render_state, position); + } +} - painter.add(luminol_egui_wgpu::Callback::new_paint_callback( - rect, - Callback { - resources: Fragile::new(self.resources.clone()), - graphics_state: Fragile::new(graphics_state), +pub struct Prepared { + tiles: ::Prepared, + collision: ::Prepared, + grid: ::Prepared, - coll_enabled: self.coll_enabled, - grid_enabled: self.grid_enabled, - }, - )); - } + coll_enabled: bool, + grid_enabled: bool, +} + +impl Renderable for Tilepicker { + type Prepared = Prepared; + + fn prepare(&mut self, graphics_state: &Arc) -> Self::Prepared { + Prepared { + tiles: self.tiles.prepare(graphics_state), + collision: self.collision.prepare(graphics_state), + grid: self.grid.prepare(graphics_state), - pub fn set_proj(&self, render_state: &luminol_egui_wgpu::RenderState, proj: glam::Mat4) { - self.viewport.set_proj(render_state, proj); + coll_enabled: self.coll_enabled, + grid_enabled: self.grid_enabled, + } } +} + +impl Drawable for Prepared { + fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>) { + self.tiles.draw(render_pass); - pub fn atlas(&self) -> &Atlas { - &self.resources.tiles.atlas + if self.coll_enabled { + self.collision.draw(render_pass); + } + + if self.grid_enabled { + self.grid.draw(render_pass); + } } } diff --git a/crates/ui/src/tabs/map/mod.rs b/crates/ui/src/tabs/map/mod.rs index f048710d..dd909734 100644 --- a/crates/ui/src/tabs/map/mod.rs +++ b/crates/ui/src/tabs/map/mod.rs @@ -108,7 +108,7 @@ enum HistoryEntry { /// Contains a deleted event and its corresponding graphic. EventDeleted { event: luminol_data::rpg::Event, - sprites: Option<(luminol_graphics::Event, luminol_graphics::Event)>, + sprite: Option, }, } @@ -245,7 +245,7 @@ impl luminol_core::Tab for Tab { ui.end_row(); for (index, layer) in - self.view.map.enabled_layers.iter_mut().enumerate() + self.view.map.tiles.enabled_layers.iter_mut().enumerate() { ui.columns(1, |columns| { columns[0].selectable_value( @@ -266,7 +266,7 @@ impl luminol_core::Tab for Tab { egui::RichText::new("Events").italics(), ); }); - ui.checkbox(&mut self.view.event_enabled, "👁"); + ui.checkbox(&mut self.view.map.event_enabled, "👁"); ui.end_row(); ui.label(egui::RichText::new("Fog").underline()); @@ -474,11 +474,11 @@ impl luminol_core::Tab for Tab { // Press delete or backspace to delete the selected event if is_delete_pressed { let event = map.events.remove(selected_event_id); - let sprites = self.view.events.try_remove(selected_event_id).ok(); + let sprite = self.view.map.events.try_remove(selected_event_id).ok(); self.push_to_history( update_state, &mut map, - HistoryEntry::EventDeleted { event, sprites }, + HistoryEntry::EventDeleted { event, sprite }, ); } @@ -598,15 +598,15 @@ impl luminol_core::Tab for Tab { Some(HistoryEntry::EventCreated(id)) => { let event = map.events.remove(id); - let sprites = self.view.events.try_remove(id).ok(); - Some(HistoryEntry::EventDeleted { event, sprites }) + let sprite = self.view.map.events.try_remove(id).ok(); + Some(HistoryEntry::EventDeleted { event, sprite }) } - Some(HistoryEntry::EventDeleted { event, sprites }) => { + Some(HistoryEntry::EventDeleted { event, sprite }) => { let id = event.id; map.events.insert(id, event); - if let Some(sprites) = sprites { - self.view.events.insert(id, sprites); + if let Some(sprite) = sprite { + self.view.map.events.insert(id, sprite); } Some(HistoryEntry::EventCreated(id)) } @@ -651,13 +651,13 @@ impl luminol_core::Tab for Tab { &tileset.passages, &tileset.priorities, &map.data, - if self.view.event_enabled { + if self.view.map.event_enabled { Some(&map.events) } else { None }, (0..map.data.zsize()) - .filter(|&i| self.view.map.enabled_layers[i]) + .filter(|&i| self.view.map.tiles.enabled_layers[i]) .rev(), |x, y, passage| { if self.passages[(x, y)] != passage { From 63f3d1b4756d10c20d211fe05951f6dae4e20920 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Thu, 13 Jun 2024 02:40:04 -0700 Subject: [PATCH 15/24] Fix event position bug --- crates/graphics/src/event.rs | 5 ++++- crates/graphics/src/map.rs | 18 ++++-------------- .../src/primitives/shaders/translation.wgsl | 2 +- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/crates/graphics/src/event.rs b/crates/graphics/src/event.rs index e8634e60..17108910 100644 --- a/crates/graphics/src/event.rs +++ b/crates/graphics/src/event.rs @@ -30,7 +30,6 @@ impl Event { graphics_state: &GraphicsState, filesystem: &impl luminol_filesystem::FileSystem, viewport: &Viewport, - transform: Transform, event: &luminol_data::rpg::Event, atlas: &Atlas, ) -> color_eyre::Result> { @@ -92,6 +91,10 @@ impl Event { (quad, egui::vec2(cw, ch)) }; + let x = event.x as f32 * 32. + (32. - sprite_size.x) / 2.; + let y = event.y as f32 * 32. + (32. - sprite_size.y); + let transform = Transform::new_position(graphics_state, glam::vec2(x, y)); + let sprite = Sprite::new( graphics_state, quad, diff --git a/crates/graphics/src/map.rs b/crates/graphics/src/map.rs index 3010fc57..789eb520 100644 --- a/crates/graphics/src/map.rs +++ b/crates/graphics/src/map.rs @@ -133,18 +133,8 @@ impl Map { .events .iter() .map(|(id, event)| { - Event::new( - graphics_state, - filesystem, - &viewport, - Transform::new_position( - graphics_state, - glam::vec2(event.x as f32 * 32., event.y as f32 * 32.), - ), - event, - &atlas, - ) - .map(|opt_e| opt_e.map(|e| (id, e))) + Event::new(graphics_state, filesystem, &viewport, event, &atlas) + .map(|opt_e| opt_e.map(|e| (id, e))) }) .flatten_ok() .try_collect()?; @@ -245,12 +235,12 @@ impl Renderable for Map { impl Drawable for Prepared { fn draw<'rpass>(&'rpass self, render_pass: &mut wgpu::RenderPass<'rpass>) { - self.tiles.draw(render_pass); - if let Some(ref pano) = self.panorama { pano.draw(render_pass); } + self.tiles.draw(render_pass); + for event in &self.events { event.draw(render_pass); } diff --git a/crates/graphics/src/primitives/shaders/translation.wgsl b/crates/graphics/src/primitives/shaders/translation.wgsl index 6728d041..ebf8a0ea 100644 --- a/crates/graphics/src/primitives/shaders/translation.wgsl +++ b/crates/graphics/src/primitives/shaders/translation.wgsl @@ -12,5 +12,5 @@ struct Viewport { fn translate_vertex(position: vec2f, viewport: Viewport, transform: Transform) -> vec2f { let position_px = position * transform.scale + transform.position; let position_norm = position_px / viewport.screen_size * 2.0 - 1.0; // convert to normalized device coordinates - return position_norm; + return vec2f(position_norm.x, -position_norm.y); // flip y-axis } \ No newline at end of file From 16c78701ac7c5538771dea81e48428aabacc65f4 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Thu, 13 Jun 2024 02:42:53 -0700 Subject: [PATCH 16/24] Actually render the grid --- crates/graphics/src/map.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/graphics/src/map.rs b/crates/graphics/src/map.rs index 789eb520..c42614af 100644 --- a/crates/graphics/src/map.rs +++ b/crates/graphics/src/map.rs @@ -193,6 +193,7 @@ pub struct Prepared { panorama: Option<::Prepared>, fog: Option<::Prepared>, collision: Option<::Prepared>, + grid: Option<::Prepared>, events: Vec<::Prepared>, } @@ -214,6 +215,7 @@ impl Renderable for Map { let collision = self .coll_enabled .then(|| self.collision.prepare(graphics_state)); + let grid = self.grid_enabled.then(|| self.grid.prepare(graphics_state)); let events = if self.event_enabled { self.events .iter_mut() @@ -228,6 +230,7 @@ impl Renderable for Map { panorama, fog, collision, + grid, events, } } @@ -252,5 +255,9 @@ impl Drawable for Prepared { if let Some(ref collision) = self.collision { collision.draw(render_pass); } + + if let Some(ref grid) = self.grid { + grid.draw(render_pass); + } } } From 8242a86921b620f221e3259c7346c1fb94ef2702 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Fri, 14 Jun 2024 20:53:55 -0700 Subject: [PATCH 17/24] Fix viewport translations --- crates/components/src/map_view.rs | 25 +++++++++++-- crates/components/src/tilepicker.rs | 8 +++- crates/graphics/src/data/viewport.rs | 37 +++++++++++++------ .../graphics/src/primitives/shaders/grid.wgsl | 4 +- .../src/primitives/shaders/translation.wgsl | 9 +++-- 5 files changed, 62 insertions(+), 21 deletions(-) diff --git a/crates/components/src/map_view.rs b/crates/components/src/map_view.rs index 8d6d9966..4fc2db2c 100644 --- a/crates/components/src/map_view.rs +++ b/crates/components/src/map_view.rs @@ -167,6 +167,11 @@ impl MapView { let mut response = ui.allocate_rect(canvas_rect, egui::Sense::click_and_drag()); + let min_clip = (ui.ctx().screen_rect().min - canvas_rect.min).max(Default::default()); + let max_clip = (canvas_rect.max - ui.ctx().screen_rect().max).max(Default::default()); + let clip_offset = (max_clip - min_clip) / 2.; + let canvas_rect = ui.ctx().screen_rect().intersect(canvas_rect); + self.cursor_pos = self.cursor_pos.clamp( egui::Pos2::ZERO, egui::pos2( @@ -284,8 +289,6 @@ impl MapView { max: canvas_pos + pos, }; - let graphics_state = graphics_state.clone(); - self.map.tiles.selected_layer = match self.selected_layer { SelectedLayer::Events => None, SelectedLayer::Tiles(selected_layer) if self.darken_unselected_layers => { @@ -293,10 +296,24 @@ impl MapView { } SelectedLayer::Tiles(_) => None, }; - let painter = luminol_graphics::Painter::new(self.map.prepare(&graphics_state)); + + // no idea why this math works (could probably be simplified) + let proj_center_x = width2 * 32. - (self.pan.x + clip_offset.x) / scale; + let proj_center_y = height2 * 32. - (self.pan.y + clip_offset.y) / scale; + let proj_width2 = canvas_rect.width() / scale / 2.; + let proj_height2 = canvas_rect.height() / scale / 2.; + self.map.viewport.set( + &graphics_state.render_state, + glam::vec2(canvas_rect.width(), canvas_rect.height()), + glam::vec2(proj_width2 - proj_center_x, proj_height2 - proj_center_y) * scale, + glam::Vec2::splat(scale), + ); + + let painter = luminol_graphics::Painter::new(self.map.prepare(graphics_state)); ui.painter() .add(luminol_egui_wgpu::Callback::new_paint_callback( - map_rect, painter, + canvas_rect, + painter, )); ui.painter().rect_stroke( diff --git a/crates/components/src/tilepicker.rs b/crates/components/src/tilepicker.rs index 9bba9d68..d635c3a5 100644 --- a/crates/components/src/tilepicker.rs +++ b/crates/components/src/tilepicker.rs @@ -165,7 +165,13 @@ impl Tilepicker { self.view.set_position( &update_state.graphics.render_state, - glam::vec2(scroll_rect.left(), scroll_rect.top()), + glam::vec2(0.0, -scroll_rect.top()), + ); + self.view.viewport.set( + &update_state.graphics.render_state, + glam::vec2(scroll_rect.width(), scroll_rect.height()), + glam::Vec2::ZERO, + glam::Vec2::ONE, ); let painter = luminol_graphics::Painter::new(self.view.prepare(&update_state.graphics)); diff --git a/crates/graphics/src/data/viewport.rs b/crates/graphics/src/data/viewport.rs index b227b7e7..eeb3d343 100644 --- a/crates/graphics/src/data/viewport.rs +++ b/crates/graphics/src/data/viewport.rs @@ -21,32 +21,47 @@ use crate::{BindGroupLayoutBuilder, GraphicsState}; #[derive(Debug)] pub struct Viewport { - size: glam::Vec2, + data: Data, uniform: wgpu::Buffer, } +#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +struct Data { + viewport_size: glam::Vec2, + viewport_translation: glam::Vec2, + viewport_scale: glam::Vec2, +} + impl Viewport { - pub fn new(graphics_state: &GraphicsState, screen_size: glam::Vec2) -> Self { + pub fn new(graphics_state: &GraphicsState, viewport_size: glam::Vec2) -> Self { + let data = Data { + viewport_size, + viewport_translation: glam::Vec2::ZERO, + viewport_scale: glam::Vec2::ONE, + }; let uniform = graphics_state.render_state.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("viewport buffer"), - contents: bytemuck::bytes_of(&screen_size), + contents: bytemuck::bytes_of(&data), usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, }, ); - Self { - size: screen_size, - uniform, - } + Self { data, uniform } } - pub fn set_size( + pub fn set( &mut self, render_state: &luminol_egui_wgpu::RenderState, - screen_size: glam::Vec2, + size: glam::Vec2, + translation: glam::Vec2, + scale: glam::Vec2, ) { - self.size = screen_size; + self.data.viewport_size = size; + self.data.viewport_translation = translation; + self.data.viewport_scale = scale; self.regen_buffer(render_state); } @@ -57,7 +72,7 @@ impl Viewport { fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { render_state .queue - .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.size)); + .write_buffer(&self.uniform, 0, bytemuck::bytes_of(&self.data)); } pub fn add_to_bind_group_layout( diff --git a/crates/graphics/src/primitives/shaders/grid.wgsl b/crates/graphics/src/primitives/shaders/grid.wgsl index 6ddcccc3..4cfa3003 100644 --- a/crates/graphics/src/primitives/shaders/grid.wgsl +++ b/crates/graphics/src/primitives/shaders/grid.wgsl @@ -65,14 +65,14 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, @builtin(instance_index) in @fragment fn fs_main(input: VertexOutput) -> @location(0) vec4 { - if viewport.screen_size.x == 0. || viewport.screen_size.y == 0. { + if viewport.viewport_size.x == 0. || viewport.viewport_size.y == 0. { discard; } var color: f32; var alpha: f32; - let diff = abs(input.position - input.vertex_position) * (viewport.screen_size / 2.); + let diff = abs(input.position - input.vertex_position) * (viewport.viewport_size / 2.); let adjusted_outer_thickness = 1.001 * display.pixels_per_point; let adjusted_inner_thickness = display.inner_thickness_in_points * adjusted_outer_thickness; diff --git a/crates/graphics/src/primitives/shaders/translation.wgsl b/crates/graphics/src/primitives/shaders/translation.wgsl index ebf8a0ea..2984b60f 100644 --- a/crates/graphics/src/primitives/shaders/translation.wgsl +++ b/crates/graphics/src/primitives/shaders/translation.wgsl @@ -6,11 +6,14 @@ struct Transform { } struct Viewport { - screen_size: vec2f, + viewport_size: vec2f, // size of the viewport in pixels + viewport_translation: vec2f, // additional translation in pixels + viewport_scale: vec2f, // additional scale in pixels } fn translate_vertex(position: vec2f, viewport: Viewport, transform: Transform) -> vec2f { - let position_px = position * transform.scale + transform.position; - let position_norm = position_px / viewport.screen_size * 2.0 - 1.0; // convert to normalized device coordinates + let position_vp = position * transform.scale + transform.position; + let position_px = position_vp * viewport.viewport_scale + viewport.viewport_translation; + let position_norm = position_px / viewport.viewport_size * 2.0 - 1.0; return vec2f(position_norm.x, -position_norm.y); // flip y-axis } \ No newline at end of file From 19c5317624ece30d5db2074f0219a4678939f960 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Fri, 14 Jun 2024 21:11:52 -0700 Subject: [PATCH 18/24] Fix autotile animation --- crates/components/src/map_view.rs | 5 +++++ crates/components/src/tilepicker.rs | 2 ++ 2 files changed, 7 insertions(+) diff --git a/crates/components/src/map_view.rs b/crates/components/src/map_view.rs index 4fc2db2c..b93487d3 100644 --- a/crates/components/src/map_view.rs +++ b/crates/components/src/map_view.rs @@ -309,6 +309,11 @@ impl MapView { glam::Vec2::splat(scale), ); + self.map + .update_animation(&graphics_state.render_state, ui.input(|i| i.time)); + ui.ctx() + .request_repaint_after(std::time::Duration::from_secs_f32(16. / 60.)); + let painter = luminol_graphics::Painter::new(self.map.prepare(graphics_state)); ui.painter() .add(luminol_egui_wgpu::Callback::new_paint_callback( diff --git a/crates/components/src/tilepicker.rs b/crates/components/src/tilepicker.rs index d635c3a5..d64d5301 100644 --- a/crates/components/src/tilepicker.rs +++ b/crates/components/src/tilepicker.rs @@ -173,6 +173,8 @@ impl Tilepicker { glam::Vec2::ZERO, glam::Vec2::ONE, ); + self.view + .update_animation(&update_state.graphics.render_state, ui.input(|i| i.time)); let painter = luminol_graphics::Painter::new(self.view.prepare(&update_state.graphics)); ui.painter() From 59f6cfc96aa5a80b6fa53b4fb1139fd7990ee6bc Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Fri, 14 Jun 2024 21:18:56 -0700 Subject: [PATCH 19/24] Fix some grid issues --- crates/components/src/map_view.rs | 4 ++++ crates/components/src/tilepicker.rs | 5 +++++ crates/graphics/src/primitives/grid/display.rs | 11 +++++++++++ 3 files changed, 20 insertions(+) diff --git a/crates/components/src/map_view.rs b/crates/components/src/map_view.rs index b93487d3..5c9bb953 100644 --- a/crates/components/src/map_view.rs +++ b/crates/components/src/map_view.rs @@ -210,6 +210,10 @@ impl MapView { .grid .display .set_inner_thickness(&graphics_state.render_state, grid_inner_thickness); + self.map + .grid + .display + .set_pixels_per_point(&graphics_state.render_state, ui.ctx().pixels_per_point()); let ctrl_drag = ui.input(|i| { if is_focused { diff --git a/crates/components/src/tilepicker.rs b/crates/components/src/tilepicker.rs index d64d5301..81775c43 100644 --- a/crates/components/src/tilepicker.rs +++ b/crates/components/src/tilepicker.rs @@ -163,6 +163,11 @@ impl Tilepicker { .intersect(scroll_rect.translate(canvas_rect.min.to_vec2())); let scroll_rect = absolute_scroll_rect.translate(-canvas_rect.min.to_vec2()); + self.view.grid.display.set_pixels_per_point( + &update_state.graphics.render_state, + ui.ctx().pixels_per_point(), + ); + self.view.set_position( &update_state.graphics.render_state, glam::vec2(0.0, -scroll_rect.top()), diff --git a/crates/graphics/src/primitives/grid/display.rs b/crates/graphics/src/primitives/grid/display.rs index fe2bdeaa..dab357e3 100644 --- a/crates/graphics/src/primitives/grid/display.rs +++ b/crates/graphics/src/primitives/grid/display.rs @@ -67,6 +67,17 @@ impl Display { } } + pub fn set_pixels_per_point( + &mut self, + render_state: &luminol_egui_wgpu::RenderState, + pixels_per_point: f32, + ) { + if self.data.pixels_per_point != pixels_per_point { + self.data.pixels_per_point = pixels_per_point; + self.regen_buffer(render_state); + } + } + fn regen_buffer(&self, render_state: &luminol_egui_wgpu::RenderState) { render_state .queue From 1b9da349d87decb7f9f3a4b2d588a0cd9a77cb64 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Fri, 14 Jun 2024 21:45:41 -0700 Subject: [PATCH 20/24] Reimplement event preview --- crates/components/src/map_view.rs | 63 +++++++++++++++---- crates/graphics/src/data/viewport.rs | 5 ++ crates/graphics/src/event.rs | 94 +++++++++++++++++++++++++++- crates/graphics/src/map.rs | 7 ++- crates/ui/src/tabs/map/mod.rs | 2 +- 5 files changed, 154 insertions(+), 17 deletions(-) diff --git a/crates/components/src/map_view.rs b/crates/components/src/map_view.rs index 5c9bb953..84986005 100644 --- a/crates/components/src/map_view.rs +++ b/crates/components/src/map_view.rs @@ -29,8 +29,8 @@ pub struct MapView { /// The first sprite is for drawing on the tilemap, /// and the second sprite is for the hover preview. - pub preview_events: HashMap, - pub last_events: HashMap, + preview_events: HashMap, + last_events: HashMap, pub map: luminol_graphics::Map, pub selected_layer: SelectedLayer, @@ -151,7 +151,7 @@ impl MapView { pub fn ui( &mut self, ui: &mut egui::Ui, - graphics_state: &std::sync::Arc, + update_state: &luminol_core::UpdateState<'_>, map: &luminol_data::rpg::Map, tilepicker: &crate::Tilepicker, dragging_event: bool, @@ -209,11 +209,11 @@ impl MapView { self.map .grid .display - .set_inner_thickness(&graphics_state.render_state, grid_inner_thickness); - self.map - .grid - .display - .set_pixels_per_point(&graphics_state.render_state, ui.ctx().pixels_per_point()); + .set_inner_thickness(&update_state.graphics.render_state, grid_inner_thickness); + self.map.grid.display.set_pixels_per_point( + &update_state.graphics.render_state, + ui.ctx().pixels_per_point(), + ); let ctrl_drag = ui.input(|i| { if is_focused { @@ -307,18 +307,18 @@ impl MapView { let proj_width2 = canvas_rect.width() / scale / 2.; let proj_height2 = canvas_rect.height() / scale / 2.; self.map.viewport.set( - &graphics_state.render_state, + &update_state.graphics.render_state, glam::vec2(canvas_rect.width(), canvas_rect.height()), glam::vec2(proj_width2 - proj_center_x, proj_height2 - proj_center_y) * scale, glam::Vec2::splat(scale), ); self.map - .update_animation(&graphics_state.render_state, ui.input(|i| i.time)); + .update_animation(&update_state.graphics.render_state, ui.input(|i| i.time)); ui.ctx() .request_repaint_after(std::time::Duration::from_secs_f32(16. / 60.)); - let painter = luminol_graphics::Painter::new(self.map.prepare(graphics_state)); + let painter = luminol_graphics::Painter::new(self.map.prepare(&update_state.graphics)); ui.painter() .add(luminol_egui_wgpu::Callback::new_paint_callback( canvas_rect, @@ -364,16 +364,18 @@ impl MapView { for (_, event) in map.events.iter() { let sprite = self.map.events.get_mut(event.id); + let has_sprite = sprite.is_some(); let event_size = sprite .as_ref() .map(|e| e.sprite_size) .unwrap_or(egui::vec2(32., 32.)); let scaled_event_size = event_size * scale; - // Darken the graphic if required + // update relevant properties if let Some(sprite) = sprite { + sprite.set_position(&update_state.graphics.render_state, event.x, event.y); sprite.sprite.graphic.set_opacity_multiplier( - &graphics_state.render_state, + &update_state.graphics.render_state, if self.darken_unselected_layers && !matches!(self.selected_layer, SelectedLayer::Events) { @@ -438,6 +440,41 @@ impl MapView { egui::Sense::click(), ); + if has_sprite { + let mut preview_sprite = + self.last_events.remove(&event.id).unwrap_or_else(|| { + let viewport = luminol_graphics::Viewport::new( + &update_state.graphics, + glam::vec2(event_size.x, event_size.y), + ); + luminol_graphics::Event::new_standalone( + &update_state.graphics, + update_state.filesystem, + &viewport, + event, + &self.map.atlas, + ) + .unwrap() + .unwrap() + }); + + if response.rect.is_positive() { + let clipped_rect = + ui.ctx().screen_rect().intersect(response.rect); + let painter = luminol_graphics::Painter::new( + preview_sprite.prepare(&update_state.graphics), + ); + ui.painter().add( + luminol_egui_wgpu::Callback::new_paint_callback( + clipped_rect, + painter, + ), + ); + } + + self.preview_events.insert(event.id, preview_sprite); + } + match self.selected_event_id { Some(id) if id == event.id => ui.painter().rect_stroke( response.rect, diff --git a/crates/graphics/src/data/viewport.rs b/crates/graphics/src/data/viewport.rs index eeb3d343..7b1476ee 100644 --- a/crates/graphics/src/data/viewport.rs +++ b/crates/graphics/src/data/viewport.rs @@ -52,6 +52,11 @@ impl Viewport { Self { data, uniform } } + pub fn set_size(&mut self, render_state: &luminol_egui_wgpu::RenderState, size: glam::Vec2) { + self.data.viewport_size = size; + self.regen_buffer(render_state); + } + pub fn set( &mut self, render_state: &luminol_egui_wgpu::RenderState, diff --git a/crates/graphics/src/event.rs b/crates/graphics/src/event.rs index 17108910..7da37819 100644 --- a/crates/graphics/src/event.rs +++ b/crates/graphics/src/event.rs @@ -26,7 +26,7 @@ pub struct Event { impl Event { // code smell, fix - pub fn new( + pub fn new_map( graphics_state: &GraphicsState, filesystem: &impl luminol_filesystem::FileSystem, viewport: &Viewport, @@ -112,6 +112,98 @@ impl Event { })) } + pub fn new_standalone( + graphics_state: &GraphicsState, + filesystem: &impl luminol_filesystem::FileSystem, + viewport: &Viewport, + event: &luminol_data::rpg::Event, + atlas: &Atlas, + ) -> color_eyre::Result> { + let Some(page) = event.pages.first() else { + color_eyre::eyre::bail!("event does not have first page"); + }; + + let mut is_placeholder = false; + let texture = if let Some(ref filename) = page.graphic.character_name { + let texture = graphics_state + .texture_loader + .load_now_dir(filesystem, "Graphics/Characters", filename) + .wrap_err_with(|| format!("Error loading event character graphic {filename:?}")); + match texture { + Ok(t) => t, + Err(e) => { + graphics_state.send_texture_error(e); + is_placeholder = true; + graphics_state.texture_loader.placeholder_texture() + } + } + } else if page.graphic.tile_id.is_some() { + atlas.atlas_texture.clone() + } else { + return Ok(None); + }; + + let (quad, sprite_size) = if let Some(id) = page.graphic.tile_id { + // Why does this have to be + 1? + let quad = atlas.calc_quad((id + 1) as i16); + + (quad, egui::vec2(32., 32.)) + } else if is_placeholder { + let rect = egui::Rect::from_min_size(egui::pos2(0.0, 0.0), egui::vec2(32., 32.0)); + let quad = Quad::new(rect, rect); + + (quad, egui::vec2(32., 32.)) + } else { + let cw = texture.width() as f32 / 4.; + let ch = texture.height() as f32 / 4.; + let pos = egui::Rect::from_min_size( + egui::pos2( + 0., //(event.x as f32 * 32.) + (16. - (cw / 2.)), + 0., //(event.y as f32 * 32.) + (32. - ch), + ), + egui::vec2(cw, ch), + ); + + // Reduced by 0.01 px on all sides to reduce texture bleeding + let tex_coords = egui::Rect::from_min_size( + egui::pos2( + page.graphic.pattern as f32 * cw + 0.01, + (page.graphic.direction as f32 - 2.) / 2. * ch + 0.01, + ), + egui::vec2(cw - 0.02, ch - 0.02), + ); + let quad = Quad::new(pos, tex_coords); + + (quad, egui::vec2(cw, ch)) + }; + + let transform = Transform::unit(graphics_state); + + let sprite = Sprite::new( + graphics_state, + quad, + page.graphic.character_hue, + page.graphic.opacity, + page.graphic.blend_type, + &texture, + viewport, + transform, + ); + + Ok(Some(Self { + sprite, + sprite_size, + })) + } + + pub fn set_position(&mut self, render_state: &luminol_egui_wgpu::RenderState, x: i32, y: i32) { + let x = x as f32 * 32. + (32. - self.sprite_size.x) / 2.; + let y = y as f32 * 32. + (32. - self.sprite_size.y); + self.sprite + .transform + .set_position(render_state, glam::vec2(x, y)); + } + pub fn sprite(&self) -> &Sprite { &self.sprite } diff --git a/crates/graphics/src/map.rs b/crates/graphics/src/map.rs index c42614af..ac5efbcf 100644 --- a/crates/graphics/src/map.rs +++ b/crates/graphics/src/map.rs @@ -19,7 +19,8 @@ use color_eyre::eyre::Context; use itertools::Itertools; use crate::{ - Collision, Drawable, Event, GraphicsState, Grid, Plane, Renderable, Tiles, Transform, Viewport, + Atlas, Collision, Drawable, Event, GraphicsState, Grid, Plane, Renderable, Tiles, Transform, + Viewport, }; pub struct Map { @@ -29,6 +30,7 @@ pub struct Map { pub collision: Collision, pub grid: Grid, pub events: luminol_data::OptionVec, + pub atlas: Atlas, pub viewport: Viewport, ani_time: Option, @@ -133,7 +135,7 @@ impl Map { .events .iter() .map(|(id, event)| { - Event::new(graphics_state, filesystem, &viewport, event, &atlas) + Event::new_map(graphics_state, filesystem, &viewport, event, &atlas) .map(|opt_e| opt_e.map(|e| (id, e))) }) .flatten_ok() @@ -147,6 +149,7 @@ impl Map { grid, events, viewport, + atlas, ani_time: None, diff --git a/crates/ui/src/tabs/map/mod.rs b/crates/ui/src/tabs/map/mod.rs index dd909734..04678ba6 100644 --- a/crates/ui/src/tabs/map/mod.rs +++ b/crates/ui/src/tabs/map/mod.rs @@ -359,7 +359,7 @@ impl luminol_core::Tab for Tab { let response = self.view.ui( ui, - &update_state.graphics, + update_state, &map, &self.tilepicker, self.event_drag_info.is_some(), From af613f6e85b51260ae37613571e55db4bedbb47f Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Fri, 14 Jun 2024 21:55:35 -0700 Subject: [PATCH 21/24] Fix collision --- crates/graphics/src/primitives/shaders/collision.wgsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/graphics/src/primitives/shaders/collision.wgsl b/crates/graphics/src/primitives/shaders/collision.wgsl index 49134567..0fbd938a 100644 --- a/crates/graphics/src/primitives/shaders/collision.wgsl +++ b/crates/graphics/src/primitives/shaders/collision.wgsl @@ -51,7 +51,7 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, instance: InstanceInput) -> } var vertex_positions = VERTEX_POSITIONS; - let vertex_position = (vertex_positions[vertex_index] + instance.tile_position) * 32.; + let vertex_position = vertex_positions[vertex_index] + (instance.tile_position * 32.); let normalized_pos = Trans::translate_vertex(vertex_position, viewport, transform); out.clip_position = vec4(normalized_pos, 0.0, 1.0); From 9a3440015648c97c0ddc1984799d6d349ee879ae Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Fri, 14 Jun 2024 22:08:14 -0700 Subject: [PATCH 22/24] Fix align bug --- crates/graphics/src/primitives/tiles/display.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/graphics/src/primitives/tiles/display.rs b/crates/graphics/src/primitives/tiles/display.rs index 60b46975..b0805912 100644 --- a/crates/graphics/src/primitives/tiles/display.rs +++ b/crates/graphics/src/primitives/tiles/display.rs @@ -38,7 +38,7 @@ struct LayerData { min_alignment_size: u32, } -#[repr(C, align(16))] +#[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] pub struct Data { opacity: f32, @@ -48,7 +48,10 @@ pub struct Data { impl Data { fn aligned_size_of(min_alignment_size: u32) -> usize { - wgpu::util::align_to(std::mem::size_of::(), min_alignment_size as usize) + wgpu::util::align_to( + std::mem::size_of::(), + (min_alignment_size as usize).max(std::mem::align_of::()), + ) } } From d6d5041e381889eb454b612f9ff3edbf4bcd6269 Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Sun, 16 Jun 2024 01:07:33 -0700 Subject: [PATCH 23/24] Fix event preview rendering --- crates/components/src/map_view.rs | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/crates/components/src/map_view.rs b/crates/components/src/map_view.rs index bd4f0d7f..2b6596e2 100644 --- a/crates/components/src/map_view.rs +++ b/crates/components/src/map_view.rs @@ -32,8 +32,8 @@ pub struct MapView { /// The first sprite is for drawing on the tilemap, /// and the second sprite is for the hover preview. - preview_events: HashMap, - last_events: HashMap, + preview_events: HashMap, + last_events: HashMap, pub map: luminol_graphics::Map, pub selected_layer: SelectedLayer, @@ -64,6 +64,11 @@ pub struct MapView { pub data_id: egui::Id, } +struct PreviewEvent { + viewport: luminol_graphics::Viewport, + sprite: luminol_graphics::Event, +} + #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Default)] pub enum SelectedLayer { #[default] @@ -376,6 +381,7 @@ impl MapView { // update relevant properties if let Some(sprite) = sprite { + // FIXME only update if necessary sprite.set_position(&update_state.graphics.render_state, event.x, event.y); sprite.sprite.graphic.set_opacity_multiplier( &update_state.graphics.render_state, @@ -444,13 +450,13 @@ impl MapView { ); if has_sprite { - let mut preview_sprite = + let mut preview = self.last_events.remove(&event.id).unwrap_or_else(|| { let viewport = luminol_graphics::Viewport::new( &update_state.graphics, glam::vec2(event_size.x, event_size.y), ); - luminol_graphics::Event::new_standalone( + let sprite = luminol_graphics::Event::new_standalone( &update_state.graphics, update_state.filesystem, &viewport, @@ -458,14 +464,20 @@ impl MapView { &self.map.atlas, ) .unwrap() - .unwrap() + .unwrap(); // FIXME: handle error + PreviewEvent { viewport, sprite } }); if response.rect.is_positive() { let clipped_rect = ui.ctx().screen_rect().intersect(response.rect); + preview.viewport.set_size( + &update_state.graphics.render_state, + glam::vec2(clipped_rect.width(), clipped_rect.height()), + ); + let painter = luminol_graphics::Painter::new( - preview_sprite.prepare(&update_state.graphics), + preview.sprite.prepare(&update_state.graphics), ); ui.painter().add( luminol_egui_wgpu::Callback::new_paint_callback( @@ -473,9 +485,9 @@ impl MapView { painter, ), ); - } - self.preview_events.insert(event.id, preview_sprite); + self.preview_events.insert(event.id, preview); + } } match self.selected_event_id { @@ -550,6 +562,9 @@ impl MapView { } } + self.last_events.clear(); + std::mem::swap(&mut self.preview_events, &mut self.last_events); // swap and clear preview events, so we only keep the ones used this frame + self.selected_event_id = selected_event.map(|e| e.id); // Draw white rectangles on the border of all events From 68a2303f54a19683e13b15e6c1d720622a09005a Mon Sep 17 00:00:00 2001 From: Speak2Erase Date: Sun, 16 Jun 2024 12:03:35 -0700 Subject: [PATCH 24/24] Fix some webgl issues --- crates/graphics/src/data/viewport.rs | 4 +++- crates/graphics/src/primitives/shaders/grid.wgsl | 8 ++++---- crates/graphics/src/primitives/shaders/translation.wgsl | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/graphics/src/data/viewport.rs b/crates/graphics/src/data/viewport.rs index 7b1476ee..9d06c749 100644 --- a/crates/graphics/src/data/viewport.rs +++ b/crates/graphics/src/data/viewport.rs @@ -27,11 +27,12 @@ pub struct Viewport { #[derive(Debug, Copy, Clone, PartialEq)] #[derive(bytemuck::Pod, bytemuck::Zeroable)] -#[repr(C)] +#[repr(C, align(16))] struct Data { viewport_size: glam::Vec2, viewport_translation: glam::Vec2, viewport_scale: glam::Vec2, + _pad: [u32; 2], } impl Viewport { @@ -40,6 +41,7 @@ impl Viewport { viewport_size, viewport_translation: glam::Vec2::ZERO, viewport_scale: glam::Vec2::ONE, + _pad: [0; 2], }; let uniform = graphics_state.render_state.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { diff --git a/crates/graphics/src/primitives/shaders/grid.wgsl b/crates/graphics/src/primitives/shaders/grid.wgsl index 4cfa3003..29508c85 100644 --- a/crates/graphics/src/primitives/shaders/grid.wgsl +++ b/crates/graphics/src/primitives/shaders/grid.wgsl @@ -28,13 +28,13 @@ const QUAD_VERTICES: array = array( ); #else const QUAD_VERTICES: array, 6> = array, 6>( - vec2(0., 0.), + vec2(0., 0.), // Provoking vertex vec2(1., 0.), - vec2(0., 1.), // Provoking vertex + vec2(0., 1.), - vec2(1., 1.), + vec2(1., 1.), // Provoking vertex vec2(0., 1.), - vec2(1., 0.), // Provoking vertex + vec2(1., 0.), ); #endif diff --git a/crates/graphics/src/primitives/shaders/translation.wgsl b/crates/graphics/src/primitives/shaders/translation.wgsl index 2984b60f..14f355f2 100644 --- a/crates/graphics/src/primitives/shaders/translation.wgsl +++ b/crates/graphics/src/primitives/shaders/translation.wgsl @@ -9,6 +9,7 @@ struct Viewport { viewport_size: vec2f, // size of the viewport in pixels viewport_translation: vec2f, // additional translation in pixels viewport_scale: vec2f, // additional scale in pixels + _pad: vec2u // 16 byte alignment (webgl requires 16 byte alignment for uniform buffers) } fn translate_vertex(position: vec2f, viewport: Viewport, transform: Transform) -> vec2f {