diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 0000000..6259fd9 --- /dev/null +++ b/.clippy.toml @@ -0,0 +1,8 @@ +# LINEBENDER LINT SET - .clippy.toml - v1 +# See https://linebender.org/wiki/canonical-lints/ +# The default Clippy value is capped at 8 bytes, which was chosen to improve performance on 32-bit. +# Given that we are building for the future and even low-end mobile phones have 64-bit CPUs, +# it makes sense to optimize for 64-bit and accept the performance hits on 32-bit. +# 16 bytes is the number of bytes that fits into two 64-bit CPU registers. +trivial-copy-size-limit = 16 +# END LINEBENDER LINT SET diff --git a/Cargo.toml b/Cargo.toml index 94b695c..462da75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,10 +20,11 @@ license = "(MIT OR Apache-2.0) AND OFL-1.1" repository = "https://github.com/linebender/bevy_vello" [workspace.dependencies] -bevy = { version = "0.14.0", default-features = false, features = [ +bevy = { version = "0.15.0", default-features = false, features = [ "bevy_asset", "bevy_winit", "bevy_core_pipeline", + "bevy_window", "bevy_pbr", "bevy_render", "bevy_ui", @@ -52,13 +53,13 @@ repository.workspace = true [dependencies] bevy = { workspace = true } vello = "0.2.1" -vello_svg = "0.3.0" -velato = "0.3.0" -thiserror = "1.0.61" -once_cell = "1.19.0" +vello_svg = "0.3.1" +velato = "0.3.1" +thiserror = "2.0.6" +once_cell = "1.20.2" [dev-dependencies] -wasm-bindgen-test = "0.3.42" +wasm-bindgen-test = "0.3.49" [features] default = ["default_font"] @@ -66,3 +67,61 @@ svg = [] lottie = [] experimental-dotLottie = ["lottie"] default_font = [] + + +[workspace.lints] +# LINEBENDER LINT SET - Cargo.toml - v2 +# See https://linebender.org/wiki/canonical-lints/ +rust.keyword_idents_2024 = "forbid" +rust.non_ascii_idents = "forbid" +rust.non_local_definitions = "forbid" +rust.unsafe_op_in_unsafe_fn = "forbid" +rust.elided_lifetimes_in_paths = "warn" +rust.let_underscore_drop = "warn" +rust.missing_debug_implementations = "warn" +rust.missing_docs = "warn" +rust.single_use_lifetimes = "warn" +rust.trivial_numeric_casts = "warn" +rust.unexpected_cfgs = "warn" +rust.unit_bindings = "warn" +rust.unnameable_types = "warn" +rust.unreachable_pub = "warn" +rust.unused_import_braces = "warn" +rust.unused_lifetimes = "warn" +rust.unused_macro_rules = "warn" +rust.unused_qualifications = "warn" +rust.variant_size_differences = "warn" +clippy.allow_attributes = "warn" +clippy.allow_attributes_without_reason = "warn" +clippy.cast_possible_truncation = "warn" +clippy.collection_is_never_read = "warn" +clippy.dbg_macro = "warn" +clippy.debug_assert_with_mut_call = "warn" +clippy.doc_markdown = "warn" +clippy.exhaustive_enums = "warn" +clippy.fn_to_numeric_cast_any = "forbid" +clippy.infinite_loop = "warn" +clippy.large_include_file = "warn" +clippy.large_stack_arrays = "warn" +clippy.match_same_arms = "warn" +clippy.mismatching_type_param_order = "warn" +clippy.missing_assert_message = "warn" +clippy.missing_errors_doc = "warn" +clippy.missing_fields_in_debug = "warn" +clippy.missing_panics_doc = "warn" +clippy.partial_pub_fields = "warn" +clippy.return_self_not_must_use = "warn" +clippy.same_functions_in_if_condition = "warn" +clippy.semicolon_if_nothing_returned = "warn" +clippy.shadow_unrelated = "warn" +clippy.should_panic_without_expect = "warn" +clippy.todo = "warn" +clippy.trivially_copy_pass_by_ref = "warn" +clippy.unseparated_literal_suffix = "warn" +clippy.use_self = "warn" +clippy.wildcard_imports = "warn" +clippy.cargo_common_metadata = "warn" +clippy.negative_feature_names = "warn" +clippy.redundant_feature_names = "warn" +clippy.wildcard_dependencies = "warn" +# END LINEBENDER LINT SET diff --git a/src/debug.rs b/src/debug.rs index d7f8bf2..5163ffc 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -1,6 +1,6 @@ //! Logic for rendering debug visualizations use crate::{ - text::VelloTextAnchor, CoordinateSpace, VelloAsset, VelloAssetAnchor, VelloFont, + text::VelloTextAnchor, CoordinateSpace, VelloAsset, VelloAsset2d, VelloAssetAnchor, VelloFont, VelloTextSection, }; use bevy::{color::palettes::css, math::Vec3Swizzles, prelude::*}; @@ -27,7 +27,7 @@ pub enum DebugVisualizations { fn render_asset_debug( query_vectors: Query< ( - &Handle, + &VelloAsset2d, &VelloAssetAnchor, &GlobalTransform, &CoordinateSpace, @@ -48,7 +48,7 @@ fn render_asset_debug( .iter() .filter(|(_, _, _, _, d)| **d == DebugVisualizations::Visible) { - if let Some(asset) = assets.get(asset) { + if let Some(asset) = assets.get(asset.id()) { match space { CoordinateSpace::WorldSpace => { // Origin @@ -63,14 +63,14 @@ fn render_asset_debug( CoordinateSpace::ScreenSpace => { // Origin let origin = gtransform.translation().xy(); - let Some(origin) = camera.viewport_to_world_2d(view, origin) else { + let Ok(origin) = camera.viewport_to_world_2d(view, origin) else { continue; }; draw_origin(&mut gizmos, projection, origin); // Bounding box let gtransform = &asset_anchor.compute(asset, gtransform); let rect_center = gtransform.translation().xy(); - let Some(rect_center) = camera.viewport_to_world_2d(view, rect_center) else { + let Ok(rect_center) = camera.viewport_to_world_2d(view, rect_center) else { continue; }; let Some(rect) = asset.bb_in_screen_space(gtransform, camera, view) else { @@ -148,13 +148,17 @@ fn render_text_debug( } }; let rect_center = origin + rect.size() / 2.0; - gizmos.rect_2d(rect_center, 0.0, rect.size(), css::WHITE); + gizmos.rect_2d( + Isometry2d::from_xy(rect_center.x, rect_center.y), + rect.size(), + css::WHITE, + ); } CoordinateSpace::ScreenSpace => { let Some(rect) = text.bb_in_screen_space(font, gtransform, camera, view) else { continue; }; - let Some(mut origin) = + let Ok(mut origin) = camera.viewport_to_world_2d(view, gtransform.translation().xy()) else { continue; @@ -195,9 +199,8 @@ fn render_text_debug( }; let rect_center = origin + Vec2::new(rect.width() / 2.0, -rect.height() / 2.0); gizmos.rect_2d( - rect_center, - 0.0, - rect.size() * Vec2::new(1.0, 1.0), + Isometry2d::from_xy(rect_center.x, rect_center.y), + rect.size() * Vec2::new(1.0, 1.0), // TODO: Why do we *= 1? css::WHITE, ); } @@ -221,5 +224,9 @@ fn draw_origin(gizmos: &mut Gizmos, projection: &OrthographicProjection, origin: /// A helper method to draw the bounding box fn draw_bounding_box(gizmos: &mut Gizmos, position: Vec2, size: Vec2) { - gizmos.rect_2d(position, 0.0, size, css::WHITE); + gizmos.rect_2d( + Isometry2d::from_xy(position.x, position.y), + size, + css::WHITE, + ); } diff --git a/src/integrations/asset.rs b/src/integrations/asset.rs index 4884e64..8935a37 100644 --- a/src/integrations/asset.rs +++ b/src/integrations/asset.rs @@ -1,6 +1,11 @@ use crate::VectorFile; use bevy::{prelude::*, reflect::TypePath}; +#[derive(Component, Default, Debug, Clone, Deref, DerefMut, PartialEq, Eq)] +// TODO: Add required components +pub struct VelloAsset2d(pub Handle); + +// TODO: Split this into VelloSvg and VelloLottie #[derive(Asset, TypePath, Clone)] pub struct VelloAsset { pub file: VectorFile, @@ -36,13 +41,15 @@ impl VelloAsset { let Rect { min, max } = self.bb_in_world_space(gtransform); camera .viewport_to_world_2d(camera_transform, min) - .zip(camera.viewport_to_world_2d(camera_transform, max)) + .ok() + .zip(camera.viewport_to_world_2d(camera_transform, max).ok()) .map(|(min, max)| Rect { min, max }) } } /// Describes how the asset is positioned relative to its [`Transform`]. It defaults to [`VelloAssetAnchor::Center`]. #[derive(Component, Default, Clone, Copy, PartialEq, Eq)] +// TODO: Add required components pub enum VelloAssetAnchor { /// Bounds start from the render position and advance up and to the right. BottomLeft, diff --git a/src/integrations/dot_lottie/player_state.rs b/src/integrations/dot_lottie/player_state.rs index 88f91de..5b0e401 100644 --- a/src/integrations/dot_lottie/player_state.rs +++ b/src/integrations/dot_lottie/player_state.rs @@ -1,11 +1,10 @@ use super::PlayerTransition; -use crate::{PlaybackOptions, Theme, VelloAsset}; -use bevy::prelude::*; +use crate::{PlaybackOptions, Theme, VelloAsset2d}; #[derive(Debug, Clone)] pub struct PlayerState { pub id: &'static str, - pub asset: Option>, + pub asset: Option, pub theme: Option, pub options: Option, pub transitions: Vec, @@ -28,7 +27,7 @@ impl PlayerState { } } - pub fn asset(mut self, asset: Handle) -> Self { + pub fn asset(mut self, asset: VelloAsset2d) -> Self { self.asset.replace(asset); self } @@ -58,7 +57,7 @@ impl PlayerState { self } - pub fn set_asset(mut self, asset: Option>) -> Self { + pub fn set_asset(mut self, asset: Option) -> Self { self.asset = asset; self } @@ -88,7 +87,7 @@ impl PlayerState { self } - pub fn get_asset(&self) -> Option<&Handle> { + pub fn get_asset(&self) -> Option<&VelloAsset2d> { self.asset.as_ref() } diff --git a/src/integrations/dot_lottie/systems.rs b/src/integrations/dot_lottie/systems.rs index 87564db..d807c57 100644 --- a/src/integrations/dot_lottie/systems.rs +++ b/src/integrations/dot_lottie/systems.rs @@ -1,7 +1,7 @@ use super::DotLottiePlayer; use crate::{ integrations::lottie::PlaybackPlayMode, PlaybackDirection, PlaybackLoopBehavior, - PlaybackOptions, PlayerTransition, Playhead, VectorFile, VelloAsset, + PlaybackOptions, PlayerTransition, Playhead, VectorFile, VelloAsset, VelloAsset2d, }; use bevy::{prelude::*, utils::Instant}; use std::time::Duration; @@ -10,7 +10,7 @@ use vello_svg::usvg::strict_num::Ulps; /// Advance all the dotLottie playheads in the scene pub fn advance_dot_lottie_playheads( mut query: Query<( - &Handle, + &VelloAsset2d, &mut Playhead, &mut DotLottiePlayer, &PlaybackOptions, @@ -70,7 +70,7 @@ pub fn advance_dot_lottie_playheads( // Advance playhead let length = end_frame - start_frame; - playhead.frame += (time.delta_seconds_f64() + playhead.frame += (time.delta_secs_f64() * options.speed * composition.frame_rate * (options.direction as i32 as f64) @@ -139,7 +139,7 @@ pub fn run_transitions( &Playhead, &PlaybackOptions, &GlobalTransform, - &mut Handle, + &mut VelloAsset2d, )>, mut assets: ResMut>, windows: Query<&Window>, @@ -156,7 +156,7 @@ pub fn run_transitions( let pointer_pos = window .cursor_position() - .and_then(|cursor| camera.viewport_to_world(view, cursor)) + .and_then(|cursor| camera.viewport_to_world(view, cursor).ok()) .map(|ray| ray.origin.truncate()); for (mut player, playhead, options, gtransform, current_asset_handle) in query_player.iter_mut() @@ -312,7 +312,7 @@ pub fn transition_state( let Some(VelloAsset { file: VectorFile::Lottie(composition), .. - }) = assets.get(target_asset) + }) = assets.get(target_asset.id()) else { warn!("not ready for state transition, re-queueing {next_state}..."); player.next_state = Some(next_state); diff --git a/src/integrations/lottie/asset_loader.rs b/src/integrations/lottie/asset_loader.rs index 3ec8c6b..6ba8eaa 100644 --- a/src/integrations/lottie/asset_loader.rs +++ b/src/integrations/lottie/asset_loader.rs @@ -3,7 +3,7 @@ use crate::{ VelloAsset, }; use bevy::{ - asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext}, + asset::{io::Reader, AssetLoader, LoadContext}, prelude::*, utils::ConditionalSendFuture, }; @@ -18,11 +18,11 @@ impl AssetLoader for VelloLottieLoader { type Error = VectorLoaderError; - fn load<'a>( - &'a self, - reader: &'a mut Reader, - _settings: &'a Self::Settings, - load_context: &'a mut LoadContext, + fn load( + &self, + reader: &mut dyn Reader, + _settings: &Self::Settings, + load_context: &mut LoadContext, ) -> impl ConditionalSendFuture> { Box::pin(async move { let mut bytes = Vec::new(); diff --git a/src/integrations/lottie/plugin.rs b/src/integrations/lottie/plugin.rs index 96e07cd..e6eacfd 100644 --- a/src/integrations/lottie/plugin.rs +++ b/src/integrations/lottie/plugin.rs @@ -13,6 +13,6 @@ impl Plugin for LottieIntegrationPlugin { systems::advance_playheads_with_options, ), ) - .add_systems(Last, systems::spawn_playheads); + .add_observer(systems::spawn_playheads); } } diff --git a/src/integrations/lottie/systems.rs b/src/integrations/lottie/systems.rs index ed8ea73..e0784e0 100644 --- a/src/integrations/lottie/systems.rs +++ b/src/integrations/lottie/systems.rs @@ -1,49 +1,51 @@ use crate::{ integrations::lottie::PlaybackPlayMode, PlaybackDirection, PlaybackLoopBehavior, - PlaybackOptions, Playhead, VectorFile, VelloAsset, + PlaybackOptions, Playhead, VectorFile, VelloAsset, VelloAsset2d, }; use bevy::{prelude::*, utils::Instant}; use std::time::Duration; use vello_svg::usvg::strict_num::Ulps; /// Spawn playheads for Lotties. Every Lottie gets exactly 1 playhead. +/// TODO: This should be a required component method pub fn spawn_playheads( + trigger: Trigger, mut commands: Commands, - query: Query<(Entity, &Handle, Option<&PlaybackOptions>), Without>, + query: Query<(&VelloAsset2d, Option<&PlaybackOptions>)>, assets: Res>, ) { - for (entity, handle, options) in query.iter() { - if let Some( - _asset @ VelloAsset { - file: _file @ VectorFile::Lottie(composition), - .. + let entity = trigger.entity(); + let Ok((handle, options)) = query.get(entity) else { + return; + }; + if let Some( + _asset @ VelloAsset { + file: _file @ VectorFile::Lottie(composition), + .. + }, + ) = assets.get(handle.id()) + { + let frame = match options { + Some(options) => match options.direction { + PlaybackDirection::Normal => options.segments.start.max(composition.frames.start), + PlaybackDirection::Reverse => { + options.segments.end.min(composition.frames.end).prev() + } }, - ) = assets.get(handle) - { - let frame = match options { - Some(options) => match options.direction { - PlaybackDirection::Normal => { - options.segments.start.max(composition.frames.start) - } - PlaybackDirection::Reverse => { - options.segments.end.min(composition.frames.end).prev() - } - }, - None => composition.frames.start, - }; - commands.entity(entity).insert(Playhead::new(frame)); - } + None => composition.frames.start, + }; + commands.entity(entity).insert(Playhead::new(frame)); } } /// Advance all lottie playheads without playback options in the scene pub fn advance_playheads_without_options( #[cfg(feature = "experimental-dotLottie")] mut query: Query< - (&Handle, &mut Playhead), + (&VelloAsset2d, &mut Playhead), (Without, Without), >, #[cfg(not(feature = "experimental-dotLottie"))] mut query: Query< - (&Handle, &mut Playhead), + (&VelloAsset2d, &mut Playhead), Without, >, mut assets: ResMut>, @@ -69,7 +71,7 @@ pub fn advance_playheads_without_options( // Advance playhead let length = end_frame - start_frame; - playhead.frame += (time.delta_seconds_f64() * composition.frame_rate) % length; + playhead.frame += (time.delta_secs_f64() * composition.frame_rate) % length; if playhead.frame > end_frame { // Wrap around to the beginning of the segment @@ -81,11 +83,11 @@ pub fn advance_playheads_without_options( /// Advance all lottie playheads with playback options in the scene pub fn advance_playheads_with_options( #[cfg(feature = "experimental-dotLottie")] mut query: Query< - (&Handle, &mut Playhead, &PlaybackOptions), + (&VelloAsset2d, &mut Playhead, &PlaybackOptions), Without, >, #[cfg(not(feature = "experimental-dotLottie"))] mut query: Query<( - &Handle, + &VelloAsset2d, &mut Playhead, &PlaybackOptions, )>, @@ -134,7 +136,7 @@ pub fn advance_playheads_with_options( // Advance playhead let length = end_frame - start_frame; - playhead.frame += (time.delta_seconds_f64() + playhead.frame += (time.delta_secs_f64() * options.speed * composition.frame_rate * (options.direction as i32 as f64) diff --git a/src/integrations/mod.rs b/src/integrations/mod.rs index 632c66a..ddc6bc0 100644 --- a/src/integrations/mod.rs +++ b/src/integrations/mod.rs @@ -18,7 +18,7 @@ mod error; pub use error::VectorLoaderError; mod asset; -pub use asset::{VelloAsset, VelloAssetAnchor}; +pub use asset::{VelloAsset, VelloAsset2d, VelloAssetAnchor}; #[derive(Clone)] pub enum VectorFile { diff --git a/src/integrations/svg/asset_loader.rs b/src/integrations/svg/asset_loader.rs index 644cab7..71217e1 100644 --- a/src/integrations/svg/asset_loader.rs +++ b/src/integrations/svg/asset_loader.rs @@ -3,7 +3,7 @@ use crate::{ VelloAsset, }; use bevy::{ - asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext}, + asset::{io::Reader, AssetLoader, LoadContext}, prelude::*, utils::ConditionalSendFuture, }; @@ -18,11 +18,11 @@ impl AssetLoader for VelloSvgLoader { type Error = VectorLoaderError; - fn load<'a>( - &'a self, - reader: &'a mut Reader, - _settings: &'a Self::Settings, - load_context: &'a mut LoadContext, + fn load( + &self, + reader: &mut dyn Reader, + _settings: &Self::Settings, + load_context: &mut LoadContext, ) -> impl ConditionalSendFuture> { Box::pin(async move { let mut bytes = Vec::new(); diff --git a/src/integrations/svg/extract.rs b/src/integrations/svg/extract.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/lib.rs b/src/lib.rs index 60a37dc..02e1f37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ pub mod prelude { pub use crate::{ debug::DebugVisualizations, - integrations::{VectorFile, VelloAsset, VelloAssetAnchor}, + integrations::{VectorFile, VelloAsset, VelloAsset2d, VelloAssetAnchor}, render::{SkipEncoding, VelloRenderSettings}, text::{VelloFont, VelloTextAnchor, VelloTextSection, VelloTextStyle}, CoordinateSpace, VelloAssetBundle, VelloScene, VelloSceneBundle, VelloTextBundle, @@ -50,24 +50,17 @@ pub enum CoordinateSpace { #[derive(Bundle, Default)] pub struct VelloAssetBundle { /// Asset data to render - pub asset: Handle, + pub asset: VelloAsset2d, /// How the asset is positioned relative to its [`Transform`]. pub asset_anchor: VelloAssetAnchor, /// The coordinate space in which this vector should be rendered. pub coordinate_space: CoordinateSpace, /// A transform to apply to this vector pub transform: Transform, - /// The global transform managed by Bevy - pub global_transform: GlobalTransform, /// Whether to render debug visualizations pub debug_visualizations: DebugVisualizations, /// User indication of whether an entity is visible. Propagates down the entity hierarchy. pub visibility: Visibility, - /// Whether or not an entity is visible in the hierarchy. - pub inherited_visibility: InheritedVisibility, - /// Algorithmically-computed indication of whether an entity is visible. Should be extracted - /// for rendering. - pub view_visibility: ViewVisibility, } #[derive(Bundle, Default)] @@ -78,15 +71,8 @@ pub struct VelloSceneBundle { pub coordinate_space: CoordinateSpace, /// A transform to apply to this scene pub transform: Transform, - /// The global transform managed by Bevy - pub global_transform: GlobalTransform, /// User indication of whether an entity is visible. Propagates down the entity hierarchy. pub visibility: Visibility, - /// Whether or not an entity is visible in the hierarchy. - pub inherited_visibility: InheritedVisibility, - /// Algorithmically-computed indication of whether an entity is visible. Should be extracted - /// for rendering. - pub view_visibility: ViewVisibility, } #[derive(Bundle, Default)] @@ -99,17 +85,10 @@ pub struct VelloTextBundle { pub coordinate_space: CoordinateSpace, /// A transform to apply to this text pub transform: Transform, - /// The global transform managed by Bevy - pub global_transform: GlobalTransform, /// Whether to render debug visualizations pub debug_visualizations: DebugVisualizations, /// User indication of whether an entity is visible. Propagates down the entity hierarchy. pub visibility: Visibility, - /// Whether or not an entity is visible in the hierarchy. - pub inherited_visibility: InheritedVisibility, - /// Algorithmically-computed indication of whether an entity is visible. Should be extracted - /// for rendering. - pub view_visibility: ViewVisibility, } /// A simple newtype component wrapper for [`vello::Scene`] for rendering. diff --git a/src/render/extract.rs b/src/render/extract.rs index 68fd37d..a9fe11e 100644 --- a/src/render/extract.rs +++ b/src/render/extract.rs @@ -11,7 +11,7 @@ pub struct ExtractedRenderAsset { pub asset_anchor: VelloAssetAnchor, pub transform: GlobalTransform, pub render_mode: CoordinateSpace, - pub ui_node: Option, + pub ui_node: Option, pub render_layers: Option, pub alpha: f32, #[cfg(feature = "lottie")] @@ -26,11 +26,11 @@ pub fn extract_svg_assets( query_vectors: Extract< Query< ( - &Handle, + &VelloAsset2d, &VelloAssetAnchor, &CoordinateSpace, &GlobalTransform, - Option<&Node>, + Option<&ComputedNode>, Option<&RenderLayers>, &ViewVisibility, &InheritedVisibility, @@ -57,7 +57,7 @@ pub fn extract_svg_assets( alpha, .. }, - ) = assets.get(asset) + ) = assets.get(asset.id()) { if view_visibility.get() && inherited_visibility.get() { commands.spawn(ExtractedRenderAsset { @@ -84,13 +84,13 @@ pub fn extract_lottie_assets( query_vectors: Extract< Query< ( - &Handle, + &VelloAsset2d, &VelloAssetAnchor, &CoordinateSpace, &GlobalTransform, &crate::Playhead, Option<&crate::Theme>, - Option<&Node>, + Option<&ComputedNode>, Option<&RenderLayers>, &ViewVisibility, &InheritedVisibility, @@ -119,7 +119,7 @@ pub fn extract_lottie_assets( alpha, .. }, - ) = assets.get(asset) + ) = assets.get(asset.id()) { if view_visibility.get() && inherited_visibility.get() { let playhead = playhead.frame(); @@ -144,7 +144,7 @@ pub struct ExtractedRenderScene { pub scene: VelloScene, pub transform: GlobalTransform, pub render_mode: CoordinateSpace, - pub ui_node: Option, + pub ui_node: Option, pub render_layers: Option, } @@ -158,7 +158,7 @@ pub fn extract_scenes( &GlobalTransform, &ViewVisibility, &InheritedVisibility, - Option<&Node>, + Option<&ComputedNode>, Option<&RenderLayers>, ), Without, diff --git a/src/render/mod.rs b/src/render/mod.rs index 8ed57cb..5c2c552 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -26,6 +26,9 @@ pub(crate) use plugin::VelloRenderPlugin; /// A handle to the screen space render target shader. pub const SSRT_SHADER_HANDLE: Handle = Handle::weak_from_u128(2314894693238056781); +#[derive(Component, Default, Debug, Clone, Deref, DerefMut, PartialEq, Eq)] +pub struct VelloCanvasMaterial2d(pub Handle); + /// A canvas material, with a shader that samples a texture with view-independent UV coordinates. #[derive(AsBindGroup, TypePath, Asset, Clone)] pub struct VelloCanvasMaterial { diff --git a/src/render/plugin.rs b/src/render/plugin.rs index 0fb258c..7c9821c 100644 --- a/src/render/plugin.rs +++ b/src/render/plugin.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{ render::{VelloCanvasMaterial, VelloRenderer, SSRT_SHADER_HANDLE}, - VelloAsset, VelloFont, VelloScene, VelloTextSection, + VelloAsset2d, VelloFont, VelloScene, VelloTextSection, }; use bevy::{ asset::load_internal_asset, @@ -93,11 +93,7 @@ impl Plugin for VelloRenderPlugin { .add_systems( PostUpdate, check_visibility::< - Or<( - With, - With>, - With, - )>, + Or<(With, With, With)>, > .in_set(VisibilitySystems::CheckVisibility), ); diff --git a/src/render/systems.rs b/src/render/systems.rs index 4afab4e..0c5273d 100644 --- a/src/render/systems.rs +++ b/src/render/systems.rs @@ -1,10 +1,11 @@ use super::{ extract::{ExtractedRenderAsset, ExtractedRenderText, SSRenderTarget}, prepare::PreparedAffine, - VelloCanvasMaterial, VelloCanvasSettings, VelloRenderSettings, VelloRenderer, + VelloCanvasMaterial, VelloCanvasMaterial2d, VelloCanvasSettings, VelloRenderSettings, + VelloRenderer, }; use crate::{ - render::extract::ExtractedRenderScene, CoordinateSpace, VelloAsset, VelloFont, VelloScene, + render::extract::ExtractedRenderScene, CoordinateSpace, VelloAsset2d, VelloFont, VelloScene, VelloTextSection, }; use bevy::{ @@ -21,7 +22,6 @@ use bevy::{ texture::GpuImage, view::{NoFrustumCulling, RenderLayers}, }, - sprite::{MaterialMesh2dBundle, Mesh2dHandle}, window::{WindowResized, WindowResolution}, }; use vello::{kurbo::Affine, RenderParams, Scene}; @@ -244,7 +244,7 @@ pub fn render_frame( pub fn resize_rendertargets( mut window_resize_events: EventReader, - mut query: Query<(&mut SSRenderTarget, &Handle)>, + mut query: Query<(&mut SSRenderTarget, &VelloCanvasMaterial2d)>, mut images: ResMut>, mut target_materials: ResMut>, windows: Query<&Window>, @@ -263,7 +263,7 @@ pub fn resize_rendertargets( } for (mut target, target_mat_handle) in query.iter_mut() { let image = setup_image(&mut images, &window.resolution); - if let Some(mat) = target_materials.get_mut(target_mat_handle) { + if let Some(mat) = target_materials.get_mut(target_mat_handle.id()) { target.0 = image.clone(); mat.texture = image; } @@ -314,20 +314,16 @@ pub fn setup_ss_rendertarget( meshes.add(rendertarget_quad) }); let texture_image = setup_image(&mut images, &window.resolution); - let render_target = SSRenderTarget(texture_image.clone()); - let mesh = Mesh2dHandle(mesh_handle.clone()); - let material = custom_materials.add(VelloCanvasMaterial { - texture: texture_image, - }); commands - .spawn(MaterialMesh2dBundle { - mesh, - material, - ..default() - }) + .spawn(( + SSRenderTarget(texture_image.clone()), + Mesh2d(mesh_handle.clone()), + MeshMaterial2d(custom_materials.add(VelloCanvasMaterial { + texture: texture_image, + })), + )) .insert(NoFrustumCulling) - .insert(render_target) .insert(settings.render_layers.clone()); } @@ -347,14 +343,7 @@ pub fn render_settings_change_detection( /// Hide the render target canvas if there is nothing to render pub fn hide_when_empty( mut query_render_target: Query<&mut Visibility, With>, - render_items: Query< - (), - Or<( - With, - With>, - With, - )>, - >, + render_items: Query<(), Or<(With, With, With)>>, ) { if let Ok(mut visibility) = query_render_target.get_single_mut() { if render_items.is_empty() { diff --git a/src/text/font_loader.rs b/src/text/font_loader.rs index 05bbcda..0951502 100644 --- a/src/text/font_loader.rs +++ b/src/text/font_loader.rs @@ -1,7 +1,7 @@ use super::font::VelloFont; use crate::integrations::VectorLoaderError; use bevy::{ - asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext}, + asset::{io::Reader, AssetLoader, LoadContext}, utils::ConditionalSendFuture, }; @@ -15,11 +15,11 @@ impl AssetLoader for VelloFontLoader { type Error = VectorLoaderError; - fn load<'a>( - &'a self, - reader: &'a mut Reader, - _settings: &'a Self::Settings, - _load_context: &'a mut LoadContext, + fn load( + &self, + reader: &mut dyn Reader, + _settings: &Self::Settings, + _load_context: &mut LoadContext, ) -> impl ConditionalSendFuture> { Box::pin(async move { let mut bytes = Vec::new(); diff --git a/src/text/vello_text.rs b/src/text/vello_text.rs index 192b009..216e947 100644 --- a/src/text/vello_text.rs +++ b/src/text/vello_text.rs @@ -3,12 +3,13 @@ use bevy::prelude::*; use vello::peniko::{self, Brush}; #[derive(Component, Default, Clone)] +#[require(VelloTextAnchor, Transform)] pub struct VelloTextSection { pub value: String, pub style: VelloTextStyle, } -#[derive(Component, Clone)] +#[derive(Clone)] pub struct VelloTextStyle { pub font: Handle, pub font_size: f32, @@ -80,7 +81,8 @@ impl VelloTextSection { let Rect { min, max } = self.bb_in_world_space(font, gtransform); camera .viewport_to_world_2d(camera_transform, min) - .zip(camera.viewport_to_world_2d(camera_transform, max)) + .ok() + .zip(camera.viewport_to_world_2d(camera_transform, max).ok()) .map(|(min, max)| Rect { min, max }) } }