Compare commits

...
Author SHA1 Message Date
Paul Sajna 6430f63702 update workspace 2023-06-21 01:20:38 -07:00
Paul Sajna 9752f39736 headless bevy 2023-06-21 01:17:41 -07:00
5 changed files with 2509 additions and 573 deletions
Generated
+2265 -110
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -1,4 +1,5 @@
[workspace]
resolver="2"
members = [
# Types: common strongly-typed tokens, ids, payloads, etc.
@@ -49,6 +50,7 @@ members = [
# Programs: Desktop apps
"crates/programs/aichatbot-sidecar",
"crates/programs/bevy-experiments",
# Services: API servers, jobs, workers, etc.
"crates/service/job/analytics_job",
+126 -7
View File
@@ -273,6 +273,15 @@ dependencies = [
"bevy_internal",
]
[[package]]
name = "bevy-experiments"
version = "0.1.0"
dependencies = [
"bevy",
"bevy_framepace",
"bevy_image_export",
]
[[package]]
name = "bevy_a11y"
version = "0.10.1"
@@ -468,6 +477,16 @@ dependencies = [
"encase_derive_impl",
]
[[package]]
name = "bevy_framepace"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1825c5a485aa2a2ffd91cc1faa1f4d621c804a893ead88cafedbe1ed6c51d34"
dependencies = [
"bevy",
"spin_sleep",
]
[[package]]
name = "bevy_gilrs"
version = "0.10.1"
@@ -525,6 +544,20 @@ dependencies = [
"smallvec",
]
[[package]]
name = "bevy_image_export"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdcb6aae8af3981628eb120cad4cc348f7012c13323b089e184dbc8d87c4dfcd"
dependencies = [
"bevy",
"bytemuck",
"futures",
"futures-lite",
"image",
"wgpu",
]
[[package]]
name = "bevy_input"
version = "0.10.1"
@@ -1442,12 +1475,48 @@ dependencies = [
"libc",
]
[[package]]
name = "futures"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
[[package]]
name = "futures-executor"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.28"
@@ -1469,6 +1538,47 @@ dependencies = [
"waker-fn",
]
[[package]]
name = "futures-macro"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.18",
]
[[package]]
name = "futures-sink"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
[[package]]
name = "futures-task"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
[[package]]
name = "futures-util"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "fxhash"
version = "0.2.1"
@@ -2388,6 +2498,12 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.27"
@@ -2673,6 +2789,16 @@ dependencies = [
"serde",
]
[[package]]
name = "spin_sleep"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cafa7900db085f4354dbc7025e25d7a839a14360ea13b5fc4fd717f2d3b23134"
dependencies = [
"once_cell",
"winapi",
]
[[package]]
name = "spirv"
version = "0.2.0+1.5.4"
@@ -2742,13 +2868,6 @@ dependencies = [
"slotmap",
]
[[package]]
name = "temp-bevy"
version = "0.1.0"
dependencies = [
"bevy",
]
[[package]]
name = "termcolor"
version = "1.2.0"
+7 -4
View File
@@ -1,5 +1,5 @@
[package]
name = "temp-bevy"
name = "bevy-experiments"
version = "0.1.0"
edition = "2021"
@@ -7,7 +7,10 @@ edition = "2021"
[dependencies]
bevy = "0.10.1"
bevy_framepace = "0.12.1"
bevy_image_export = "0.7.2"
#[profile.release]
#opt-level = "z"
#lto = "fat"
[profile.release]
opt-level = "z"
lto = "fat"
+109 -452
View File
@@ -1,235 +1,62 @@
//! Loads and renders a glTF file as a scene.
use std::f32::consts::*;
use core::time::Duration;
use bevy::{
pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap},
prelude::*,
scene::SceneInstance,
animation::{AnimationClip, AnimationPlayer},
};
use bevy::{
input::mouse::MouseMotion,
render::{
camera::RenderTarget,
render_resource::{
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
},
},
};
use bevy_image_export::{ImageExportPlugin, ImageExportSource, ImageExportBundle};
use bevy::winit::WinitPlugin;
use bevy::app::ScheduleRunnerSettings;
use bevy::app::ScheduleRunnerPlugin;
use bevy::app::AppExit;
// number of frames after which the application exits
const DESIRED_FRAME_COUNT: u32 = 120;
fn main() {
let export_plugin = ImageExportPlugin::default();
let export_threads = export_plugin.threads.clone();
App::new()
.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 1.0 / 5.0f32,
})
.insert_resource(DirectionalLightShadowMap { size: 4096 })
.add_plugins(DefaultPlugins)
.insert_resource(ScheduleRunnerSettings::run_loop(Duration::from_secs_f64(0.0)))
.add_plugins(DefaultPlugins.build().disable::<WinitPlugin>())
.add_plugin(ScheduleRunnerPlugin)
.add_plugin(export_plugin)
.add_startup_system(setup)
.add_system(setup_scene_once_loaded)
//.add_system(animate_light_direction)
//.add_system(animate_model_direction)
.add_system(keyboard_animation_control)
.add_system(camera_controller)
.add_system(text_update_system)
.add_system(update)
.run();
export_threads.finish();
}
#[derive(Resource)]
struct Animations(Vec<Handle<AnimationClip>>);
#[derive(Component)]
struct PosText;
#[derive(Component)]
struct RotText;
#[derive(Component)]
struct MainCamera;
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.insert_resource(Animations(vec![
asset_server.load("Roko_Anim_Wave_noOptimization.glb#Animation0"),
]));
commands.spawn((
Camera3dBundle {
transform: Transform::from_xyz(0.0, 1.5, 1.5),
//transform: Transform {
//translation: Vec3::new(0.0, -10.0, 5.0),
//rotation: Quat::from_xyzw(0.574, 0.0, 0.0, 0.819),
//scale: Vec3::new(1.0, 1.0, 1.0),
//},
//projection: Projection::Orthographic(OrthographicProjection{
//near: 0.1,
//far: 100.0,
//scale: 7.312,
//..default()
//}
//),
..default()
},
MainCamera,
CameraController::default(),
EnvironmentMapLight {
diffuse_map: asset_server.load("pisa_diffuse_rgb9e5_zstd.ktx2"),
specular_map: asset_server.load("pisa_specular_rgb9e5_zstd.ktx2"),
},
));
commands.spawn((
TextBundle::from_sections([
TextSection::new(
"Position: ",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 20.0,
color: Color::WHITE,
},
),
TextSection::new(
"X: ",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 20.0,
color: Color::WHITE,
},
),
TextSection::from_style(TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 20.0,
color: Color::GOLD,
}),
TextSection::new(
"Y: ",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 20.0,
color: Color::WHITE,
},
),
TextSection::from_style(TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 20.0,
color: Color::GOLD,
}),
TextSection::new(
"Z: ",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 20.0,
color: Color::WHITE,
},
),
TextSection::from_style(TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 20.0,
color: Color::GOLD,
}),
]).with_style(
Style {
position_type: PositionType::Absolute,
position: UiRect {
right: Val::Px(00.0),
..default()
},
..default()
}
),
PosText,
));
commands.spawn((
TextBundle::from_sections([
TextSection::new(
"Rotation: ",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 20.0,
color: Color::WHITE,
},
),
TextSection::new(
"X: ",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 20.0,
color: Color::WHITE,
},
),
TextSection::from_style(TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 20.0,
color: Color::GOLD,
}),
TextSection::new(
"Y: ",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 20.0,
color: Color::WHITE,
},
),
TextSection::from_style(TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 20.0,
color: Color::GOLD,
}),
TextSection::new(
"Z: ",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 20.0,
color: Color::WHITE,
},
),
TextSection::from_style(TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 20.0,
color: Color::GOLD,
}),
TextSection::new(
"W: ",
TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 20.0,
color: Color::WHITE,
},
),
TextSection::from_style(TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 20.0,
color: Color::GOLD,
}),
]).with_style(
Style {
position_type: PositionType::Absolute,
position: UiRect {
top: Val::Px(20.0),
right: Val::Px(00.0),
..default()
},
..default()
}
),
RotText,
));
commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight {
shadows_enabled: true,
..default()
},
// This is a relatively small scene, so use tighter shadow
// cascade bounds than the default for better quality.
// We also adjusted the shadow map to be larger since we're
// only using a single cascade.
cascade_shadow_config: CascadeShadowConfigBuilder {
num_cascades: 1,
maximum_distance: 1.6,
@@ -243,276 +70,106 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
},
..default()
});
commands.spawn(SceneBundle {
scene: asset_server.load("Roko_nogamerig.glb#Scene0"),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
//transform: Transform::from_rotation(Quat::from_rotation_x(3.14)),
..default()
});
//commands.spawn(AnimationPlayer::default());
commands.spawn((
SceneBundle {
scene: asset_server.load("Roko_nogamerig.glb#Scene0"),
transform: Transform::from_xyz(0.0, 0.0, 0.0),
..default()
},
Roko
));
}
fn animate_light_direction(
time: Res<Time>,
mut query: Query<&mut Transform, With<DirectionalLight>>,
) {
for mut transform in &mut query {
transform.rotation = Quat::from_euler(
EulerRot::ZYX,
0.0,
1.0 * PI,
-FRAC_PI_4,
);
}
}
fn animate_model_direction(
time: Res<Time>,
mut query: Query<&mut Transform, With<SceneInstance>>,
) {
for mut transform in &mut query {
transform.rotation = Quat::from_euler(
EulerRot::XYZ,
0.0,
1.0 * PI,
0.0,
);
}
}
// Once the scene is loaded, start the animation
fn setup_scene_once_loaded(
animations: Res<Animations>,
mut player: Query<&mut AnimationPlayer>,
mut done: Local<bool>,
) {
if !*done {
if let Ok(mut player) = player.get_single_mut() {
player.play(animations.0[0].clone_weak()).repeat();
*done = true;
}
}
}
//fn play_animation(
//mut animation_player: Query<&mut AnimationPlayer>,
//animations: Res<Animations>,
//mut done: Local<bool>,
//time: Res<Time>,
//assets: Res<Assets<AnimationClip>>,
//) {
//if !*done {
//if let Ok(mut player) = animation_player.get_single_mut() {
////println!("{:#?}", *assets.get(&animations.0[0]).unwrap().curves());
//player.play(animations.0[0].clone_weak()).repeat();
//*done = true;
//} else {
//println!("fail?!");
//}
//}
fn keyboard_animation_control(
keyboard_input: Res<Input<KeyCode>>,
mut animation_player: Query<&mut AnimationPlayer>,
animations: Res<Animations>,
mut current_animation: Local<usize>,
) {
if let Ok(mut player) = animation_player.get_single_mut() {
if keyboard_input.just_pressed(KeyCode::Space) {
if player.is_paused() {
player.resume();
} else {
player.pause();
}
}
if keyboard_input.just_pressed(KeyCode::Up) {
let speed = player.speed();
player.set_speed(speed * 1.2);
}
if keyboard_input.just_pressed(KeyCode::Down) {
let speed = player.speed();
player.set_speed(speed * 0.8);
}
if keyboard_input.just_pressed(KeyCode::Left) {
let elapsed = player.elapsed();
player.set_elapsed(elapsed - 0.1);
}
if keyboard_input.just_pressed(KeyCode::Right) {
let elapsed = player.elapsed();
player.set_elapsed(elapsed + 0.1);
}
if keyboard_input.just_pressed(KeyCode::Return) {
*current_animation = (*current_animation + 1) % animations.0.len();
player
.play_with_transition(
animations.0[*current_animation].clone_weak(),
Duration::from_millis(250),
)
.repeat();
}
}
}
#[derive(Component)]
pub struct CameraController {
pub enabled: bool,
pub initialized: bool,
pub sensitivity: f32,
pub key_forward: KeyCode,
pub key_back: KeyCode,
pub key_left: KeyCode,
pub key_right: KeyCode,
pub key_up: KeyCode,
pub key_down: KeyCode,
pub key_run: KeyCode,
pub mouse_key_enable_mouse: MouseButton,
pub keyboard_key_enable_mouse: KeyCode,
pub walk_speed: f32,
pub run_speed: f32,
pub friction: f32,
pub pitch: f32,
pub yaw: f32,
pub velocity: Vec3,
}
struct Roko;
impl Default for CameraController {
fn default() -> Self {
Self {
enabled: true,
initialized: false,
sensitivity: 0.5,
key_forward: KeyCode::W,
key_back: KeyCode::S,
key_left: KeyCode::A,
key_right: KeyCode::D,
key_up: KeyCode::E,
key_down: KeyCode::Q,
key_run: KeyCode::LShift,
mouse_key_enable_mouse: MouseButton::Left,
keyboard_key_enable_mouse: KeyCode::M,
walk_speed: 2.0,
run_speed: 6.0,
friction: 0.5,
pitch: 0.0,
yaw: 0.0,
velocity: Vec3::ZERO,
}
}
}
#[derive(Component)]
struct ExportBundleMarker;
fn update(
mut commands: Commands,
export_bundles: Query<Entity, With<ExportBundleMarker>>,
_roko: Query<Entity, With<Roko>>,
mut frame: Local<u32>,
mut exit: EventWriter<AppExit>,
mut animation_player: Query<&mut AnimationPlayer>,
animations: Res<Animations>,
mut anim_started: Local<bool>,
mut img_exp_started: Local<bool>,
mut images: ResMut<Assets<Image>>,
mut export_sources: ResMut<Assets<ImageExportSource>>
pub fn camera_controller(
time: Res<Time>,
mut mouse_events: EventReader<MouseMotion>,
mouse_button_input: Res<Input<MouseButton>>,
key_input: Res<Input<KeyCode>>,
mut move_toggled: Local<bool>,
mut query: Query<(&mut Transform, &mut CameraController), With<Camera>>,
) {
let dt = time.delta_seconds();
if !*anim_started {
if let Ok(mut player) = animation_player.get_single_mut() {
player.play(animations.0[0].clone_weak());//.repeat();
*anim_started = true;
}
}
if let Ok((mut transform, mut options)) = query.get_single_mut() {
if !options.initialized {
let (yaw, pitch, _roll) = transform.rotation.to_euler(EulerRot::YXZ);
options.yaw = yaw;
options.pitch = pitch;
options.initialized = true;
}
if !options.enabled {
return;
}
// Handle key input
let mut axis_input = Vec3::ZERO;
if key_input.pressed(options.key_forward) {
axis_input.z += 1.0;
}
if key_input.pressed(options.key_back) {
axis_input.z -= 1.0;
}
if key_input.pressed(options.key_right) {
axis_input.x += 1.0;
}
if key_input.pressed(options.key_left) {
axis_input.x -= 1.0;
}
if key_input.pressed(options.key_up) {
axis_input.y += 1.0;
}
if key_input.pressed(options.key_down) {
axis_input.y -= 1.0;
}
if key_input.just_pressed(options.keyboard_key_enable_mouse) {
*move_toggled = !*move_toggled;
}
// Apply movement update
if axis_input != Vec3::ZERO {
let max_speed = if key_input.pressed(options.key_run) {
options.run_speed
} else {
options.walk_speed
// wait for the animation to start before we start exporting images
// (tryna avoid t-pose frames)
if let Ok(player) = animation_player.get_single() {
if *anim_started && !*img_exp_started && player.elapsed() >= 1.0 / 24.0 {
let output_texture_handle = {
let size = Extent3d {
width: 1920,
height: 1080,
..default()
};
options.velocity = axis_input.normalize() * max_speed;
} else {
let friction = options.friction.clamp(0.0, 1.0);
options.velocity *= 1.0 - friction;
if options.velocity.length_squared() < 1e-6 {
options.velocity = Vec3::ZERO;
}
}
let forward = transform.forward();
let right = transform.right();
transform.translation += options.velocity.x * dt * right
+ options.velocity.y * dt * Vec3::Y
+ options.velocity.z * dt * forward;
let mut export_texture = Image {
texture_descriptor: TextureDescriptor {
label: None,
size,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8UnormSrgb,
mip_level_count: 1,
sample_count: 1,
usage: TextureUsages::COPY_DST
| TextureUsages::COPY_SRC
| TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
},
..default()
};
export_texture.resize(size);
// Handle mouse input
let mut mouse_delta = Vec2::ZERO;
if mouse_button_input.pressed(options.mouse_key_enable_mouse) || *move_toggled {
for mouse_event in mouse_events.iter() {
mouse_delta += mouse_event.delta;
}
images.add(export_texture)
};
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(0.0, 1.5, 1.5),
camera: Camera {
target: RenderTarget::Image(output_texture_handle.clone()),
..default()
},
..default()
});
commands.spawn((
ImageExportBundle {
source: export_sources.add(bevy_image_export::ImageExportSource(output_texture_handle)),
..default()
},
ExportBundleMarker,
));
*img_exp_started = true;
}
if mouse_delta != Vec2::ZERO {
// Apply look update
options.pitch = (options.pitch - mouse_delta.y * 0.5 * options.sensitivity * dt)
.clamp(-PI / 2., PI / 2.);
options.yaw -= mouse_delta.x * options.sensitivity * dt;
transform.rotation = Quat::from_euler(EulerRot::ZYX, 0.0, options.yaw, options.pitch);
}
if *frame == DESIRED_FRAME_COUNT {
commands.entity(export_bundles.single()).despawn();
exit.send(AppExit);
}
if *img_exp_started {
*frame += 1;
}
}
}
fn text_update_system(camera_q: Query<(&Camera, &GlobalTransform), With<MainCamera>>,
windows: Query<&mut Window>,
mut pos_text: Query<&mut Text, With<PosText>>,
mut rot_text: Query<&mut Text, Without<PosText>>
) {
let (camera, camera_transform) = camera_q.single();
let window = windows.get_single().unwrap();
let transform = camera_transform.compute_transform();
let position = transform.translation;
let rotation = transform.rotation;
for mut text in &mut pos_text {
text.sections[2].value = format!("{:.2} ", position.x);
text.sections[4].value = format!("{:.2} ", position.y);
text.sections[6].value = format!("{:.2} ", position.z);
}
for mut text in &mut rot_text {
text.sections[2].value = format!("{:.4} ", rotation.x);
text.sections[4].value = format!("{:.4} ", rotation.y);
text.sections[6].value = format!("{:.4} ", rotation.z);
text.sections[8].value = format!("{:.4} ", rotation.w);
}
}