animation works

This commit is contained in:
Lee-Orr
2024-03-15 15:26:16 -04:00
parent e0b5d01d31
commit e816e9ba0f
5 changed files with 331 additions and 59 deletions
+232 -5
View File
@@ -1,11 +1,20 @@
use std::time::Duration;
use bevy::{
ecs::{
entity::{EntityMapper, MapEntities},
reflect::ReflectMapEntities,
},
prelude::*,
utils::HashSet,
};
use bevy_tweening::{
lens::{TransformPositionLens, TransformRotationLens, TransformScaleLens},
Animator, AnimatorState, EaseFunction, EaseMethod, Sequence, Targetable, Tracks, Tween,
TweenCompleted, TweeningPlugin,
};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use space_editor::space_prefab::editor_registry::EditorRegistryExt;
@@ -31,11 +40,21 @@ impl Plugin for TimelinePlugin {
app.insert_resource(GlobalSeekTime {
time: 0.,
playing: false,
});
delta: None,
})
.add_event::<TweenCompleted>();
app.add_systems(PreUpdate, (process_timeline_events, update_tick).chain());
app.add_systems(Update, process_keyframe_events);
app.add_systems(
Update,
(
process_keyframe_events,
timeline_bound_component_animator_system::<Transform>,
),
);
app.add_systems(PostUpdate, generate_transform_animation);
#[cfg(feature = "wasm")]
{
@@ -76,6 +95,7 @@ pub enum KeyframeInteractionEvent {
pub struct GlobalSeekTime {
pub time: f32,
pub playing: bool,
pub delta: Option<f32>,
}
#[derive(Clone, Debug, Copy, Default, Reflect, PartialEq, Serialize, Deserialize)]
@@ -180,15 +200,23 @@ fn process_timeline_events(
for event in e_timeline_event.read() {
match event {
TimelineInteractionEvent::Play => r_global_seek_time.playing = true,
TimelineInteractionEvent::Pause => r_global_seek_time.playing = false,
TimelineInteractionEvent::Seek(time) => r_global_seek_time.time = *time,
TimelineInteractionEvent::Pause => {
r_global_seek_time.playing = false;
}
TimelineInteractionEvent::Seek(time) => {
r_global_seek_time.time = *time;
}
}
r_global_seek_time.delta = None;
}
}
fn update_tick(mut r_global_seek_time: ResMut<GlobalSeekTime>, time: Res<Time>) {
if r_global_seek_time.playing {
r_global_seek_time.time += time.delta_seconds();
let delta = time.delta_seconds();
r_global_seek_time.time += delta;
r_global_seek_time.delta = Some(delta);
}
}
@@ -227,6 +255,205 @@ fn process_keyframe_events(
}
}
fn generate_transform_animation(
mut commands: Commands,
changed: Query<&Keyframe, (With<TransformKeyframe>, Changed<Keyframe>)>,
keyframes: Query<(&Keyframe, &Transform, &TransformKeyframe, Entity)>,
) {
let changed_targets = changed
.iter()
.map(|keyframe| keyframe.target)
.collect::<HashSet<_>>();
let keyframes = keyframes
.iter()
.filter(|(keyframe, _, _, _)| changed_targets.contains(&keyframe.target))
.group_by(|(keyframe, _, _, _)| keyframe.target);
for (target, keyframes) in &keyframes {
let keyframes = keyframes.sorted_by(|a, b| a.0.time.total_cmp(&b.0.time));
let (_, _, _, translation, rotation, scale) = keyframes.fold(
(
Option::<(f32, Vec3)>::None,
Option::<(f32, Quat)>::None,
Option::<(f32, Vec3)>::None,
Vec::<Tween<Transform>>::new(),
Vec::<Tween<Transform>>::new(),
Vec::<Tween<Transform>>::new(),
),
|(
last_translation,
last_rotation,
last_scale,
mut translation_list,
mut rotation_list,
mut scale_list,
),
(keyframe, transform, transform_keyframe, _)| {
let time = keyframe.time;
let translation = transform.translation;
let rotation = transform.rotation;
let scale = transform.scale;
info!("Processing Keyframe: {time} previous: {last_rotation:?}");
if (transform_keyframe.affect_translation) {
let (last_time, start) = match last_translation {
Some(v) => v,
None => (0., translation),
};
info!("Should apply translation");
if (time - last_time).abs() > 0.00001 {
translation_list.push(Tween::new(
EaseMethod::Linear,
Duration::from_secs_f32(time - last_time),
TransformPositionLens {
start,
end: translation,
},
));
}
}
if (transform_keyframe.affect_rotation) {
let (last_time, start) = match last_rotation {
Some(v) => v,
None => (0., rotation),
};
info!("Should apply rotation");
if (time - last_time).abs() > 0.00001 {
rotation_list.push(Tween::new(
EaseMethod::Linear,
Duration::from_secs_f32(time - last_time),
TransformRotationLens {
start,
end: rotation,
},
));
}
}
if (transform_keyframe.affect_scale) {
let (last_time, start) = match last_scale {
Some(v) => v,
None => (0., scale),
};
info!("Should apply scale");
if (time - last_time).abs() > 0.00001 {
scale_list.push(Tween::new(
EaseMethod::Linear,
Duration::from_secs_f32(time - last_time),
TransformScaleLens { start, end: scale },
));
}
}
let next_translation = if transform_keyframe.affect_translation {
Some((time, translation))
} else {
last_translation
};
let next_rotation = if transform_keyframe.affect_rotation {
Some((time, rotation))
} else {
last_rotation
};
let next_scale = if transform_keyframe.affect_scale {
Some((time, scale))
} else {
last_scale
};
(
next_translation,
next_rotation,
next_scale,
translation_list,
rotation_list,
scale_list,
)
},
);
let mut tracks = Vec::with_capacity(3);
if !translation.is_empty() {
tracks.push(Sequence::new(translation));
} else {
info!("No translation");
}
if !rotation.is_empty() {
tracks.push(Sequence::new(rotation));
}else {
info!("No rotation");
}
if !scale.is_empty() {
tracks.push(Sequence::new(scale));
}else {
info!("No scale");
}
if tracks.is_empty() {
commands.entity(target).remove::<Animator<Transform>>();
} else {
let tracks = Tracks::new(tracks);
commands.entity(target).insert(Animator::new(tracks));
}
}
}
struct ComponentTarget<'a, T: Component>(&'a mut T);
impl<'a, T: Component> ComponentTarget<'a, T> {
fn new(target: Mut<'a, T>) -> Self {
Self(target.into_inner())
}
}
impl<'a, T: Component> Targetable<T> for ComponentTarget<'a, T> {
fn target_mut(&mut self) -> &mut T {
self.0
}
}
pub fn timeline_bound_component_animator_system<T: Component>(
time: Res<GlobalSeekTime>,
mut query: Query<(Entity, &mut T, &mut Animator<T>)>,
events: ResMut<Events<TweenCompleted>>,
) {
if time.is_changed() {
info!("Animating with time: {time:?}");
let mut events: Mut<Events<TweenCompleted>> = events.into();
for (entity, target, mut animator) in query.iter_mut() {
if animator.tweenable().duration() <= Duration::from_secs_f32(0.0001) {
continue;
}
let mut target = ComponentTarget::new(target);
if let Some(delta) = time.delta {
info!("Playing animation with delta {delta}");
animator.tweenable_mut().tick(
Duration::from_secs_f32(delta),
&mut target,
entity,
&mut events,
);
} else {
info!("setting tween time to {}", time.time);
let mut tween = animator
.tweenable_mut();
tween.set_elapsed(Duration::from_secs_f32(time.time));
info!("Tween time set successfully");
tween.tick(Duration::ZERO, &mut target, entity, &mut events);
}
}
}
}
#[cfg(feature = "wasm")]
mod wasm {
use bevy::{prelude::*, utils::hashbrown::HashMap, window::PrimaryWindow};
+3 -3
View File
@@ -218,7 +218,7 @@ pub(crate) fn update_camera_motion(
// main_cam_transform.look_at(Vec3::ZERO, Vec3::Y);
}
fn zoom_camera(main_cam_transform: &mut Transform, time: &Time<()>, speed : f32) {
fn zoom_camera(main_cam_transform: &mut Transform, time: &Time<()>, speed: f32) {
let scaled_time = time.elapsed().as_secs_f32() * speed;
let normalized_cos = (-scaled_time.cos() + 1.0) / 2.0;
let start_pos = Vec3::new(0.366, 1.1, 2.5);
@@ -228,7 +228,7 @@ fn zoom_camera(main_cam_transform: &mut Transform, time: &Time<()>, speed : f32)
main_cam_transform.rotation = Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0);
}
fn pan_camera(main_cam_transform: &mut Transform, time: &Time<()>, speed : f32) {
fn pan_camera(main_cam_transform: &mut Transform, time: &Time<()>, speed: f32) {
let scaled_time = time.elapsed().as_secs_f32() * speed;
let normalized_cos = (-scaled_time.cos() + 1.0) / 2.0;
let start_pos = Vec3::new(0.676, 1.1, 0.119);
@@ -238,7 +238,7 @@ fn pan_camera(main_cam_transform: &mut Transform, time: &Time<()>, speed : f32)
main_cam_transform.rotation = Quat::from_euler(EulerRot::XYZ, 0.0, 0.0, 0.0);
}
fn orbit_camera(main_cam_transform: &mut Mut<'_, Transform>, time: &Res<'_, Time>, speed : f32) {
fn orbit_camera(main_cam_transform: &mut Mut<'_, Transform>, time: &Res<'_, Time>, speed: f32) {
let scaled_time = time.elapsed().as_secs_f32() * speed;
let max_angle = std::f32::consts::PI / 2.0;
let normalized_cos = (scaled_time.cos() + 1.0) / 2.0;
+7 -6
View File
@@ -144,9 +144,11 @@ fn update_screen_capture_on_extract_step(
fn extract_screen_capture_components(
mut commands: Commands,
screen_capure_comps: Extract<Query<(Entity, &ScreenCapture), With<Camera>>>,
scene_state : Extract<Res<State<SceneState>>>
scene_state: Extract<Res<State<SceneState>>>,
) {
if scene_state.get() != &SceneState::Active { return };
if scene_state.get() != &SceneState::Active {
return;
};
for (entity, screen_capture) in screen_capure_comps.iter() {
if screen_capture.should_capture() {
info!(
@@ -252,25 +254,24 @@ impl ViewNode for ScreenCaptureNode {
//==============================================================================
fn save_buffer_to_file(
mut frames_to_skip : Local<Option<u32>>,
mut frames_to_skip: Local<Option<u32>>,
buffers: Query<(&ScreenCapture, &ScreenCaptureBuffers)>,
device: Res<RenderDevice>,
) {
for (screen_capture, buffers) in buffers.iter() {
// ALERT!! ALERT!! Filthy Hack in effect
// Engine does not load scene in time for the first 3 frames
// So we skip them. Oops :(
// TODO: Fix the Race Condition and properly take screenshots.
let frames_skipped = frames_to_skip.unwrap_or(3);
if frames_skipped > 0 {
println!("Skipping Frame");
*frames_to_skip = Some(frames_skipped - 1);
return;
}
// Filthy Hack Ends
if buffers.main_bytes_per_row != buffers.main_padded_bytes_per_row {
println!("These don't match!");
}