bevy_manim/
bevy.rs

1use bevy::
2{
3    asset::{Handle, Assets},
4    app::{Plugin, AppBuilder},
5    render::
6    {
7        mesh::Mesh,
8        entity::MeshBundle,
9        pipeline::{RenderPipelines, PipelineDescriptor, RenderPipeline},
10        shader::{Shader, ShaderStages, ShaderStage},
11    },
12    ecs::
13    {
14        entity::Entity,
15        system::{IntoSystem, Commands, ResMut, Query, Res},
16        query::{With, Added},
17        // schedule::ExclusiveSystemDescriptorCoercion,
18    },
19    core::Time,
20};
21
22use crate::play::ScheduledAnimation;
23use crate::core::{PackedAnimation, AnimationKind};
24use crate::draw::Drawing;
25use std::time::Duration;
26
27const VERTEX_SHADER: &str = r#"
28#version 450
29
30layout(location = 0) in vec3 Vertex_Position;
31layout(location = 1) in vec4 Vertex_Color;
32layout(location = 0) out vec4 v_color;
33
34layout(set = 0, binding = 0) uniform CameraViewProj {
35    mat4 ViewProj;
36};
37
38layout(set = 1, binding = 0) uniform Transform {
39    mat4 Model;
40};
41
42void main() {
43    gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0);
44    v_color = Vertex_Color;
45}
46"#;
47
48const FRAGMENT_SHADER: &str = r#"
49#version 450
50
51layout(location = 0) in vec4 v_color;
52layout(location = 0) out vec4 o_Target;
53
54void main() {
55    o_Target = v_color;
56}
57"#;
58
59pub struct ManimRenderPipelines
60{
61    pub pipelines: RenderPipelines
62}
63
64
65pub struct ManimPlugin;
66impl Plugin for ManimPlugin
67{
68    fn build(&self, app: &mut AppBuilder)
69    {
70        app.add_plugin(registration::RegisterSystems);
71        app.add_system(animation_processor.system());
72
73        let world = app.world_mut();
74        let mut shaders = world.get_resource_mut::<Assets<Shader>>().unwrap();
75        let pipeline = PipelineDescriptor::default_config(ShaderStages {
76            vertex: shaders.add(Shader::from_glsl(ShaderStage::Vertex, VERTEX_SHADER)),
77            fragment: Some(shaders.add(Shader::from_glsl(ShaderStage::Fragment, FRAGMENT_SHADER))),
78        });
79        drop(shaders);
80        let mut pipelines = world.get_resource_mut::<Assets<PipelineDescriptor>>().unwrap();
81        let pipeline_handle = pipelines.add(pipeline);
82        let render_pipelines = RenderPipelines::from_pipelines(vec![RenderPipeline::new(
83            pipeline_handle,
84        )]);
85        world.insert_resource(ManimRenderPipelines { pipelines: render_pipelines })
86    }
87}
88
89pub struct ManimDrawing;
90
91fn animation_processor
92(
93    mut commands: Commands,
94    mut animated: Query<(Entity, &Handle<Mesh>, &mut ScheduledAnimation), With<ManimDrawing>>,
95    mut meshes: ResMut<Assets<Mesh>>,
96    time: Res<Time>,
97)
98{
99    let since_start = time.seconds_since_startup() as f32;
100    for (entity, mesh_handle, mut scheduled_animation) in animated.iter_mut()
101    {
102        if let Some(mesh) = meshes.get_mut(mesh_handle.clone())
103        {
104            let progress = (since_start - scheduled_animation.start) / scheduled_animation.duration.as_secs_f32();
105            if progress <= 0.0
106            {
107                configure_mesh(mesh, &scheduled_animation.animation, 0.0);
108            }
109            else if progress > 0.0 && progress < 1.0
110            {
111                configure_mesh(mesh, &scheduled_animation.animation, progress);
112            }
113            else if progress >= 1.0
114            {
115                if scheduled_animation.is_loop
116                {
117                    scheduled_animation.start = since_start
118                }
119                else
120                {
121                    commands.entity(entity).remove::<ScheduledAnimation>();
122                }
123            }
124        }
125        else
126        {
127            commands.entity(entity).remove::<ScheduledAnimation>();
128        }
129    }
130}
131
132fn configure_mesh(mesh: &mut Mesh, animation: &PackedAnimation, progress: f32)
133{
134    let mut tmp = animation.lock().unwrap();
135
136    // this line causes lag
137    // if tmp.get_progress() == progress { return }
138
139    tmp.set_progress(progress);
140    mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, tmp.get_vertices());
141    mesh.set_attribute(Mesh::ATTRIBUTE_COLOR, tmp.get_colors());
142    mesh.set_indices(Some(tmp.get_indices()));
143}
144
145pub struct AnimationDescription<A: AnimationKind>
146{
147    pub animation: A,
148    pub start: f32,
149    pub duration: Duration,
150    pub is_loop: bool,
151}
152
153pub(crate) fn register_drawing<T: Drawing>
154(
155    mut commands: Commands,
156    query: Query<(Entity, &T), Added<T>>,
157    mut meshes: ResMut<Assets<Mesh>>,
158    pipeline: Res<ManimRenderPipelines>
159)
160{
161    for (entity, drawing) in query.iter()
162    {
163        let mesh = drawing.get_mesh();
164        let handle = meshes.add(mesh);
165        commands.entity(entity)
166            .insert(ManimDrawing)
167            .insert_bundle(MeshBundle
168            {
169                mesh: handle,
170                render_pipelines: pipeline.pipelines.clone(),
171                ..Default::default()
172            });
173    }
174}
175
176pub(crate) fn register_animation<D: Drawing, A: AnimationKind>
177(
178    mut commands: Commands,
179    query: Query<(Entity, &D, &AnimationDescription<A>), Added<AnimationDescription<A>>>
180)
181{
182    for (entity, drawing, animation) in query.iter()
183    {
184        commands.entity(entity)
185            .remove::<AnimationDescription<A>>()
186            .insert(ScheduledAnimation
187            {
188                is_loop: animation.is_loop,
189                start: animation.start,
190                duration: animation.duration,
191                animation: drawing.animate(animation.animation.clone())
192            });
193    }
194}
195
196
197mod registration
198{
199    use bevy::app::{Plugin, AppBuilder, CoreStage};
200    use bevy::ecs::system::IntoSystem;
201    use crate::bevy::register_animation;
202    use crate::bevy::register_drawing;
203    use crate::draw::primitives::*;
204    use crate::anim::emergence::*;
205
206    macro_rules! nested_register {
207        ($app:expr; ($($draw:ident),*) $anim:tt) => {
208            $(nested_register!(@register $app; $draw $anim);)*
209        };
210        (@register $app:expr; $draw:ident ($($anim:ident),*)) => {
211            $app.add_system_to_stage(CoreStage::PostUpdate, register_drawing::<$draw>.system());
212            $($app.add_system_to_stage(CoreStage::Last, register_animation::<$draw, $anim>.system());)*
213        };
214    }
215
216    pub(crate) struct RegisterSystems;
217    impl Plugin for RegisterSystems
218    {
219        fn build(&self, app: &mut AppBuilder)
220        {
221            nested_register!(
222            app;
223            (Line, Point, Rect, Polygon, Circle)
224            (Fade, FromPoint)
225            );
226        }
227    }
228}