diff --git a/crates/components/src/map_view.rs b/crates/components/src/map_view.rs index 5b7ef58b..cd603fcf 100644 --- a/crates/components/src/map_view.rs +++ b/crates/components/src/map_view.rs @@ -15,8 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Luminol. If not, see . -use std::sync::Arc; - use itertools::Itertools; pub struct MapView { @@ -30,8 +28,9 @@ pub struct MapView { /// The first sprite is for drawing on the tilemap, /// and the second sprite is for the hover preview. - pub preview_events: luminol_data::OptionVec>, // TODO only add events when they are being displayed as a preview - pub map_view: luminol_graphics::MapView, + pub preview_events: + luminol_data::OptionVec>, // TODO only add events when they are being displayed as a preview + pub map_view: luminol_graphics::BundleRenderer, pub selected_layer: SelectedLayer, pub selected_event_id: Option, @@ -87,13 +86,13 @@ impl MapView { |x, y, passage| passages[(x, y)] = passage, ); - let map_view = luminol_graphics::MapView::new( + let map_view = luminol_graphics::BundleRenderer::new(luminol_graphics::MapView::new( &update_state.graphics, update_state.filesystem, &map, tileset, &passages, - )?; + )?); let preview_events = map .events @@ -105,7 +104,7 @@ impl MapView { e, map_view.atlas(), ) - .map(|opt_e| opt_e.map(|e| (id, Arc::new(e)))) + .map(|opt_e| opt_e.map(|e| (id, luminol_graphics::ArcRenderer::new(e)))) }) .flatten_ok() .try_collect()?; @@ -378,9 +377,15 @@ impl MapView { } } + let time = ui.input(|i| i.time); + self.map_view + .increment_animation_time(&graphics_state, time); self.map_view .paint(&graphics_state, ui.painter(), canvas_rect); + ui.ctx() + .request_repaint_after(std::time::Duration::from_secs_f32(1.0 / 16.0)); + ui.painter().rect_stroke( map_rect, 5., diff --git a/crates/graphics/src/event.rs b/crates/graphics/src/event.rs index 609f40af..f45fd83f 100644 --- a/crates/graphics/src/event.rs +++ b/crates/graphics/src/event.rs @@ -119,37 +119,14 @@ impl Event { pub fn set_proj(&self, render_state: &luminol_egui_wgpu::RenderState, proj: glam::Mat4) { self.viewport.set_proj(render_state, proj); } +} - pub fn draw<'rpass>( +impl crate::Renderable for Event { + fn draw<'rpass>( &'rpass self, graphics_state: &'rpass GraphicsState, render_pass: &mut impl wgpu::util::RenderEncoder<'rpass>, ) { - self.sprite.draw(graphics_state, render_pass); - } - - pub fn paint( - self: Arc, - graphics_state: Arc, - painter: &egui::Painter, - rect: egui::Rect, - ) { - struct Callback(Arc, Arc); - - 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, - ) { - self.0.draw(&self.1, render_pass); - } - } - - painter.add(luminol_egui_wgpu::Callback::new_paint_callback( - rect, - Callback(self, graphics_state), - )); + self.sprite.draw(graphics_state, render_pass) } } diff --git a/crates/graphics/src/lib.rs b/crates/graphics/src/lib.rs index 1127682e..ee1853f7 100644 --- a/crates/graphics/src/lib.rs +++ b/crates/graphics/src/lib.rs @@ -35,6 +35,9 @@ pub mod event; pub mod map; pub mod plane; +pub mod renderers; +pub use renderers::{ArcRenderer, BundleRenderer, Renderable}; + pub use event::Event; pub use map::MapView; pub use plane::Plane; diff --git a/crates/graphics/src/map.rs b/crates/graphics/src/map.rs index 0090c7ac..5b1cc44d 100644 --- a/crates/graphics/src/map.rs +++ b/crates/graphics/src/map.rs @@ -19,7 +19,6 @@ use color_eyre::eyre::Context; use itertools::Itertools; use std::sync::Arc; -use std::time::Duration; use crate::{Collision, Event, GraphicsState, Grid, Plane, Tiles, Viewport}; @@ -32,15 +31,13 @@ pub struct MapView { pub events: luminol_data::OptionVec, viewport: Arc, - ani_time: Option, + ani_time: f64, pub fog_enabled: bool, pub pano_enabled: bool, pub coll_enabled: bool, pub grid_enabled: bool, pub enabled_layers: Vec, - - render_bundle: Option>, } impl MapView { @@ -142,15 +139,13 @@ impl MapView { viewport, - ani_time: None, + ani_time: 0.0, fog_enabled: true, pano_enabled: true, coll_enabled: false, grid_enabled: true, enabled_layers: vec![true; map.data.zsize()], - - render_bundle: None, }) } @@ -180,101 +175,48 @@ impl MapView { self.viewport.set_proj(render_state, proj); } - pub fn paint( - &mut self, - graphics_state: &GraphicsState, - painter: &egui::Painter, - rect: egui::Rect, - ) { - struct Callback(Arc); - - impl luminol_egui_wgpu::CallbackTrait for Callback { - fn paint<'a>( - &'a self, - _: egui::PaintCallbackInfo, - render_pass: &mut wgpu::RenderPass<'a>, - _: &'a luminol_egui_wgpu::CallbackResources, - ) { - render_pass.execute_bundles(std::iter::once(self.0.as_ref())); - } + pub fn increment_animation_time(&mut self, graphics_state: &GraphicsState, time: f64) { + if time - self.ani_time >= 16. / 60. { + self.ani_time = time; + self.tiles + .autotiles + .inc_ani_index(&graphics_state.render_state); } - - 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.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.)); - - let bundle = match self.render_bundle.clone() { - Some(bundle) => bundle, - None => { - let render_bundle = self.make_render_bundle(graphics_state); - self.render_bundle.insert(render_bundle).clone() - } - }; - - painter.add(luminol_egui_wgpu::Callback::new_paint_callback( - rect, - Callback(bundle), - )); } +} - fn make_render_bundle(&self, graphics_state: &GraphicsState) -> Arc { - let mut render_bundle_encoder = graphics_state - .render_state - .device - .create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor { - label: Some("luminol map view render bundle encoder"), - color_formats: &[Some(graphics_state.render_state.target_format)], - sample_count: 1, - ..Default::default() - }); - +impl crate::Renderable for MapView { + fn draw<'rpass>( + &'rpass self, + graphics_state: &'rpass GraphicsState, + render_pass: &mut impl wgpu::util::RenderEncoder<'rpass>, + ) { if self.pano_enabled { if let Some(panorama) = &self.panorama { - panorama.draw(graphics_state, &mut render_bundle_encoder); + panorama.draw(graphics_state, render_pass); } } - self.tiles.draw( - graphics_state, - &self.enabled_layers, - &mut render_bundle_encoder, - ); + self.tiles + .draw(graphics_state, &self.enabled_layers, render_pass); for (_, event) in self.events.iter() { - event.draw(graphics_state, &mut render_bundle_encoder); + event.draw(graphics_state, render_pass); } if self.fog_enabled { if let Some(fog) = &self.fog { - fog.draw(graphics_state, &mut render_bundle_encoder); + fog.draw(graphics_state, render_pass); } } if self.coll_enabled { - self.collision - .draw(graphics_state, &mut render_bundle_encoder); + self.collision.draw(graphics_state, render_pass); } if self.grid_enabled { // self.grid // .draw(graphics_state, info, &mut render_bundle_encoder); } - - let bundle = render_bundle_encoder.finish(&wgpu::RenderBundleDescriptor { - label: Some("luminol map view render bundle"), - }); - Arc::new(bundle) } } diff --git a/crates/graphics/src/plane.rs b/crates/graphics/src/plane.rs index b90a638e..52259e46 100644 --- a/crates/graphics/src/plane.rs +++ b/crates/graphics/src/plane.rs @@ -62,12 +62,14 @@ impl Plane { Self { sprite } } +} - pub fn draw<'rpass>( +impl crate::Renderable for Plane { + fn draw<'rpass>( &'rpass self, graphics_state: &'rpass GraphicsState, render_pass: &mut impl wgpu::util::RenderEncoder<'rpass>, ) { - self.sprite.draw(graphics_state, render_pass); + self.sprite.draw(graphics_state, render_pass) } } diff --git a/crates/graphics/src/primitives/sprite/mod.rs b/crates/graphics/src/primitives/sprite/mod.rs index b7ddb209..96f9cac0 100644 --- a/crates/graphics/src/primitives/sprite/mod.rs +++ b/crates/graphics/src/primitives/sprite/mod.rs @@ -77,8 +77,10 @@ impl Sprite { bytemuck::cast_slice(&vertices), ); } +} - pub fn draw<'rpass>( +impl crate::Renderable for Sprite { + fn draw<'rpass>( &'rpass self, graphics_state: &'rpass GraphicsState, render_pass: &mut impl wgpu::util::RenderEncoder<'rpass>, diff --git a/crates/graphics/src/renderers.rs b/crates/graphics/src/renderers.rs new file mode 100644 index 00000000..6f626d26 --- /dev/null +++ b/crates/graphics/src/renderers.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 std::sync::Arc; + +use crate::GraphicsState; + +#[derive(Debug)] +pub struct BundleRenderer { + renderable: T, + render_bundle: Option>, +} + +impl Clone for BundleRenderer { + fn clone(&self) -> Self { + Self { + renderable: self.renderable.clone(), + render_bundle: None, + } + } +} + +impl BundleRenderer { + pub fn new(renderable: T) -> Self { + Self { + renderable, + render_bundle: None, + } + } + + pub fn paint( + &mut self, + graphics_state: &GraphicsState, + painter: &egui::Painter, + rect: egui::Rect, + ) { + struct Callback(Arc); + + impl luminol_egui_wgpu::CallbackTrait for Callback { + fn paint<'a>( + &'a self, + _: egui::PaintCallbackInfo, + render_pass: &mut wgpu::RenderPass<'a>, + _: &'a luminol_egui_wgpu::CallbackResources, + ) { + let bundle = self.0.as_ref(); + render_pass.execute_bundles(std::iter::once(bundle)); + } + } + + let bundle = self.render_bundle.get_or_insert_with(|| { + let mut encoder = graphics_state + .render_state + .device + .create_render_bundle_encoder(&wgpu::RenderBundleEncoderDescriptor { + color_formats: &[Some(graphics_state.render_state.target_format)], + sample_count: 1, + ..Default::default() + }); + + self.renderable.draw(graphics_state, &mut encoder); + + let bundle = encoder.finish(&Default::default()); + Arc::new(bundle) + }); + + painter.add(luminol_egui_wgpu::Callback::new_paint_callback( + rect, + Callback(bundle.clone()), + )); + } + + pub fn mark_dirty(&mut self) { + self.render_bundle = None + } +} + +impl std::ops::Deref for BundleRenderer { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.renderable + } +} + +impl std::ops::DerefMut for BundleRenderer { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.renderable + } +} + +#[derive(Debug)] +pub struct ArcRenderer { + renderable: Arc, +} + +impl Clone for ArcRenderer { + fn clone(&self) -> Self { + Self { + renderable: self.renderable.clone(), + } + } +} + +impl ArcRenderer { + pub fn new(renderable: T) -> Self { + Self { + renderable: Arc::new(renderable), + } + } + + pub fn paint( + self, + graphics_state: Arc, + painter: &egui::Painter, + rect: egui::Rect, + ) { + struct Callback(Arc, Arc); + + impl luminol_egui_wgpu::CallbackTrait for Callback { + fn paint<'a>( + &'a self, + _: egui::PaintCallbackInfo, + render_pass: &mut wgpu::RenderPass<'a>, + _: &'a luminol_egui_wgpu::CallbackResources, + ) { + self.0.draw(&self.1, render_pass) + } + } + + painter.add(luminol_egui_wgpu::Callback::new_paint_callback( + rect, + Callback(self.renderable.clone(), graphics_state), + )); + } +} + +impl std::ops::Deref for ArcRenderer { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.renderable + } +} + +pub trait Renderable { + fn draw<'rpass>( + &'rpass self, + graphics_state: &'rpass GraphicsState, + render_pass: &mut impl wgpu::util::RenderEncoder<'rpass>, + ); +}