Skip to main content

bevy_pretty_text/effects/
material.rs

1//! Material [`effects`](crate::parser#effects) are `Bevy` [assets](bevy::asset)
2//! that are dynamically constructed at run time and applied to text spans.
3//!
4//! Material effects refer to shader driven effects, such as [`Rainbow`]. For ECS
5//! driven effects, see [the example here](crate::effects::dynamic#ecs-effects).
6//!
7//! [`Rainbow`]: crate::effects::behavior::Rainbow
8//!
9//! Only 1 material effect will work at a time, whereas [dynamic effects](crate::effects::dynamic)
10//! can be composed.
11//!
12//! If no material is supplied, then the [default glyph material] is used.
13//!
14//! [default glyph material]: DEFAULT_GLYPH_SHADER_HANDLE
15//!
16//! # Using Material Effects
17//!
18//! ```
19//! # use bevy::prelude::*;
20//! # use bevy_pretty_text::prelude::*;
21//! # let mut world = World::new();
22//! // Built-in effects are provided with the `default_effects` feature!
23//! world.spawn(pretty!("[my glitchy text span](glitch)"));
24//!
25//! // Effect with arguments
26//! world.spawn(pretty!("[my rainbow text span](rainbow(1, 0.5))"));
27//! ```
28//!
29//! # Defining Custom Materials
30//!
31//! A [`GlyphMaterial`] provides a vertex and fragment shader for the render
32//! pipeline. It must derive [`AsBindGroup`], just like Bevy materials, for passing
33//! data to the GPU.
34//!
35//! [See here for an example.](crate::effects::dynamic#defining-custom-effects)
36
37use std::marker::PhantomData;
38
39use bevy::asset::uuid_handle;
40use bevy::prelude::*;
41use bevy::render::extract_component::ExtractComponent;
42use bevy::render::render_resource::AsBindGroup;
43use bevy::shader::ShaderRef;
44
45use crate::PrettyText;
46use crate::effects::dynamic::{DynamicEffect, PrettyTextEffectAppExt};
47use crate::render::GlyphMaterialPlugin;
48
49pub(super) fn plugin(_app: &mut App) {
50    #[cfg(not(test))]
51    {
52        bevy::shader::load_shader_library!(_app, "../bevy_pretty_text.wgsl");
53        bevy::asset::load_internal_asset!(
54            _app,
55            DEFAULT_GLYPH_SHADER_HANDLE,
56            "../default_glyph_material.wgsl",
57            Shader::from_wgsl
58        );
59    }
60}
61
62/// The default shader for [`Glyph`](crate::glyph::Glyph)s.
63///
64/// Custom text materials can use the default vertex or fragment shader if no
65/// special behavior is required.
66pub const DEFAULT_GLYPH_SHADER_HANDLE: Handle<Shader> =
67    uuid_handle!("35d4f25c-eb2b-4f26-872f-ef666a76554e");
68
69/// A special material used for rendering a [`Glyph`](crate::glyph::Glyph).
70///
71/// See [the module documentation](crate::effects::material) for general information
72/// about glyph materials and how to implement your own.
73#[derive(Debug, Default, Clone, Component, ExtractComponent, Reflect)]
74#[require(PrettyText)]
75pub struct PrettyTextMaterial<M: GlyphMaterial>(pub Handle<M>);
76
77/// A special material that renders [`Glyph`](crate::glyph::Glyph)s.
78///
79/// Implementors must additionally derive [`DynamicEffect`].
80///
81/// See [the module documentation](crate::effects::material) for general information
82/// about glyph materials and how to implement your own.
83pub trait GlyphMaterial: AsBindGroup + Asset + Clone + Sized {
84    /// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned,
85    /// the default glyph vertex shader will be used.
86    fn vertex_shader() -> ShaderRef {
87        ShaderRef::Default
88    }
89
90    /// Returns this material's fragment shader. If [`ShaderRef::Default`] is returned,
91    /// the default glyph fragment shader will be used.
92    fn fragment_shader() -> ShaderRef {
93        ShaderRef::Default
94    }
95}
96
97/// Extension trait for registering [text materials](crate::effects::material).
98pub trait PrettyTextMaterialAppExt {
99    /// Register material `T` with a `tag`.
100    fn register_pretty_material<T>(&mut self, tag: &'static str) -> &mut Self
101    where
102        T: Default + GlyphMaterial + DynamicEffect,
103        T::Data: PartialEq + Eq + std::hash::Hash + Clone;
104}
105
106impl PrettyTextMaterialAppExt for App {
107    fn register_pretty_material<T>(&mut self, tag: &'static str) -> &mut Self
108    where
109        T: Default + GlyphMaterial + DynamicEffect,
110        T::Data: PartialEq + Eq + std::hash::Hash + Clone,
111    {
112        self.add_plugins(PrettyTextMaterialPlugin::<T>::default())
113            .register_pretty_effect::<T>(tag)
114    }
115}
116
117/// Adds the necessary ECS resources and render logic to enable rendering entities
118/// using the given [`GlyphMaterial`] asset type.
119#[derive(Debug)]
120pub struct PrettyTextMaterialPlugin<T>(PhantomData<T>);
121
122impl<T> Default for PrettyTextMaterialPlugin<T> {
123    fn default() -> Self {
124        PrettyTextMaterialPlugin(PhantomData)
125    }
126}
127
128impl<T> Plugin for PrettyTextMaterialPlugin<T>
129where
130    T: GlyphMaterial,
131    T::Data: PartialEq + Eq + std::hash::Hash + Clone,
132{
133    fn build(&self, app: &mut App) {
134        app.add_plugins(GlyphMaterialPlugin::<T>::default())
135            .register_type::<PrettyTextMaterial<T>>();
136    }
137}