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 },
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 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}