playback in headless mode

This commit is contained in:
Lee-Orr
2024-03-21 18:05:10 -04:00
parent 081443e04d
commit 4b07ed3f39
5 changed files with 58 additions and 30 deletions
+2
View File
@@ -41,3 +41,5 @@ Thumbs.db
/target /target
/screen_shots /screen_shots
/frames
+7 -6
View File
@@ -17,7 +17,7 @@ use itertools::Itertools;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use space_editor::{prelude::PrefabMarker, space_prefab::editor_registry::EditorRegistryExt}; use space_editor::{prelude::PrefabMarker, space_prefab::editor_registry::EditorRegistryExt};
use crate::interaction::{MarkedForDeletion, Selectable}; use crate::{headless::HeadlessSettings, interaction::{MarkedForDeletion, Selectable}, StudioMode};
pub struct TimelinePlugin; pub struct TimelinePlugin;
@@ -135,7 +135,7 @@ impl Default for TransformKeyframe {
#[reflect(Component)] #[reflect(Component)]
pub struct Keyframe { pub struct Keyframe {
target: AnimationTarget, target: AnimationTarget,
time: f32 pub time: f32
} }
#[derive(Component, Clone, Debug, Default, Reflect, PartialEq, Eq, PartialOrd, Ord)] #[derive(Component, Clone, Debug, Default, Reflect, PartialEq, Eq, PartialOrd, Ord)]
@@ -262,9 +262,12 @@ fn process_timeline_events(
} }
} }
fn update_tick(mut r_global_seek_time: ResMut<GlobalSeekTime>, time: Res<Time>) { fn update_tick(mut r_global_seek_time: ResMut<GlobalSeekTime>, time: Res<Time>, headless_settings: Option<Res<HeadlessSettings>>) {
if r_global_seek_time.playing { if r_global_seek_time.playing {
let delta = time.delta_seconds(); let delta = match headless_settings {
Some(headless_settings) => headless_settings.frame_delta,
None => time.delta_seconds()
};
r_global_seek_time.time += delta; r_global_seek_time.time += delta;
r_global_seek_time.delta = Some(delta); r_global_seek_time.delta = Some(delta);
} }
@@ -529,7 +532,6 @@ pub fn timeline_bound_component_animator_system<T: Component>(
events: ResMut<Events<TweenCompleted>>, events: ResMut<Events<TweenCompleted>>,
) { ) {
if time.is_changed() && time.time >= 0.0 { if time.is_changed() && time.time >= 0.0 {
info!("Animating with time: {time:?}");
let mut events: Mut<Events<TweenCompleted>> = events.into(); let mut events: Mut<Events<TweenCompleted>> = events.into();
for (entity, target, mut animator) in query.iter_mut() { for (entity, target, mut animator) in query.iter_mut() {
if animator.tweenable().duration() <= Duration::from_secs_f32(0.0001) { if animator.tweenable().duration() <= Duration::from_secs_f32(0.0001) {
@@ -537,7 +539,6 @@ pub fn timeline_bound_component_animator_system<T: Component>(
} }
let mut target = ComponentTarget::new(target); let mut target = ComponentTarget::new(target);
if let Some(delta) = time.delta { if let Some(delta) = time.delta {
info!("Playing animation with delta {delta}");
animator.tweenable_mut().tick( animator.tweenable_mut().tick(
Duration::from_secs_f32(delta), Duration::from_secs_f32(delta),
&mut target, &mut target,
+41 -7
View File
@@ -9,11 +9,7 @@ use space_editor::prelude::{EditorEvent, EditorPrefabPath};
use wgpu::Extent3d; use wgpu::Extent3d;
use crate::{ use crate::{
bvh::{BvhAnimCompleteEvent, TestBvh}, anim::timeline_animation::{Keyframe, TimelineInteractionEvent}, bvh::{BvhAnimCompleteEvent, TestBvh}, scene::SceneState, screenshot::{ScreenCapture, ScreenShotPlugin}, BvhPlugin, CommonPlugins, LoadLibraryObjectEvent, LoadSceneEvent, MainCamera, MixamoImportEvent, MixamoPlugin, RemoteAssetPlugin, SkyboxId, StudioMode, WorldGridPlugin
scene::SceneState,
screenshot::{ScreenCapture, ScreenShotPlugin},
BvhPlugin, CommonPlugins, LoadLibraryObjectEvent, LoadSceneEvent, MainCamera,
MixamoImportEvent, MixamoPlugin, RemoteAssetPlugin, SkyboxId, StudioMode, WorldGridPlugin,
}; };
#[derive(Debug, Resource, Clone)] #[derive(Debug, Resource, Clone)]
@@ -24,6 +20,14 @@ pub enum HeadlessMode {
None, None,
} }
#[derive(Resource)]
pub enum CameraMotion {
Orbit(f32),
Pan(f32),
Zoom(f32),
Static,
}
pub fn start_headless( pub fn start_headless(
mode: HeadlessMode, mode: HeadlessMode,
fps: u32, fps: u32,
@@ -39,6 +43,8 @@ pub fn start_headless(
max_frames: frames_to_capture, max_frames: frames_to_capture,
output_dims: image_res, output_dims: image_res,
output_dir: outout_dir.to_string(), output_dir: outout_dir.to_string(),
fps: fps as f32,
frame_delta: 1.0 / (fps as f32),
}); });
app.add_plugins(( app.add_plugins((
@@ -51,7 +57,7 @@ pub fn start_headless(
close_when_requested: true, close_when_requested: true,
}), }),
ScreenShotPlugin, ScreenShotPlugin,
CommonPlugins.build().disable::<WorldGridPlugin>(), CommonPlugins.build().disable::<WorldGridPlugin>()
)); ));
match mode { match mode {
@@ -80,7 +86,8 @@ pub fn start_headless(
cmd.insert_resource(SkyboxId(skybox.clone())); cmd.insert_resource(SkyboxId(skybox.clone()));
ew.send(EditorEvent::Load(EditorPrefabPath::File(scene.clone()))) ew.send(EditorEvent::Load(EditorPrefabPath::File(scene.clone())))
}, },
); )
.add_systems(OnEnter(SceneState::Active), start_timeline_animation);
} }
HeadlessMode::Mixamo(mixamo_path, skybox) => { HeadlessMode::Mixamo(mixamo_path, skybox) => {
app.add_plugins(MixamoPlugin); app.add_plugins(MixamoPlugin);
@@ -120,6 +127,8 @@ pub struct HeadlessSettings {
pub output_dims: Extent3d, pub output_dims: Extent3d,
pub output_dir: String, pub output_dir: String,
pub max_frames: u32, pub max_frames: u32,
pub fps: f32,
pub frame_delta: f32,
} }
impl Default for HeadlessSettings { impl Default for HeadlessSettings {
@@ -132,6 +141,8 @@ impl Default for HeadlessSettings {
}, },
output_dir: "./screen_shots/".to_string(), output_dir: "./screen_shots/".to_string(),
max_frames: u32::MAX, max_frames: u32::MAX,
fps: 24.0,
frame_delta: 1.0 / 24.0
} }
} }
} }
@@ -172,3 +183,26 @@ fn check_for_mixamo_capture_complete(
} }
} }
} }
fn start_timeline_animation(
mut headless_settings: ResMut<HeadlessSettings>,
keyframes: Query<&Keyframe>,
camera: Query<Entity, With<MainCamera>>,
mut commands: Commands,
mut ev: EventWriter<TimelineInteractionEvent>
) {
if keyframes.is_empty() {
return;
}
commands.remove_resource::<CameraMotion>();
let total_time = keyframes.iter().fold(0f32, |previous, keyframe| previous.max(keyframe.time));
let frames = (total_time * headless_settings.fps).ceil() as u32;
headless_settings.max_frames = frames;
ev.send(TimelineInteractionEvent::Play);
let camera = camera.single();
commands.entity(camera).insert(
ScreenCapture::from_out(&headless_settings.output_dir)
//Part of quick hack, see screenshot.rs#261
.with_max_frames(headless_settings.max_frames + 3),
);
}
+5 -10
View File
@@ -1,7 +1,7 @@
use bevy::{prelude::*, render::render_resource::Extent3d, transform::components::Transform}; use bevy::{prelude::*, render::render_resource::Extent3d, transform::components::Transform};
use clap::{Args, Parser, Subcommand}; use clap::{Args, Parser, Subcommand};
use studio::{ use studio::{
headless::{start_headless, HeadlessMode}, headless::{start_headless, CameraMotion, HeadlessMode},
scene::SceneState, scene::SceneState,
}; };
@@ -180,19 +180,14 @@ fn main() {
app.run(); app.run();
} }
#[derive(Resource)]
enum CameraMotion {
Orbit(f32),
Pan(f32),
Zoom(f32),
Static,
}
pub(crate) fn update_camera_motion( pub(crate) fn update_camera_motion(
mut camera: Query<&mut Transform, (With<Camera>, Without<Parent>)>, mut camera: Query<&mut Transform, (With<Camera>, Without<Parent>)>,
camera_motion: Res<CameraMotion>, camera_motion: Option<Res<CameraMotion>>,
time: Res<Time>, time: Res<Time>,
) { ) {
let Some(camera_motion) = camera_motion else {
return;
};
println!("{}", camera.iter().count()); println!("{}", camera.iter().count());
let Ok(mut main_cam_transform) = camera.get_single_mut() else { let Ok(mut main_cam_transform) = camera.get_single_mut() else {
return; return;
+3 -7
View File
@@ -57,7 +57,7 @@ impl Plugin for ScenePlugin {
) )
.add_systems( .add_systems(
Update, Update,
(init_object_loading, init_scene_import, init_scene_loading) (init_object_loading, init_scene_import, init_scene_loading, start_capture.run_if(is_headless))
.in_set(SceneSpawnerSystem), .in_set(SceneSpawnerSystem),
) )
.add_systems( .add_systems(
@@ -65,7 +65,6 @@ impl Plugin for ScenePlugin {
( (
tag_scene_elements, tag_scene_elements,
frame_scene_in_view, frame_scene_in_view,
start_capture.run_if(is_headless),
) )
.chain() .chain()
.in_set(SceneSpawnerSystem), .in_set(SceneSpawnerSystem),
@@ -339,10 +338,7 @@ fn initialiaze_headless(
}, },
..default() ..default()
}, },
CameraControllerBundle::new(focus, camera_xform.translation), CameraControllerBundle::new(focus, camera_xform.translation)
ScreenCapture::from_out(&headless_settings.output_dir)
//Part of quick hack, see screenshot.rs#261
.with_max_frames(headless_settings.max_frames + 3),
)); ));
} }
@@ -520,7 +516,7 @@ fn frame_scene_in_view(
cam_xform.translation = cam.focus + cam_xform.back() * radius; cam_xform.translation = cam.focus + cam_xform.back() * radius;
} }
fn start_capture(mut captures: Query<&mut ScreenCapture>) { fn start_capture(mut captures: Query<&mut ScreenCapture, Added<ScreenCapture>>) {
for mut capture in captures.iter_mut() { for mut capture in captures.iter_mut() {
capture.start_capturing(); capture.start_capturing();
} }