Update to bevy 0.13 (#87)

Co-authored-by: Lee-Orr <lee-orr@users.noreply.github.com>
Co-authored-by: Danny McGee <dannymcgee@gmail.com>
This commit is contained in:
rewin
2024-04-10 04:47:40 +03:00
committed by GitHub
parent 03f90eee47
commit eb1198d440
67 changed files with 3747 additions and 1682 deletions
+4
View File
@@ -48,6 +48,10 @@ jobs:
sudo apt-get install -y --no-install-recommends \
libasound2-dev \
libudev-dev \
librust-wayland-commons-dev \
librust-wayland-sys-dev \
libwayland-client++0 \
libwayland-client0 \
git
- name: Install JS dependencies
run: npm ci
Generated
+3048 -1109
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -9,7 +9,7 @@ members = [
anyhow = "1.0.75"
[workspace.dependencies.bevy]
version = "0.12.1"
version = "0.13.2"
features = ["tonemapping_luts"]
[profile.dev-custom]
@@ -20,4 +20,4 @@ opt-level = 1
opt-level = 3
[profile.release]
opt-level = "s"
opt-level = 3
+6 -1
View File
@@ -23,6 +23,10 @@ RUN apt-get update \
fontconfig \
libfontconfig1-dev \
libssl-dev \
librust-wayland-commons-dev \
librust-wayland-sys-dev \
libwayland-client++0 \
libwayland-client0 \
pkg-config
# Install Rust
@@ -49,4 +53,5 @@ COPY ./ .
RUN LD_LIBRARY_PATH=/usr/lib:${LD_LIBRARY_PATH} \
$HOME/.cargo/bin/cargo build \
--release \
--bin studio-headless
--bin studio-headless \
--features headless
+4 -2
View File
@@ -1,6 +1,8 @@
{
"space_editor_core::hotkeys::HotkeySet<space_editor_ui::tools::gizmo::GizmoHotkey>": "{\"space_editor_core::hotkeys::HotkeySet<space_editor_ui::tools::gizmo::GizmoHotkey>\":(bindings:{Rotate:[R],Translate:[G],Scale:[S],Multiple:[ShiftLeft],Clone:[AltLeft],Delete:[X]},name:\"GizmoHotkey\")}",
"space_editor_ui::settings::NewWindowSettings": "{\"space_editor_ui::settings::NewWindowSettings\":(new_tab:SameNode)}",
"space_editor_core::hotkeys::HotkeySet<space_editor_ui::tools::gizmo::GizmoHotkey>": "{\"space_editor_core::hotkeys::HotkeySet<space_editor_ui::tools::gizmo::GizmoHotkey>\":(bindings:{Clone:[AltLeft],Rotate:[KeyR],Multiple:[ShiftLeft],Delete:[KeyX],Translate:[KeyG],Scale:[KeyS]},name:\"GizmoHotkey\")}",
"space_persistence::PersistenceSettings": "{\"space_persistence::PersistenceSettings\":(load_on_startup:true,save_on_close:true)}",
"space_undo::ChangeChainSettings": "{\"space_undo::ChangeChainSettings\":(max_change_chain_size:200)}",
"space_editor_ui::settings::GameModeSettings": "{\"space_editor_ui::settings::GameModeSettings\":(mode:Game3D)}",
"space_editor_ui::sizing::Sizing": "{\"space_editor_ui::sizing::Sizing\":(icon:Regular,gizmos:Gizmos,text:14.0)}",
"space_editor_ui::settings::NewWindowSettings": "{\"space_editor_ui::settings::NewWindowSettings\":(new_tab:SameNode)}",
}
@@ -75,9 +75,10 @@ export class CanvasProviderElement
@on("keyup")
_pipeKeyboardEvents(event: KeyboardEvent): void {
if (event.repeat) return;
if (this.#canvas != null) {
// If we own the canvas, we're going to catch events emitting from the
// canvas and then repeat them, eventually causing a stack overflow
const canvas = document.getElementById(this.#canvasId);
if (canvas && event.target === canvas) {
// If this event originated from the Bevy canvas, repeating it will just
// cause an infinite feedback loop and a stack overflow
return;
}
+19 -14
View File
@@ -14,14 +14,15 @@ path = "src/main.rs"
[[bin]]
name = "studio-headless"
required-features = ["headless"]
path = "src/main-headless.rs"
[dependencies]
bevy-inspector-egui = "0.22.0"
bevy_mod_raycast = "0.16.0"
bevy_rapier3d = "0.23.0"
bevy_mod_outline = "0.6.0"
bevy_web_asset = "0.7.0"
bevy-inspector-egui = "0.23.0"
bevy_mod_raycast = "0.17.0"
bevy_rapier3d = "0.25.0"
bevy_mod_outline = "0.7.0"
bevy_web_asset = "0.8.0"
bitflags = "2.4.0"
console_error_panic_hook = { version = "0.1.7", optional = true }
itertools = "0.12.1"
@@ -31,35 +32,38 @@ serde = "1.0.192"
serde-wasm-bindgen = { version = "0.6.1", optional = true }
serde_json = "1.0.108"
studio-bvh = { path = "../studio-bvh" }
wasm-bindgen = "0.2.84"
web-sys = { version = "0.3.65", optional = true, features = [
wasm-bindgen = "0.2"
web-sys = { version = "0.3", optional = true, features = [
"CustomEvent",
"CustomEventInit",
"Document",
"HtmlElement",
"Window",
"Location"
"Location",
"WorkerGlobalScope"
] }
clap = { version = "4.4.18", features = ["derive"] }
futures-intrusive = "0.5.0"
wgpu = "0.17.1"
wgpu = "0.19.3"
futures-lite = "2.2.0"
futures = "0.3.30"
image = "0.24.8"
image = "0.25.0"
bytemuck = "1.14.1"
space_editor = {git = "https://github.com/rewin123/space_editor", rev = "0f2439ba8cd38ebf2db8d90ea6de736992bcb56e"}
space_editor = {git = "https://github.com/rewin123/space_editor", rev = "daeaef70e4eb3ce1f945e296383a9eb2f31900b3"}
anyhow.workspace = true
serde_repr = "0.1.18"
bevy_tweening = "0.9"
async-compat = "0.2"
uuid = { version = "1", features = ["v4", "serde"]}
indexed_db_futures = { version = "0.4", optional = true}
wasm-bindgen-futures = { version = "0.4", optional = true}
dashmap = "5"
once_cell = "1"
bevy_tweening = "0.10.0"
[dependencies.reqwest]
version = "0.11.24"
version = "0.12.2"
features = [
"default",
"json",
@@ -77,12 +81,13 @@ wasm = [
"bevy_rapier3d/wasm-bindgen",
]
desktop = []
headless = []
[dependencies.bevy]
workspace = true
[dependencies.bevy_mod_picking]
version = "0.17.0"
version = "0.18.0"
default-features = false
features = [
"backend_bevy_ui",
@@ -1,7 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_gltf::loader::GltfLoader",
settings: (),
),
)
-4
View File
@@ -1,4 +0,0 @@
(
meta_format_version: "1.0",
asset: Ignore,
)
-7
View File
@@ -1,7 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_gltf::loader::GltfLoader",
settings: (),
),
)
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
#import bevy_pbr::forward_io::VertexOutput
@group(1) @binding(0)
@group(2) @binding(0)
var<uniform> material_color: vec4<f32>;
@fragment
@@ -1,7 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::render_resource::shader::ShaderLoader",
settings: (),
),
)
-4
View File
@@ -1,4 +0,0 @@
(
meta_format_version: "1.0",
asset: Ignore,
)
@@ -1,7 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
settings: (),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
@@ -1,7 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
settings: (),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
@@ -1,7 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
settings: (),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
@@ -1,7 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
settings: (),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
@@ -1,7 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
settings: (),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
@@ -1,7 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::hdr_texture_loader::HdrTextureLoader",
settings: (),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
@@ -1,11 +0,0 @@
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
is_srgb: true,
sampler: Default,
),
),
)
+7
View File
@@ -0,0 +1,7 @@
{
"space_undo::ChangeChainSettings": "{\"space_undo::ChangeChainSettings\":(max_change_chain_size:200)}",
"space_editor_ui::settings::NewWindowSettings": "{\"space_editor_ui::settings::NewWindowSettings\":(new_tab:SameNode)}",
"space_editor_ui::settings::GameModeSettings": "{\"space_editor_ui::settings::GameModeSettings\":(mode:Game3D)}",
"space_persistence::PersistenceSettings": "{\"space_persistence::PersistenceSettings\":(load_on_startup:true,save_on_close:true)}",
"space_editor_ui::sizing::Sizing": "{\"space_editor_ui::sizing::Sizing\":(icon:Regular,gizmos:Gizmos,text:14.0)}",
}
+1 -1
View File
@@ -21,7 +21,7 @@ async function main() {
}
/** @type import("node:child_process").StdioOptions */
const stdio = ["ignore", "inherit", "inherit"];
const stdio = ["inherit", "inherit", "inherit"];
const outDir = path.join(workspaceRoot, "dist/studio");
+2 -2
View File
@@ -1,8 +1,8 @@
use bevy::{
ecs::entity::{EntityHashMap, EntityHashSet},
math::Affine3A,
prelude::*,
render::{mesh::skinning::SkinnedMesh, view::NoFrustumCulling},
utils::{EntityHashMap, EntityHashSet},
};
use space_editor::space_prefab::editor_registry::EditorRegistryExt;
@@ -98,7 +98,7 @@ fn process_skinned_meshes(
// ```
// Building this hashmap before processing each joint avoids redundancy in
// the common case where the same joints are bound to multiple skinned meshes
let mut joint_collections = EntityHashMap::<Entity, EntityHashSet<Entity>>::default();
let mut joint_collections = EntityHashMap::<EntityHashSet>::default();
for (ent, skinned_mesh) in q_skinned_meshes.iter() {
cmd.entity(ent)
.insert(NoFrustumCulling)
+8 -4
View File
@@ -12,7 +12,6 @@ use serde::{Deserialize, Serialize};
use space_editor::{prelude::PrefabMarker, space_prefab::editor_registry::EditorRegistryExt};
use crate::{
headless::HeadlessSettings,
interaction::{MarkedForDeletion, Selectable},
scene::SceneElement,
};
@@ -39,7 +38,7 @@ impl Plugin for TimelinePlugin {
.register_type::<uuid::Uuid>()
.add_event::<TimelineInteractionEvent>()
.add_event::<KeyframeInteractionEvent>()
.add_state::<KeyingMode>();
.init_state::<KeyingMode>();
app.insert_resource(GlobalSeekTime {
time: 0.,
@@ -341,13 +340,18 @@ fn process_timeline_events(
fn update_tick(
mut r_global_seek_time: ResMut<GlobalSeekTime>,
time: Res<Time>,
headless_settings: Option<Res<HeadlessSettings>>,
#[cfg(feature = "headless")] headless_settings: Option<Res<crate::headless::HeadlessSettings>>,
) {
if r_global_seek_time.playing {
#[cfg(feature = "headless")]
let delta = match headless_settings {
Some(headless_settings) => headless_settings.frame_delta,
None => time.delta_seconds(),
};
#[cfg(not(feature = "headless"))]
let delta = time.delta_seconds();
r_global_seek_time.time += delta;
r_global_seek_time.delta = Some(delta);
}
@@ -465,7 +469,7 @@ fn process_auto_key(
for entity in &changed {
info!("Setting Auto Key");
event.send(KeyframeInteractionEvent::RecordKeyframe { target: entity })
event.send(KeyframeInteractionEvent::RecordKeyframe { target: entity });
}
}
+25 -9
View File
@@ -1,11 +1,27 @@
use bevy::{
app::{App, Plugin, PostUpdate, Update},
asset::{Asset, AssetApp, AssetEvent, Assets, Handle},
core::Name,
ecs::{
query::{ReadOnlyWorldQuery, WorldQuery},
system::Command,
component::Component,
entity::Entity,
event::{Event, EventReader, EventWriter},
query::{QueryData, QueryFilter, With, Without},
schedule::{apply_deferred, IntoSystemConfigs},
system::{Command, Commands, Query, Res, Resource},
world::World,
},
hierarchy::{BuildWorldChildren, Children},
math::{EulerRot, Quat, Vec3},
reflect::{Reflect, TypePath},
render::color::Color,
time::{Time, Timer, TimerMode},
transform::{
components::{GlobalTransform, Transform},
TransformSystem,
},
prelude::*,
transform::TransformSystem,
};
use space_editor::space_prefab::ext::{in_state, resource_exists, Deref, DerefMut, SpatialBundle};
use studio_bvh::{frames::Frame, ChannelType};
mod error;
@@ -30,7 +46,7 @@ impl Plugin for BvhPlugin {
// module's `SceneState`) to manage the order/frequency at
// which these systems run instead of just running them all
// every frame
.add_systems(Update, spawn_test_bvh.run_if(resource_exists::<TestBvh>()))
.add_systems(Update, spawn_test_bvh.run_if(resource_exists::<TestBvh>))
.add_systems(
PostUpdate,
(compute_inverse_bindposes, apply_deferred, animate_bvh)
@@ -129,7 +145,7 @@ pub fn animate_bvh(
}
}
anim.timer.percent()
anim.timer.fraction()
} else {
0.
};
@@ -143,7 +159,7 @@ pub fn animate_bvh(
}
}
fn lerp_frames<T: ReadOnlyWorldQuery>(
fn lerp_frames<T: QueryFilter>(
joint_query: &mut Query<JointQuery, T>,
current_joint: Entity,
bvh: &studio_bvh::Bvh,
@@ -226,8 +242,8 @@ pub struct BvhAnimCompleteEvent;
#[derive(Debug, Component, Reflect)]
pub struct BvhJoint(usize);
#[derive(Debug, WorldQuery)]
#[world_query(mutable)]
#[derive(Debug, QueryData)]
#[query_data(mutable)]
pub struct JointQuery {
pub name: &'static Name,
pub joint: &'static BvhJoint,
+27 -2
View File
@@ -30,9 +30,34 @@ impl Plugin for CameraControllerPlugin {
)
.in_set(CameraMotionSystem),
);
#[cfg(not(feature = "wasm"))]
app.add_systems(PreUpdate, sync_camera_viewport);
}
}
#[cfg(not(feature = "wasm"))]
fn sync_camera_viewport(
mut camera: Query<&mut Camera, With<crate::MainCamera>>,
window: Query<&Window, With<PrimaryWindow>>,
) {
use bevy::render::camera::Viewport;
let Ok(mut camera) = camera.get_single_mut() else {
return;
};
let Ok(window) = window.get_single() else {
return;
};
camera.viewport = Some(Viewport {
physical_position: UVec2::new(0, 0),
physical_size: UVec2::new(window.physical_width(), window.physical_height()),
..default()
});
}
#[derive(Component)]
pub struct CameraController {
pub focus: Vec3,
@@ -314,8 +339,8 @@ fn do_pan(
};
// Pan along local XY plane, with the distance scaled by the radius
let up = xform.up();
let left = xform.left();
let up = xform.up().xyz();
let left = xform.left().xyz();
let delta = (pan.y * up + pan.x * left) * controller.radius;
// Update camera translation and focus point
+69 -50
View File
@@ -1,22 +1,23 @@
use std::fmt;
use bevy::{prelude::*, window::PrimaryWindow};
use bevy_inspector_egui::{bevy_egui::EguiPlugin, DefaultInspectorConfigPlugin};
use bevy_inspector_egui::DefaultInspectorConfigPlugin;
use bevy_mod_picking::debug::DebugPickingMode;
use space_editor::prelude::space_undo::AppAutoUndo;
use space_editor::{
prelude::EditorCameraMarker,
space_editor_ui::{
bottom_menu::BottomMenuPlugin, tools::gizmo::GizmoToolPlugin, EditorDefaultCameraPlugin,
EditorGizmoConfigPlugin, EditorPickingPlugin, EditorPluginGroup, EditorUiCore,
ShowEditorUi,
},
prelude::*,
space_editor_ui::{tools::gizmo::GizmoToolPlugin, EditorPluginGroup},
};
use crate::StudioMode;
use crate::{
anim::SkeletalJoint,
camera_controller::{CameraController, CameraMotionSystem},
input::{Action, InputAction, Lifecycle},
is_editor, MainCamera,
interaction::Selectable,
is_editor,
scene::SceneElement,
MainCamera,
};
#[derive(Resource, Deref, DerefMut)]
@@ -32,13 +33,16 @@ pub struct DebugPlugin;
impl Plugin for DebugPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(DebugMode(false))
.insert_resource(State::new(DebugPickingMode::Disabled))
.insert_resource(GizmoConfig {
enabled: true,
depth_bias: -0.1,
line_perspective: false,
line_width: 2.,
..default()
.insert_resource(DebugPickingMode::Disabled)
.add_systems(Startup, |mut config_store: ResMut<GizmoConfigStore>| {
let (config, _) = config_store.config_mut::<DefaultGizmoConfigGroup>();
*config = GizmoConfig {
enabled: true,
depth_bias: -0.1,
line_perspective: false,
line_width: 2.,
..default()
};
})
.add_systems(
Update,
@@ -46,7 +50,7 @@ impl Plugin for DebugPlugin {
toggle_debug_mode,
update_debug_picking_mode
.run_if(is_editor)
.run_if(resource_changed::<DebugMode>()),
.run_if(resource_changed::<DebugMode>),
update_gizmo_line_thickness,
(draw_world_axes, draw_debug_points, draw_skeletal_joints)
.after(CameraMotionSystem)
@@ -54,38 +58,52 @@ impl Plugin for DebugPlugin {
),
);
//space_editor stuff
// app.add_plugins(UndoPlugin);
app.add_plugins(
EditorPluginGroup
.build()
.disable::<EditorUiCore>()
.add_before::<EditorDefaultCameraPlugin, _>(EditorUiCore {
disable_no_editor_cams: false,
})
.disable::<EguiPlugin>()
.disable::<DefaultInspectorConfigPlugin>()
.disable::<EditorPickingPlugin>()
.disable::<EditorDefaultCameraPlugin>()
.disable::<BottomMenuPlugin>()
.disable::<GizmoToolPlugin>()
.disable::<EditorGizmoConfigPlugin>()
.set(space_editor::prelude::SelectedPlugin {
show_selected_wireframe: false,
}),
);
let editor_ui_group = EditorPluginGroup
.build()
.disable::<EditorUiCore>()
.add_before::<GameViewPlugin, _>(EditorUiCore {
disable_no_editor_cams: false,
})
// .disable::<EguiPlugin>()
.disable::<DefaultInspectorConfigPlugin>()
.disable::<EditorPickingPlugin>()
.disable::<EditorDefaultCameraPlugin>()
// .disable::<BottomMenuPlugin>()
.disable::<GizmoToolPlugin>()
// .disable::<EditorGizmoConfigPlugin>()
// .disable::<MeshlessVisualizerPlugin>()
.disable::<space_editor::prelude::SelectedPlugin>();
app.add_systems(Update, sync_editor_camera);
app.add_systems(
Update,
update_editor_ui_mode.run_if(resource_changed::<DebugMode>()),
);
let mut only_core = false;
#[cfg(feature = "wasm")]
{
if let Some(state) = app.world.get_resource::<StudioMode>() {
if *state == StudioMode::Headless {
only_core = true;
}
}
if only_core {
app.add_plugins(PrefabPlugin).add_plugins(EditorCore);
} else {
app.add_plugins(editor_ui_group);
app.auto_undo::<Handle<StandardMaterial>>();
app.auto_undo::<SkeletalJoint>();
app.auto_undo::<Handle<Mesh>>();
app.auto_undo::<SceneElement>();
app.auto_undo::<Selectable>();
#[cfg(feature = "wasm")]
{
app.add_systems(
PostUpdate,
wasm::notify_debug_mode_toggle.map(bevy::utils::error),
);
}
app.add_systems(Update, sync_editor_camera);
app.add_systems(
PostUpdate,
wasm::notify_debug_mode_toggle.map(bevy::utils::error),
Update,
update_editor_ui_mode.run_if(resource_changed::<DebugMode>),
);
}
}
@@ -112,12 +130,12 @@ fn update_editor_ui_mode(
fn update_debug_picking_mode(
r_debug: Res<DebugMode>,
mut s_debug_picking: ResMut<NextState<DebugPickingMode>>,
mut s_debug_picking: ResMut<DebugPickingMode>,
) {
s_debug_picking.set(match **r_debug {
*s_debug_picking = match **r_debug {
true => DebugPickingMode::Normal,
false => DebugPickingMode::Disabled,
});
};
}
pub fn is_debug_mode(r_debug: Res<DebugMode>) -> bool {
@@ -125,10 +143,11 @@ pub fn is_debug_mode(r_debug: Res<DebugMode>) -> bool {
}
fn update_gizmo_line_thickness(
mut config: ResMut<GizmoConfig>,
mut config_store: ResMut<GizmoConfigStore>,
q_window: Query<&Window, (Changed<Window>, With<PrimaryWindow>)>,
) {
if let Ok(scale_factor) = q_window.get_single().map(|win| win.scale_factor() as f32) {
if let Ok(scale_factor) = q_window.get_single().map(|win| win.scale_factor()) {
let (config, _) = config_store.config_mut::<DefaultGizmoConfigGroup>();
config.line_width = 2. * scale_factor;
}
}
+63 -28
View File
@@ -2,7 +2,10 @@ use std::time::Duration;
use bevy::{
app::{AppExit, ScheduleRunnerPlugin},
asset::AssetMetaCheck,
core_pipeline::Skybox,
prelude::*,
winit::WinitPlugin,
};
use bevy_web_asset::WebAssetPlugin;
use space_editor::prelude::{EditorEvent, EditorPrefabPath};
@@ -11,7 +14,7 @@ use wgpu::Extent3d;
use crate::{
anim::timeline_animation::{Keyframe, TimelineInteractionEvent},
bvh::{BvhAnimCompleteEvent, TestBvh},
scene::SceneState,
scene::{SceneState, SkyboxHolder},
screenshot::{ScreenCapture, ScreenShotPlugin},
BvhPlugin, CommonPlugins, LoadLibraryObjectEvent, LoadSceneEvent, MainCamera,
MixamoImportEvent, MixamoPlugin, RemoteAssetPlugin, SkyboxId, StudioMode, WorldGridPlugin,
@@ -37,7 +40,7 @@ pub fn start_headless(
mode: HeadlessMode,
fps: u32,
outout_dir: &str,
frames_to_capture: u32,
frames_to_capture: Option<u32>,
image_res: Extent3d,
) -> App {
let mut app = App::new();
@@ -52,17 +55,38 @@ pub fn start_headless(
frame_delta: 1.0 / (fps as f32),
});
app.insert_resource(AssetMetaCheck::Never);
app.add_plugins((
ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(1.0 / fps as f64)),
WebAssetPlugin,
RemoteAssetPlugin,
DefaultPlugins.set(WindowPlugin {
primary_window: None,
exit_condition: bevy::window::ExitCondition::DontExit,
close_when_requested: true,
}),
DefaultPlugins
.set(TaskPoolPlugin {
task_pool_options: TaskPoolOptions {
async_compute: bevy::core::TaskPoolThreadAssignmentPolicy {
min_threads: 1,
max_threads: usize::MAX,
percent: 0.75,
},
..default()
},
})
.set(WindowPlugin {
primary_window: None,
exit_condition: bevy::window::ExitCondition::DontExit,
close_when_requested: true,
})
.set(AssetPlugin {
mode: AssetMode::Unprocessed,
..default()
})
.disable::<WinitPlugin>(),
ScreenShotPlugin,
CommonPlugins.build().disable::<WorldGridPlugin>(),
CommonPlugins
.build()
.disable::<WorldGridPlugin>()
.disable::<crate::UiLayerPlugin>(),
));
match mode {
@@ -78,7 +102,6 @@ pub fn start_headless(
ew.send(LoadLibraryObjectEvent("Mannequin.gltf".to_string()));
},
);
app.add_systems(
PostUpdate,
check_for_bvh_capture_complete.run_if(in_state(SceneState::Active)),
@@ -87,12 +110,18 @@ pub fn start_headless(
HeadlessMode::Scene(scene, skybox) => {
app.add_systems(
Startup,
move |mut cmd: Commands, mut ew: EventWriter<EditorEvent>| {
cmd.insert_resource(SkyboxId(skybox.clone()));
ew.send(EditorEvent::Load(EditorPrefabPath::File(scene.clone())))
move |mut cmd: Commands,
mut ew: EventWriter<EditorEvent>,
asset_server: Res<AssetServer>| {
cmd.insert_resource(SkyboxHolder(Some(Skybox {
image: asset_server.load(skybox.clone()),
brightness: 1.0,
})));
ew.send(EditorEvent::Load(EditorPrefabPath::File(scene.clone())));
},
)
.add_systems(OnEnter(SceneState::Active), start_timeline_animation);
);
app.add_systems(OnEnter(SceneState::Active), start_timeline_animation);
}
HeadlessMode::Mixamo(mixamo_path, skybox) => {
app.add_plugins(MixamoPlugin);
@@ -119,19 +148,11 @@ pub fn start_headless(
app
}
pub fn is_headless(studio_state: Res<StudioMode>) -> bool {
*studio_state == StudioMode::Headless
}
pub fn is_not_headless(studio_state: Res<StudioMode>) -> bool {
*studio_state != StudioMode::Headless
}
#[derive(Resource)]
pub struct HeadlessSettings {
pub output_dims: Extent3d,
pub output_dir: String,
pub max_frames: u32,
pub max_frames: Option<u32>,
pub fps: f32,
pub frame_delta: f32,
}
@@ -145,7 +166,7 @@ impl Default for HeadlessSettings {
depth_or_array_layers: 1,
},
output_dir: "./screen_shots/".to_string(),
max_frames: u32::MAX,
max_frames: None,
fps: 24.0,
frame_delta: 1.0 / 24.0,
}
@@ -189,6 +210,7 @@ fn check_for_mixamo_capture_complete(
}
}
#[cfg(not(feature = "wasm"))]
fn start_timeline_animation(
mut headless_settings: ResMut<HeadlessSettings>,
keyframes: Query<&Keyframe>,
@@ -197,21 +219,34 @@ fn start_timeline_animation(
mut ev: EventWriter<TimelineInteractionEvent>,
) {
if keyframes.is_empty() {
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.unwrap_or(0) + 3),
);
if headless_settings.max_frames.is_none() {
error!("Both keyframes and max_frames are empty. Aborting.");
}
return;
}
commands.remove_resource::<CameraMotion>();
let total_time = keyframes
.iter()
.fold(0f32, |previous, keyframe| previous.max(keyframe.time))
.fold(1f32, |previous, keyframe| previous.max(keyframe.time))
.max(3.0);
let frames = (total_time * headless_settings.fps).ceil() as u32;
headless_settings.max_frames = frames;
let mut frames = (total_time * headless_settings.fps).ceil() as u32;
if let Some(max_frames) = headless_settings.max_frames {
frames = max_frames;
}
info!("Frames to capture {}", frames);
headless_settings.max_frames = Some(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),
.with_max_frames(frames + 3),
);
}
+4 -4
View File
@@ -1,6 +1,6 @@
use bevy::{
ecs::entity::{EntityHashMap, EntityHashSet},
prelude::*,
utils::{EntityHashMap, EntityHashSet},
};
/// Find the parent entity of `target`. Returns `None` if `target` has no
@@ -29,7 +29,7 @@ pub(crate) fn nearest_common_ancestor(
q_parents: &Query<Option<&Parent>>,
) -> Option<Entity> {
// Find the depth of each entity in the tree
let mut depths: EntityHashMap<Entity, usize> = EntityHashMap::default();
let mut depths = EntityHashMap::default();
for &ent in entities {
depths.insert(ent, find_depth(ent, q_parents));
}
@@ -39,7 +39,7 @@ pub(crate) fn nearest_common_ancestor(
// Traverse up the tree from each entity until we find each one's ancestor
// at the level of lowest_depth
let mut equalized: EntityHashSet<Entity> = EntityHashSet::default();
let mut equalized = EntityHashSet::default();
for ent in entities {
let mut depth = depths[ent];
let mut result = *ent;
@@ -59,7 +59,7 @@ pub(crate) fn nearest_common_ancestor(
// by adding a some kind of mode switch to this function's parameters.
// Walk up the tree from each of those entities until we find the common ancestor
let mut parents: EntityHashSet<Entity> = EntityHashSet::default();
let mut parents = EntityHashSet::default();
loop {
for &ent in equalized.iter() {
if let Some(parent) = find_parent(ent, q_parents) {
+49 -25
View File
@@ -50,8 +50,8 @@ impl Plugin for StudioInputPlugin {
/// Logs all `InputAction`s emitted, along with the input state at the time they
/// were fired, with `debug` verbosity.
fn log_input_actions(
ri_keyboard: Res<Input<KeyCode>>,
ri_mouse_buttons: Res<Input<MouseButton>>,
ri_keyboard: Res<ButtonInput<KeyCode>>,
ri_mouse_buttons: Res<ButtonInput<MouseButton>>,
mut er_input_actions: EventReader<InputAction>,
) {
if er_input_actions.is_empty() {
@@ -78,9 +78,20 @@ fn log_input_actions(
let mod_keys = [AltLeft, AltRight, ShiftLeft, ControlLeft, ControlRight]
.into_iter()
.collect::<HashSet<_>>();
let action_keys = [W, A, S, D, E, Q, Escape, Grave, Delete, F]
.into_iter()
.collect::<HashSet<_>>();
let action_keys = [
KeyCode::KeyW,
KeyCode::KeyA,
KeyCode::KeyS,
KeyCode::KeyD,
KeyCode::KeyE,
KeyCode::KeyQ,
KeyCode::Escape,
KeyCode::Quote,
KeyCode::Delete,
KeyCode::KeyF,
]
.into_iter()
.collect::<HashSet<_>>();
let keys_pressed = ri_keyboard.get_pressed().copied().collect::<HashSet<_>>();
let keys_display = keys_pressed
@@ -151,16 +162,19 @@ impl From<Selection> for Action {
// TODO: Make the bindings of raw device inputs to InputActions configurable
fn translate_selection_events(keyboard: Res<Input<KeyCode>>, mut input: EventWriter<InputAction>) {
fn translate_selection_events(
keyboard: Res<ButtonInput<KeyCode>>,
mut input: EventWriter<InputAction>,
) {
if keyboard.just_pressed(KeyCode::Escape) {
input.send(InputAction(Lifecycle::Start, Selection::Clear.into()));
} else if keyboard.just_released(KeyCode::Escape) {
input.send(InputAction(Lifecycle::Stop, Selection::Clear.into()));
}
if keyboard.just_pressed(KeyCode::F) {
if keyboard.just_pressed(KeyCode::KeyF) {
input.send(InputAction(Lifecycle::Start, Selection::Focus.into()));
} else if keyboard.just_released(KeyCode::F) {
} else if keyboard.just_released(KeyCode::KeyF) {
input.send(InputAction(Lifecycle::Stop, Selection::Focus.into()));
}
@@ -171,11 +185,14 @@ fn translate_selection_events(keyboard: Res<Input<KeyCode>>, mut input: EventWri
}
}
fn translate_debug_toggle(keyboard: Res<Input<KeyCode>>, mut input: EventWriter<InputAction>) {
fn translate_debug_toggle(
keyboard: Res<ButtonInput<KeyCode>>,
mut input: EventWriter<InputAction>,
) {
if keyboard.pressed(KeyCode::ControlLeft) || keyboard.pressed(KeyCode::ControlRight) {
if keyboard.just_pressed(KeyCode::Grave) {
if keyboard.just_pressed(KeyCode::Backquote) {
input.send(InputAction(Lifecycle::Start, Action::ToggleDebug));
} else if keyboard.just_released(KeyCode::Grave) {
} else if keyboard.just_released(KeyCode::Backquote) {
input.send(InputAction(Lifecycle::Stop, Action::ToggleDebug));
}
}
@@ -183,8 +200,8 @@ fn translate_debug_toggle(keyboard: Res<Input<KeyCode>>, mut input: EventWriter<
fn translate_dcc_camera_events(
mouse_motion: Res<LastFrameMouseDelta>,
mouse_buttons: Res<Input<MouseButton>>,
keyboard: Res<Input<KeyCode>>,
mouse_buttons: Res<ButtonInput<MouseButton>>,
keyboard: Res<ButtonInput<KeyCode>>,
mut input: EventWriter<InputAction>,
) {
let delta = mouse_motion.get();
@@ -288,12 +305,19 @@ fn translate_dcc_camera_events(
fn translate_fps_camera_events(
mut mouse_wheel: EventReader<MouseWheel>,
keyboard: Res<Input<KeyCode>>,
keyboard: Res<ButtonInput<KeyCode>>,
mut input: EventWriter<InputAction>,
) {
// Camera Translation (FPS-like)
use KeyCode::*;
let wasd_keys = [W, A, S, D, E, Q];
let wasd_keys = [
KeyCode::KeyW,
KeyCode::KeyA,
KeyCode::KeyS,
KeyCode::KeyD,
KeyCode::KeyE,
KeyCode::KeyQ,
];
let wasd_keys_set = wasd_keys.into_iter().collect::<HashSet<_>>();
// A little unfortunate that `get_pressed()` doesn't just return a reference
@@ -308,12 +332,12 @@ fn translate_fps_camera_events(
let mut mvmt = Vec3::ZERO;
#[rustfmt::skip] {
if wasd_keys_pressed.contains(&W) { mvmt.z -= 1.; }
if wasd_keys_pressed.contains(&A) { mvmt.x -= 1.; }
if wasd_keys_pressed.contains(&S) { mvmt.z += 1.; }
if wasd_keys_pressed.contains(&D) { mvmt.x += 1.; }
if wasd_keys_pressed.contains(&E) { mvmt.y += 1.; }
if wasd_keys_pressed.contains(&Q) { mvmt.y -= 1.; }
if wasd_keys_pressed.contains(&KeyCode::KeyW) { mvmt.z -= 1.; }
if wasd_keys_pressed.contains(&KeyCode::KeyA) { mvmt.x -= 1.; }
if wasd_keys_pressed.contains(&KeyCode::KeyS) { mvmt.z += 1.; }
if wasd_keys_pressed.contains(&KeyCode::KeyD) { mvmt.x += 1.; }
if wasd_keys_pressed.contains(&KeyCode::KeyE) { mvmt.y += 1.; }
if wasd_keys_pressed.contains(&KeyCode::KeyQ) { mvmt.y -= 1.; }
};
let mvmt = mvmt.normalize_or_zero();
@@ -394,9 +418,9 @@ fn read_mouse_delta(
}
// TODO: Make it configurable
fn send_undo_event(mut events: EventWriter<UndoRedo>, keyboard: Res<Input<KeyCode>>) {
fn send_undo_event(mut events: EventWriter<UndoRedo>, keyboard: Res<ButtonInput<KeyCode>>) {
if keyboard.pressed(KeyCode::ControlLeft)
&& keyboard.just_pressed(KeyCode::Z)
&& keyboard.just_pressed(KeyCode::KeyZ)
&& !keyboard.pressed(KeyCode::ShiftLeft)
{
events.send(UndoRedo::Undo);
@@ -404,7 +428,7 @@ fn send_undo_event(mut events: EventWriter<UndoRedo>, keyboard: Res<Input<KeyCod
}
if keyboard.pressed(KeyCode::ControlLeft)
&& keyboard.just_pressed(KeyCode::Z)
&& keyboard.just_pressed(KeyCode::KeyZ)
&& keyboard.pressed(KeyCode::ShiftLeft)
{
events.send(UndoRedo::Redo);
+4 -10
View File
@@ -1,9 +1,5 @@
use bevy::{
core_pipeline::{clear_color::ClearColorConfig, fxaa::Fxaa},
prelude::*,
render::view::RenderLayers,
};
use space_editor::space_editor_ui::NotShowCamera;
use bevy::{core_pipeline::fxaa::Fxaa, prelude::*, render::view::RenderLayers};
use space_editor::space_editor_ui::camera_plugin::NotShowCamera;
use crate::MainCamera;
@@ -23,13 +19,11 @@ pub(super) fn spawn_gizmo_camera(
Name::new("Gizmo Camera"),
GizmoCamera,
Camera3dBundle {
camera_3d: Camera3d {
clear_color: ClearColorConfig::None,
..default()
},
camera_3d: Camera3d { ..default() },
camera: Camera {
hdr: true,
order: 1,
clear_color: ClearColorConfig::None,
..default()
},
..default()
+4 -5
View File
@@ -46,8 +46,7 @@ impl Plugin for GizmoPlugin {
app.add_systems(
PreUpdate,
spawn_gizmo_camera.run_if(
any_with_component::<MainCamera>()
.and_then(not(any_with_component::<GizmoCamera>())),
any_with_component::<MainCamera>.and_then(not(any_with_component::<GizmoCamera>)),
),
);
app.add_systems(
@@ -55,11 +54,11 @@ impl Plugin for GizmoPlugin {
(
despawn_existing_gizmo.run_if(
resource_changed_or_removed::<Selection>()
.or_else(resource_changed::<TransformMode>()),
.or_else(resource_changed::<TransformMode>),
),
spawn_gizmo.after(despawn_existing_gizmo).run_if(
resource_exists::<Selection>()
.and_then(not(any_with_component::<TransformGizmo>())),
resource_exists::<Selection>
.and_then(not(any_with_component::<TransformGizmo>)),
),
(update_target_global_trasform, sync_gizmo_transform)
.chain()
+21 -20
View File
@@ -2,7 +2,7 @@ use bevy::prelude::*;
use crate::{design_system, math::geo};
use super::material::GizmoMaterial;
use super::material::*;
#[derive(Resource, Reflect, Debug, Default)]
#[reflect(Resource)]
@@ -38,22 +38,23 @@ pub(super) struct GizmoMats {
pub rotate_ss: Handle<GizmoMaterial>,
}
#[allow(deprecated)]
pub(super) fn init_resources(
mut cmd: Commands,
mut ra_meshes: ResMut<Assets<Mesh>>,
mut ra_mats: ResMut<Assets<GizmoMaterial>>,
) {
let translate_line = ra_meshes.add(Mesh::from(shape::Cylinder {
let translate_line = ra_meshes.add(Cylinder {
radius: 0.03,
height: 0.8,
resolution: 6,
segments: 1,
}));
half_height: 0.4,
});
let translate_arrow = ra_meshes.add(Mesh::from(geo::Cone {
radius: 0.115,
length: 0.4,
resolution: 12,
}));
let translate_plane = ra_meshes.add(Mesh::from(geo::Plane2Sided { size: 0.25 }));
let translate_ss = ra_meshes.add(
Mesh::try_from(shape::Icosphere {
@@ -98,25 +99,25 @@ pub(super) fn init_resources(
uniform_scale_cube,
});
let translate_x = ra_mats.add(design_system::Color::RED.into());
let translate_yz = ra_mats.add(design_system::Color::RED.into());
let translate_x = ra_mats.add(GizmoMaterial::from(design_system::Color::RED));
let translate_yz = ra_mats.add(GizmoMaterial::from(design_system::Color::RED));
let translate_y = ra_mats.add(design_system::Color::GREEN.into());
let translate_xz = ra_mats.add(design_system::Color::GREEN.into());
let translate_y = ra_mats.add(GizmoMaterial::from(design_system::Color::GREEN));
let translate_xz = ra_mats.add(GizmoMaterial::from(design_system::Color::GREEN));
let translate_z = ra_mats.add(design_system::Color::BLUE.into());
let translate_xy = ra_mats.add(design_system::Color::BLUE.into());
let translate_z = ra_mats.add(GizmoMaterial::from(design_system::Color::BLUE));
let translate_xy = ra_mats.add(GizmoMaterial::from(design_system::Color::BLUE));
let translate_ss = ra_mats.add(Color::WHITE.into());
let translate_ss = ra_mats.add(GizmoMaterial::from(Color::WHITE));
let rotate_x = ra_mats.add(design_system::Color::RED.into());
let rotate_y = ra_mats.add(design_system::Color::GREEN.into());
let rotate_z = ra_mats.add(design_system::Color::BLUE.into());
let rotate_ss = ra_mats.add(Color::WHITE.into());
let rotate_x = ra_mats.add(GizmoMaterial::from(design_system::Color::RED));
let rotate_y = ra_mats.add(GizmoMaterial::from(design_system::Color::GREEN));
let rotate_z = ra_mats.add(GizmoMaterial::from(design_system::Color::BLUE));
let rotate_ss = ra_mats.add(GizmoMaterial::from(Color::WHITE));
let scale_x = ra_mats.add(design_system::Color::RED.into());
let scale_y = ra_mats.add(design_system::Color::GREEN.into());
let scale_z = ra_mats.add(design_system::Color::BLUE.into());
let scale_x = ra_mats.add(GizmoMaterial::from(design_system::Color::RED));
let scale_y = ra_mats.add(GizmoMaterial::from(design_system::Color::GREEN));
let scale_z = ra_mats.add(GizmoMaterial::from(design_system::Color::BLUE));
let rotate_trackball = ra_mats.add(GizmoMaterial {
color: Color::WHITE.with_a(0.0),
+5 -3
View File
@@ -288,7 +288,7 @@ pub(super) fn on_rotate(
early_exit_test: &early_exit_test,
..default()
};
let hits = raycast.cast_ray(ray.into(), &settings);
let hits = raycast.cast_ray(ray, &settings);
// If the pointer is no longer in the vicinity of the sphere, we'll
// invoke this closure to extrapolate additional rotation from the
@@ -302,7 +302,9 @@ pub(super) fn on_rotate(
}
let view_plane_normal = (cam_xform.translation() - p1).normalize();
let hit_dist = ray.intersect_plane(p1, view_plane_normal).unwrap();
let hit_dist = ray
.intersect_plane(p1, Plane3d::new(view_plane_normal))
.unwrap();
let p2 = ray.origin + ray.direction * hit_dist;
let drag_dist = (p2 - p1).length();
// TODO: Sensitivity could use some tuning
@@ -366,7 +368,7 @@ pub(super) fn on_rotate(
};
// Find the world-space p2, if possible
let Some(hit_dist) = ray.intersect_plane(p0, axis_ws) else {
let Some(hit_dist) = ray.intersect_plane(p0, Plane3d::new(axis_ws)) else {
// This could fail if the axis of rotation is nearly
// perpendicular to the view direction
return;
+2 -2
View File
@@ -261,7 +261,7 @@ pub(super) fn on_scale(
let plane_vector = axis_ws.cross(axis_tan);
// Find the intersection of the camera-to-world ray against that plane
let Some(hit_dist) = ray.intersect_plane(origin_ws, plane_vector) else {
let Some(hit_dist) = ray.intersect_plane(origin_ws, Plane3d::new(plane_vector)) else {
return;
};
let hit_point = ray.origin + ray.direction * hit_dist;
@@ -299,7 +299,7 @@ pub(super) fn on_scale(
.affine()
.transform_vector3(*plane_normal)
.normalize();
let Some(hit_dist) = ray.intersect_plane(origin_ws, normal_ws) else {
let Some(hit_dist) = ray.intersect_plane(origin_ws, Plane3d::new(normal_ws)) else {
return;
};
let hit_point = ray.origin + ray.direction * hit_dist;
+2 -2
View File
@@ -257,7 +257,7 @@ pub(super) fn on_translate(
let plane_vector = axis_ws.cross(axis_tan);
// Find the intersection of the camera-to-world ray against that plane
let Some(hit_dist) = ray.intersect_plane(origin_ws, plane_vector) else {
let Some(hit_dist) = ray.intersect_plane(origin_ws, Plane3d::new(plane_vector)) else {
return;
};
let hit_point = ray.origin + ray.direction * hit_dist;
@@ -290,7 +290,7 @@ pub(super) fn on_translate(
};
// Find the intersection of the camera-to-world ray against that plane
let Some(hit_dist) = ray.intersect_plane(origin_ws, plane_normal) else {
let Some(hit_dist) = ray.intersect_plane(origin_ws, Plane3d::new(plane_normal)) else {
return;
};
let target_ws = ray.origin + ray.direction * hit_dist;
+1 -1
View File
@@ -20,7 +20,7 @@ impl Plugin for SelectionPlugin {
Update,
(
update_selection,
process_delete_inputs.run_if(resource_exists::<Selection>()),
process_delete_inputs.run_if(resource_exists::<Selection>),
),
)
.add_systems(
+9 -2
View File
@@ -1,6 +1,6 @@
#![allow(clippy::type_complexity, clippy::too_many_arguments)]
use bevy::{app::PluginGroupBuilder, prelude::*};
use bevy::{app::PluginGroupBuilder, asset::AssetMetaCheck, prelude::*};
use bevy_mod_picking::DefaultPickingPlugins;
use bevy_web_asset::WebAssetPlugin;
use scene::ImportSceneEvent;
@@ -16,6 +16,7 @@ mod camera_controller;
mod change_tracking;
mod debug;
mod design_system;
#[cfg(feature = "headless")]
pub mod headless;
mod hierarchy;
mod input;
@@ -28,6 +29,7 @@ mod path;
mod persistance;
mod remote;
pub mod scene;
#[cfg(feature = "headless")]
pub mod screenshot;
mod skybox;
mod ui;
@@ -89,7 +91,7 @@ impl PluginGroup for CommonPlugins {
.add(InterpolationPlugin)
.add(ScenePlugin)
.add(MaterialPlugin)
.add(SkyboxPlugin)
// .add(SkyboxPlugin)
.add(StudioInputPlugin)
.add(UiLayerPlugin)
.add(WorldGridPlugin)
@@ -148,6 +150,11 @@ pub fn start(init: InitParams, mode: StudioMode, window_config: Option<Window>)
let mut app = App::new();
app.insert_resource(mode);
app.insert_resource(bevy::winit::WinitSettings {
focused_mode: bevy::winit::UpdateMode::Continuous,
unfocused_mode: bevy::winit::UpdateMode::Continuous,
});
app.insert_resource(AssetMetaCheck::Never);
app.add_plugins((
#[cfg(feature = "wasm")]
-3
View File
@@ -89,7 +89,6 @@ fn main() {
depth_or_array_layers: 1,
});
let fps = fps.unwrap_or(24);
let frames = frames.unwrap_or(30);
let skybox = skybox.unwrap_or("gum_trees_4k".to_string());
let mut app = start_headless(
HeadlessMode::Scene(scene_dir, skybox),
@@ -140,7 +139,6 @@ fn main() {
depth_or_array_layers: 1,
});
let fps = fps.unwrap_or(24);
let frames = frames.unwrap_or(u32::MAX);
let skybox = skybox.unwrap_or("gum_trees_4k".to_string());
start_headless(
HeadlessMode::Bvh(bvh_dir, skybox),
@@ -165,7 +163,6 @@ fn main() {
depth_or_array_layers: 1,
});
let fps = fps.unwrap_or(24);
let frames = frames.unwrap_or(u32::MAX);
let skybox = skybox.unwrap_or("gum_trees_4k".to_string());
start_headless(
HeadlessMode::Mixamo(mixamo_dir, skybox),
+8 -5
View File
@@ -1,7 +1,10 @@
use bevy::{
math::vec3,
prelude::*,
render::{mesh::Indices, primitives::Aabb, render_resource::PrimitiveTopology},
render::{
mesh::Indices, primitives::Aabb, render_asset::RenderAssetUsages,
render_resource::PrimitiveTopology,
},
};
pub struct Cone {
@@ -45,8 +48,8 @@ impl From<Cone> for Mesh {
positions.push(vec3(0., c.length, 0.));
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
mesh.set_indices(Some(Indices::U32(indices)));
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::all());
mesh.insert_indices(Indices::U32(indices));
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
mesh
@@ -104,8 +107,8 @@ impl From<Plane2Sided> for Mesh {
2, 3, 1,
];
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
mesh.set_indices(Some(Indices::U32(indices)));
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::all());
mesh.insert_indices(Indices::U32(indices));
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
mesh
+4 -3
View File
@@ -1,4 +1,5 @@
use bevy::prelude::*;
use space_editor::space_prefab::ext::{Deref, DerefMut};
use crate::math::{NearlyEq, NearlyZero};
@@ -56,7 +57,7 @@ where
Easing::SineOut => self.t.curve_sin_out(),
};
t.lerp(self.initial, self.target)
Lerp::lerp(t, self.initial, self.target)
}
}
}
@@ -68,8 +69,8 @@ where
{
fn default() -> Self {
Self {
initial: default(),
target: default(),
initial: T::default(),
target: T::default(),
t: 0.,
easing: Easing::Linear,
}
+2 -2
View File
@@ -6,13 +6,13 @@ pub struct MixamoPlugin;
impl Plugin for MixamoPlugin {
fn build(&self, app: &mut App) {
app.add_event::<MixamoImportEvent>();
app.add_state::<MixamoState>();
app.init_state::<MixamoState>();
app.add_systems(
Update,
(
import_mixamo_scene,
tick_mixamo_import.run_if(resource_exists::<MixamoImport>()),
tick_mixamo_import.run_if(resource_exists::<MixamoImport>),
tick_scene_spawning.run_if(in_state(MixamoState::Spawning)),
start_mixamo_anim.run_if(in_state(MixamoState::Processing)),
),
+12 -3
View File
@@ -1,11 +1,20 @@
use bevy::prelude::*;
use space_editor::prelude::*;
use crate::StudioMode;
pub struct PersistancePlugin;
impl Plugin for PersistancePlugin {
fn build(&self, app: &mut App) {
info!("PersistancePlugin::build");
if let Some(state) = app.world.get_resource::<StudioMode>() {
if *state == StudioMode::Headless {
return;
}
}
app.editor_tab_by_trait(
EditorTabName::Other("Persistance".to_string()),
DebugPersistanceTab::default(),
@@ -31,11 +40,11 @@ impl EditorTab for DebugPersistanceTab {
world: &mut World,
) {
if ui.button("Save").clicked() {
world.send_event(EditorEvent::Save(EditorPrefabPath::MemoryCahce));
world.send_event(EditorEvent::Save(EditorPrefabPath::MemoryCache));
}
if ui.button("Load").clicked() {
world.send_event(EditorEvent::Load(EditorPrefabPath::MemoryCahce));
world.send_event(EditorEvent::Load(EditorPrefabPath::MemoryCache));
}
if ui.button("Clear level").clicked() {
@@ -68,7 +77,7 @@ mod wasm {
pub(super) fn forward_save_scene_events(mut ew_editor_events: EventWriter<EditorEvent>) {
if SAVE_SCENE.take_message().is_some() {
ew_editor_events.send(EditorEvent::Save(EditorPrefabPath::MemoryCahce));
ew_editor_events.send(EditorEvent::Save(EditorPrefabPath::MemoryCache));
}
}
+1 -1
View File
@@ -16,7 +16,7 @@ impl Plugin for RemoteAssetPlugin {
AssetSourceId::Name("remote".into()),
AssetSource::build()
.with_reader(|| Box::new(RemoteAssetReader))
.with_writer(|| None),
.with_writer(|_| None),
);
}
}
+23 -16
View File
@@ -1,6 +1,5 @@
use std::{io::ErrorKind, path::Path};
use std::{io::ErrorKind, path::Path, sync::Arc};
use async_compat::Compat;
use bevy::{
asset::io::{AssetReader, AssetReaderError, PathStream, Reader, VecReader},
prelude::*,
@@ -22,10 +21,10 @@ impl RemoteAssetReader {
let token = path
.ext_file_prefix()
.ok_or_else(|| {
AssetReaderError::Io(std::io::Error::new(
AssetReaderError::Io(Arc::new(std::io::Error::new(
ErrorKind::Other,
format!("Failed to parse token from '{path:?}'"),
))
)))
})?
.to_str()
.unwrap();
@@ -41,10 +40,10 @@ impl RemoteAssetReader {
.json::<GetMediaFileSuccessResponse>()
.await
.map_err(IntoError::<AssetReaderError>::into),
other => Err(AssetReaderError::Io(std::io::Error::new(
other => Err(AssetReaderError::Io(Arc::new(std::io::Error::new(
ErrorKind::Other,
format!("Status Code from endpoint: {other}"),
))),
)))),
}?;
let url = format!(
@@ -70,10 +69,10 @@ impl RemoteAssetReader {
.map_err(IntoError::<AssetReaderError>::into)?
.into_iter()
.collect::<Vec<_>>()),
other => Err(AssetReaderError::Io(std::io::Error::new(
other => Err(AssetReaderError::Io(Arc::new(std::io::Error::new(
ErrorKind::Other,
format!("Status Code from bucket URL: {other}"),
))),
)))),
}?;
let reader: Box<Reader> = Box::new(VecReader::new(bytes));
@@ -85,19 +84,22 @@ impl RemoteAssetReader {
let token = path
.ext_file_prefix()
.ok_or_else(|| {
AssetReaderError::Io(std::io::Error::new(
AssetReaderError::Io(Arc::new(std::io::Error::new(
ErrorKind::Other,
format!("Failed to parse token from '{path:?}'"),
))
)))
})?
.to_str()
.unwrap();
let bytes = wasm::LoadLocalFromIdb::new(token);
info!("Sent request to bevy");
let bytes = bytes
.await
.map_err(|e| std::io::Error::new(ErrorKind::Other, format!("{e:?}")))?;
let bytes = bytes.await.map_err(|e| {
AssetReaderError::Io(Arc::new(std::io::Error::new(
ErrorKind::Other,
format!("{e:?}"),
)))
})?;
let reader: Box<Reader> = Box::new(VecReader::new(bytes));
Ok(reader)
@@ -114,7 +116,9 @@ impl AssetReader for RemoteAssetReader {
info!("wasm mode not detected");
// NB: Headless mode (server rendering) is running without a Tokio runtime.
// The reqwest HTTP client requires async-compat to function at runtime.
Box::pin(Compat::new(async move { self.load(path).await }))
Box::pin(async_compat::Compat::new(
async move { self.load(path).await },
))
}
#[cfg(all(feature = "wasm", target_family = "wasm"))]
@@ -159,7 +163,10 @@ trait IntoError<Output> {
impl IntoError<AssetReaderError> for reqwest::Error {
fn into(self) -> AssetReaderError {
AssetReaderError::Io(std::io::Error::new(ErrorKind::Other, format!("{self}")))
AssetReaderError::Io(Arc::new(std::io::Error::new(
ErrorKind::Other,
format!("{self}"),
)))
}
}
@@ -181,7 +188,7 @@ mod wasm {
type: "request-file";
detail: string;
}
declare global {
export interface GlobalEventHandlersEventMap {
"request-file": RequestFileEvent;
+73 -44
View File
@@ -1,17 +1,12 @@
use bevy::{
asset::{DependencyLoadState, LoadState, RecursiveDependencyLoadState},
core_pipeline::{
bloom::{BloomCompositeMode, BloomPrefilterSettings, BloomSettings},
fxaa::Fxaa,
tonemapping::Tonemapping,
},
asset::{DependencyLoadState, LoadState},
core_pipeline::{tonemapping::Tonemapping, Skybox},
gltf::Gltf,
math::vec2,
pbr::ShadowFilteringMethod,
prelude::*,
render::{camera::RenderTarget, primitives::Aabb},
render::primitives::Aabb,
scene::InstanceId,
utils::{EntityHashSet, HashSet},
utils::HashSet,
};
#[cfg(feature = "wasm")]
use serde_repr::{Deserialize_repr, Serialize_repr};
@@ -24,20 +19,19 @@ use space_editor::{
};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::wasm_bindgen;
use wgpu::{TextureDescriptor, TextureUsages};
use crate::{
anim::timeline_animation::AnimationTarget,
camera_controller::{CameraController, CameraControllerBundle},
change_tracking::{ChangeTracker, ChangeTrackingPlugin},
debug::DebugPoint,
headless::{is_headless, is_not_headless, HeadlessSettings},
is_editor,
math::{Combinable, Extrema},
screenshot::ScreenCapture,
MainCamera,
};
#[cfg(feature = "headless")]
use crate::screenshot::ScreenCapture;
#[cfg(feature = "wasm")]
mod wasm;
@@ -48,28 +42,24 @@ pub struct SceneSpawnerSystem;
impl Plugin for ScenePlugin {
fn build(&self, app: &mut App) {
app.add_plugins(ChangeTrackingPlugin);
#[cfg(not(feature = "headless"))]
app.add_plugins(crate::change_tracking::ChangeTrackingPlugin);
app.insert_resource(ClearColor(Color::hsl(0.0, 0.0, 0.25)))
.insert_resource(Msaa::Off)
.add_state::<SceneState>()
.init_state::<SceneState>()
.add_event::<LoadLibraryObjectEvent>()
.add_event::<LoadSceneEvent>()
.add_event::<ImportSceneEvent>()
.add_systems(
Startup,
(
initialize.run_if(is_not_headless),
initialiaze_headless.run_if(is_headless),
),
)
.add_systems(Startup, initialize)
.add_systems(
Update,
(
init_object_loading,
init_scene_import,
init_scene_loading,
start_capture.run_if(is_headless),
#[cfg(feature = "headless")]
start_capture,
)
.in_set(SceneSpawnerSystem),
)
@@ -88,19 +78,23 @@ impl Plugin for ScenePlugin {
),
);
app.init_resource::<SkyboxHolder>();
app.add_systems(Update, attach_skybox);
//Auto loading screen
//Register handles on which we will look to show loading screen
app.init_resource::<LoadingQueue>();
app.add_systems(
PostUpdate,
(
check_loading,
auto_add_handle::<Scene>,
auto_add_handle::<Mesh>,
auto_add_handle::<StandardMaterial>,
),
);
app.add_systems(PreUpdate, check_loading);
//setup default light
app.add_systems(Startup, minimal_scene_setup);
@@ -111,7 +105,7 @@ impl Plugin for ScenePlugin {
wasm::handle_spawn_asset_messages,
wasm::notify_state_changes
.map(bevy::utils::error)
.run_if(resource_changed::<State<SceneState>>()),
.run_if(resource_changed::<State<SceneState>>),
wasm::notify_loading_queue_changes.map(bevy::utils::error),
),
);
@@ -120,6 +114,23 @@ impl Plugin for ScenePlugin {
}
}
#[derive(Resource, Default)]
pub struct SkyboxHolder(pub Option<Skybox>);
fn attach_skybox(
mut cmd: Commands,
skybox_holder: ResMut<SkyboxHolder>,
mut q_camera: Query<Entity, (With<Camera3d>, Without<Skybox>)>,
) {
let Some(skybox) = skybox_holder.0.as_ref() else {
return;
};
for entity in q_camera.iter_mut() {
cmd.entity(entity).insert(skybox.clone());
}
}
fn minimal_scene_setup(mut cmd: Commands) {
cmd.spawn((
Name::new("Directional Light"),
@@ -127,7 +138,7 @@ fn minimal_scene_setup(mut cmd: Commands) {
DirectionalLightBundle {
directional_light: DirectionalLight {
shadows_enabled: false,
illuminance: 20000.0,
illuminance: 5000.0,
..default()
},
transform: Transform::default().looking_at(Vec3::NEG_ONE, Vec3::Y),
@@ -160,6 +171,7 @@ pub struct LoadingQueue {
}
fn check_loading(
mut frames_without_loading: Local<usize>,
mut loading_queue: ResMut<LoadingQueue>,
mut st_next: ResMut<NextState<SceneState>>,
r_scene_state: Res<State<SceneState>>,
@@ -170,38 +182,39 @@ fn check_loading(
// `LoadingQueue` is empty when we're in `SceneState::None`, it just means
// our startup routine hasn't yet gotten to the point of processing any
// `Handle<T>`s.
if loading_queue.queue.is_empty() && **r_scene_state == SceneState::Loading {
if loading_queue.queue.is_empty()
&& **r_scene_state == SceneState::Loading
&& *frames_without_loading > 200
{
info!("Transition to active");
st_next.set(SceneState::Active);
return;
}
if loading_queue.queue.is_empty() && **r_scene_state == SceneState::Loading {
*frames_without_loading += 1;
}
if !loading_queue.queue.is_empty() {
*frames_without_loading = 0;
//filter queue to only include notloaded assets
let mut not_loaded_count = 0;
// info!("Loading {} assets", loading_queue.queue.len());
loading_queue.queue.retain(|h| {
if let Some((load_state, dep_load_state, rec_load_state)) =
asset_server.get_load_states(h.id())
{
let is_loaded = dep_load_state == DependencyLoadState::Loaded;
let is_failed = dep_load_state == DependencyLoadState::Failed
|| rec_load_state == RecursiveDependencyLoadState::Failed
|| load_state == LoadState::Failed;
if let Some((load_state, dep_load_state, _)) = asset_server.get_load_states(h.id()) {
if load_state == LoadState::NotLoaded {
not_loaded_count += 1;
}
!(is_failed || is_loaded)
load_state == LoadState::Loading || dep_load_state == DependencyLoadState::Loading
} else {
false
}
});
info!("Loading {} assets", loading_queue.queue.len());
if loading_queue.queue.len() - not_loaded_count == 0 {
//not change state
} else {
@@ -212,9 +225,10 @@ fn check_loading(
fn auto_add_handle<T: Asset>(
mut queue: ResMut<LoadingQueue>,
query: Query<&Handle<T>, Added<Handle<T>>>,
query: Query<&Handle<T>, Changed<Handle<T>>>,
) {
for h in query.iter() {
info!("Added: {}", h.id());
queue.queue.push(h.clone().untyped());
}
}
@@ -285,7 +299,17 @@ pub struct SceneData {
#[derive(Component)]
pub struct SceneRoot;
#[cfg(not(feature = "headless"))]
fn initialize(mut cmd: Commands) {
use crate::change_tracking::ChangeTracker;
use bevy::{
core_pipeline::{
bloom::{BloomCompositeMode, BloomPrefilterSettings, BloomSettings},
fxaa::Fxaa,
},
pbr::ShadowFilteringMethod,
};
// Camera, graphics settings
let focus = Vec3::ZERO;
let camera_xform = Transform::from_xyz(-2.5, 4.5, 5.0).looking_at(focus, Vec3::Y);
@@ -321,11 +345,15 @@ fn initialize(mut cmd: Commands) {
));
}
fn initialiaze_headless(
#[cfg(feature = "headless")]
fn initialize(
mut cmd: Commands,
mut image_assets: ResMut<Assets<Image>>,
headless_settings: Res<HeadlessSettings>,
headless_settings: Res<crate::headless::HeadlessSettings>,
) {
use bevy::render::camera::RenderTarget;
use wgpu::{TextureDescriptor, TextureUsages};
// Camera, graphics settings
let focus = Vec3::ZERO;
let camera_xform = Transform::from_xyz(-2.5, 4.5, 5.0).looking_at(focus, Vec3::Y);
@@ -453,7 +481,7 @@ fn tag_scene_elements(
fn autoplay_animations(
mut cmd: Commands,
mut l_done: Local<HashSet<String>>,
mut l_animating: Local<EntityHashSet<Entity>>,
mut l_animating: Local<HashSet<Entity>>,
ra_gltfs: Res<Assets<Gltf>>,
ra_anims: Res<Assets<AnimationClip>>,
q_prefabs: Query<(&GltfPrefab, &Handle<Gltf>)>,
@@ -541,7 +569,7 @@ fn frame_scene_in_view(
// Project each corner onto the camera plane
let v = p - plane_origin;
let proj = p - v.project_onto_normalized(plane_normal);
let proj = p - v.project_onto_normalized(plane_normal.xyz());
cmd.spawn(DebugPoint {
point: proj,
@@ -611,6 +639,7 @@ fn frame_scene_in_view(
cam_xform.translation = cam.focus + cam_xform.back() * radius;
}
#[cfg(feature = "headless")]
fn start_capture(mut captures: Query<&mut ScreenCapture, Added<ScreenCapture>>) {
for mut capture in captures.iter_mut() {
capture.start_capturing();
+37 -23
View File
@@ -3,7 +3,7 @@ use bevy::{
core_pipeline::{core_3d, prepass::ViewPrepassTextures},
prelude::*,
render::{
render_graph::{RenderGraphApp, ViewNode, ViewNodeRunner},
render_graph::{RenderGraphApp, RenderLabel, ViewNode, ViewNodeRunner},
render_resource::{
Buffer, BufferDescriptor, BufferUsages, ImageCopyBuffer, ImageDataLayout, MapMode,
},
@@ -11,12 +11,14 @@ use bevy::{
view::ViewTarget,
Extract, Render, RenderSet,
},
tasks::Task,
};
use bytemuck::AnyBitPattern;
use image::{
error::UnsupportedErrorKind, EncodableLayout, ImageBuffer, ImageError, Pixel,
PixelWithColorType, Rgba,
};
use std::sync::Mutex;
use wgpu::{Extent3d, MaintainBase};
use crate::scene::SceneState;
@@ -51,16 +53,18 @@ impl Plugin for ScreenShotPlugin {
)
//Render Graph Additions
.add_render_graph_node::<ViewNodeRunner<ScreenCaptureNode>>(
core_3d::graph::NAME,
ScreenCaptureNode::NAME,
core_3d::graph::Core3d,
ScreenCaptureNode,
)
.add_render_graph_edges(
core_3d::graph::NAME,
&[
core_3d::graph::node::TONEMAPPING,
ScreenCaptureNode::NAME,
core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
],
.add_render_graph_edge(
core_3d::graph::Core3d,
core_3d::graph::Node3d::Tonemapping,
ScreenCaptureNode,
)
.add_render_graph_edge(
core_3d::graph::Core3d,
ScreenCaptureNode,
core_3d::graph::Node3d::EndMainPassPostProcessing,
);
}
}
@@ -112,7 +116,7 @@ impl ScreenCapture {
}
pub fn should_capture(&self) -> bool {
self.frames < self.max_frames && self.is_capturing()
self.frames <= self.max_frames && self.is_capturing()
}
pub fn frames_captured(&self) -> u32 {
@@ -128,10 +132,17 @@ fn update_screen_capture_on_extract_step(
mut screen_captures: Query<&mut ScreenCapture>,
mut exit_event: EventWriter<AppExit>,
) {
let loading_count;
{
let mut queue = QUEUE_SAVE.lock().unwrap();
queue.retain(|task| !task.is_finished());
loading_count = queue.len();
}
for mut screen_capture in screen_captures.iter_mut() {
screen_capture.frames += 1;
if !screen_capture.should_capture() {
if !screen_capture.should_capture() && loading_count == 0 {
exit_event.send(AppExit);
}
// if screen_capture.number_of_frames == 0 {
@@ -146,7 +157,7 @@ fn extract_screen_capture_components(
screen_capure_comps: Extract<Query<(Entity, &ScreenCapture), With<Camera>>>,
scene_state: Extract<Res<State<SceneState>>>,
) {
if scene_state.get() != &SceneState::Active {
if *scene_state.get() != SceneState::Active {
return;
};
for (entity, screen_capture) in screen_capure_comps.iter() {
@@ -185,7 +196,7 @@ fn prepare_screen_capture_buffers(
let size = texture.size();
let format = texture.format();
let bytes_per_row =
(size.width / format.block_dimensions().0) * format.block_size(None).unwrap();
(size.width / format.block_dimensions().0) * format.block_copy_size(None).unwrap();
let padded_bytes_per_row =
RenderDevice::align_copy_bytes_per_row(bytes_per_row as usize) as u32;
@@ -210,13 +221,9 @@ fn prepare_screen_capture_buffers(
// Screen Shot View Node
//==============================================================================
#[derive(Default)]
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel, Default)]
struct ScreenCaptureNode;
impl ScreenCaptureNode {
pub const NAME: &'static str = "ScreenCaptureNode";
}
impl ViewNode for ScreenCaptureNode {
type ViewQuery = (
Entity,
@@ -253,6 +260,8 @@ impl ViewNode for ScreenCaptureNode {
// Saving the buffer to file
//==============================================================================
static QUEUE_SAVE: Mutex<Vec<Task<()>>> = Mutex::new(Vec::new());
fn save_buffer_to_file(
mut frames_to_skip: Local<Option<u32>>,
buffers: Query<(&ScreenCapture, &ScreenCaptureBuffers)>,
@@ -297,14 +306,19 @@ fn save_buffer_to_file(
screen_capture.to_directory, screen_capture.frames
);
save_buffer::<Rgba<u8>>(&main_image_values, &buffers.main_buffer_extent, &path);
let pool = bevy::tasks::AsyncComputeTaskPool::get();
let extent = buffers.main_buffer_extent;
let task = pool.spawn(async move {
save_buffer::<Rgba<u8>>(main_image_values, extent, path.clone());
});
QUEUE_SAVE.lock().unwrap().push(task);
}
}
fn save_buffer<P: Pixel + PixelWithColorType>(
image_bytes: &[P::Subpixel],
source_size: &Extent3d,
path: &str,
image_bytes: Vec<P::Subpixel>,
source_size: Extent3d,
path: String,
) where
P::Subpixel: AnyBitPattern,
[P::Subpixel]: EncodableLayout,
+57 -53
View File
@@ -1,11 +1,4 @@
use core::fmt;
use bevy::{
asset::LoadState,
core_pipeline::Skybox,
prelude::*,
render::render_resource::{TextureViewDescriptor, TextureViewDimension},
};
use bevy::{asset::LoadState, prelude::*};
use crate::MainCamera;
@@ -16,9 +9,9 @@ impl Plugin for SkyboxPlugin {
app.add_systems(
Update,
(
load_skybox.run_if(resource_exists_and_changed::<SkyboxId>()),
load_skybox.run_if(resource_exists_and_changed::<SkyboxId>),
spawn_skybox
.run_if(resource_exists::<SkyboxAssets>())
.run_if(resource_exists::<SkyboxAssets>)
.run_if(skybox_assets_loaded),
),
);
@@ -38,7 +31,7 @@ struct SkyboxAssets {
env_specular: Handle<Image>,
}
fn load_skybox(mut cmd: Commands, assets: Res<AssetServer>, id: Res<SkyboxId>) {
fn load_skybox(mut cmd: Commands, _assets: Res<AssetServer>, id: Res<SkyboxId>) {
let id = &**id;
if let Ok(color) = Color::hex(id) {
cmd.insert_resource(ClearColor(color));
@@ -47,12 +40,19 @@ fn load_skybox(mut cmd: Commands, assets: Res<AssetServer>, id: Res<SkyboxId>) {
brightness: 1.0,
})
} else {
let paths = SkyboxPaths::new(id);
cmd.insert_resource(SkyboxAssets {
cubemap: assets.load(paths.cubemap),
env_diffuse: assets.load(paths.env_diffuse),
env_specular: assets.load(paths.env_specular),
let color = Color::hex("AAAAAA").unwrap();
cmd.insert_resource(ClearColor(color));
cmd.insert_resource(AmbientLight {
color,
brightness: 1.0,
});
// let paths = SkyboxPaths::new(id);
// cmd.insert_resource(SkyboxAssets {
// cubemap: assets.load(paths.cubemap),
// env_diffuse: assets.load(paths.env_diffuse),
// env_specular: assets.load(paths.env_specular),
// });
}
}
@@ -81,49 +81,53 @@ fn skybox_assets_loaded(
}
fn spawn_skybox(
mut cmd: Commands,
mut images: ResMut<Assets<Image>>,
skybox_assets: Res<SkyboxAssets>,
q_camera: Query<Entity, With<MainCamera>>,
_cmd: Commands,
_images: ResMut<Assets<Image>>,
_skybox_assets: Res<SkyboxAssets>,
_q_camera: Query<Entity, With<MainCamera>>,
) {
// Configure the skybox texture as a cubemap
let image = images.get_mut(&skybox_assets.cubemap).unwrap();
if image.texture_descriptor.array_layer_count() == 1 {
image.reinterpret_stacked_2d_as_array(6);
image.texture_view_descriptor = Some(TextureViewDescriptor {
dimension: Some(TextureViewDimension::Cube),
..default()
});
}
// // Configure the skybox texture as a cubemap
// let image = images.get_mut(&skybox_assets.cubemap).unwrap();
// if image.texture_descriptor.array_layer_count() == 1 {
// image.reinterpret_stacked_2d_as_array(6);
// image.texture_view_descriptor = Some(TextureViewDescriptor {
// dimension: Some(TextureViewDimension::Cube),
// ..default()
// });
// }
// Add components to the scene camera
cmd.entity(q_camera.single()).insert((
Skybox(skybox_assets.cubemap.clone()),
EnvironmentMapLight {
diffuse_map: skybox_assets.env_diffuse.clone(),
specular_map: skybox_assets.env_specular.clone(),
},
));
// // Add components to the scene camera
// cmd.entity(q_camera.single()).insert((
// Skybox {
// image: skybox_assets.cubemap.clone(),
// brightness: 10.0,
// },
// EnvironmentMapLight {
// diffuse_map: skybox_assets.env_diffuse.clone(),
// specular_map: skybox_assets.env_specular.clone(),
// intensity: 10.0,
// },
// ));
// Remove the intermediate resource
cmd.remove_resource::<SkyboxAssets>();
// // Remove the intermediate resource
// cmd.remove_resource::<SkyboxAssets>();
}
struct SkyboxPaths {
cubemap: String,
env_diffuse: String,
env_specular: String,
}
// struct SkyboxPaths {
// cubemap: String,
// env_diffuse: String,
// env_specular: String,
// }
impl SkyboxPaths {
fn new(name: impl fmt::Display) -> Self {
Self {
cubemap: format!("skyboxes/{name}.hdr"),
env_diffuse: format!("skyboxes/{name}_diffuse.zstd.ktx2"),
env_specular: format!("skyboxes/{name}_specular.zstd.ktx2"),
}
}
}
// impl SkyboxPaths {
// fn new(name: impl fmt::Display) -> Self {
// Self {
// cubemap: format!("skyboxes/{name}.hdr"),
// env_diffuse: format!("skyboxes/{name}_diffuse.zstd.ktx2"),
// env_specular: format!("skyboxes/{name}_specular.zstd.ktx2"),
// }
// }
// }
#[cfg(feature = "wasm")]
mod wasm {
+4 -4
View File
@@ -31,22 +31,22 @@ fn read_command_system(mut commands: Commands, mut change_event: EventWriter<New
match cmd {
BlockingCommand::Cube => {
new_entity
.insert(MeshPrimitivePrefab::Cube(1.0))
.insert(MeshPrimitive3dPrefab::Cube(1.0))
.insert(Name::new("Cube"));
}
BlockingCommand::Sphere => {
new_entity
.insert(MeshPrimitivePrefab::Sphere(SpherePrefab { r: 1.0 }))
.insert(MeshPrimitive3dPrefab::Sphere(SpherePrefab { r: 1.0 }))
.insert(Name::new("Sphere"));
}
BlockingCommand::Torus => {
new_entity
.insert(MeshPrimitivePrefab::Torus(TorusPrefab::default()))
.insert(MeshPrimitive3dPrefab::Torus(TorusPrefab::default()))
.insert(Name::new("Torus"));
}
BlockingCommand::Cylinder => {
new_entity
.insert(MeshPrimitivePrefab::Cylinder(CylinderPrefab::default()))
.insert(MeshPrimitive3dPrefab::Cylinder(CylinderPrefab::default()))
.insert(Name::new("Cylinder"));
}
};
+4 -3
View File
@@ -14,7 +14,7 @@ impl Plugin for FpsCounterPlugin {
.add_systems(Update, update)
.add_systems(
Update,
update_visibility.run_if(resource_exists_and_changed::<DebugMode>()),
update_visibility.run_if(resource_exists_and_changed::<DebugMode>),
);
}
}
@@ -31,11 +31,12 @@ fn layout(mut cmd: Commands) {
..default()
},
)
.with_text_alignment(TextAlignment::Right)
.with_text_justify(JustifyText::Right)
.with_style(Style {
position_type: PositionType::Absolute,
right: Val::Px(16.),
bottom: Val::Px(16.),
..default()
});
@@ -53,7 +54,7 @@ fn update(
diag: Res<DiagnosticsStore>,
mut counter: Query<&mut Text, With<FramerateCounter>>,
) {
if let Some(fps_diag) = diag.get(FrameTimeDiagnosticsPlugin::FPS) {
if let Some(fps_diag) = diag.get(&FrameTimeDiagnosticsPlugin::FPS) {
let fps = if let Some(fps_smoothed) = fps_diag.smoothed() {
fps_smoothed
} else {
+13 -4
View File
@@ -12,24 +12,33 @@ use serde_json::{self, json, Map as JsonMap, Value as JsonValue};
use space_editor::{
space_editor_ui::{
editor_tab::{EditorTab, EditorTabName},
EditorUiAppExt,
ui_plugin::EditorUiAppExt,
},
space_prefab::{
component::MeshPrimitivePrefab, editor_registry::EditorRegistryExt, save::SaveState,
component::MeshPrimitive3dPrefab, editor_registry::EditorRegistryExt, save::SaveState,
},
};
use wasm_bindgen::prelude::*;
use crate::material::short_material::ShortMaterial;
use crate::{
interaction::Selection,
wasm::{self, StaticBuffer, WasmMessageBuffer},
};
use crate::{material::short_material::ShortMaterial, StudioMode};
pub struct InspectorPlugin;
impl Plugin for InspectorPlugin {
fn build(&self, app: &mut App) {
#[cfg(not(feature = "wasm"))]
return;
if let Some(state) = app.world.get_resource::<StudioMode>() {
if *state == StudioMode::Headless {
return;
}
}
app.init_resource::<EntityDescription>();
app.add_event::<UpdateEntityDescription>();
@@ -67,7 +76,7 @@ impl Plugin for InspectorPlugin {
app.register_inspect::<Transform>();
app.register_inspect::<Name>();
app.register_inspect::<MeshPrimitivePrefab>();
app.register_inspect::<MeshPrimitive3dPrefab>();
//register standart material
{
+27 -6
View File
@@ -48,10 +48,13 @@ impl fmt::Debug for ViewportSize {
#[cfg(feature = "wasm")]
pub mod wasm {
use bevy::prelude::*;
use bevy::{prelude::*, render::camera::Viewport};
use wasm_bindgen::prelude::*;
use crate::wasm::{StaticBuffer, WasmMessageBuffer};
use crate::{
wasm::{StaticBuffer, WasmMessageBuffer},
MainCamera,
};
use super::ViewportSize;
@@ -66,9 +69,10 @@ pub mod wasm {
impl Plugin for ViewportPlugin {
fn build(&self, app: &mut App) {
app.add_event::<ViewportResizeEvent>()
.add_systems(PreUpdate, dispatch_resize_events)
.add_systems(Update, sync_window_to_viewport);
app.add_event::<ViewportResizeEvent>().add_systems(
PreUpdate,
(dispatch_resize_events, sync_window_to_viewport).chain(),
);
}
}
@@ -78,16 +82,33 @@ pub mod wasm {
fn sync_window_to_viewport(
mut events: EventReader<ViewportResizeEvent>,
mut window: Query<&mut Window>,
mut q_camera: Query<&mut Camera, With<MainCamera>>,
) {
if let Some(event) = events.read().last() {
let ViewportSize { width, height } = event.0;
window.single_mut().resolution.set(width, height);
let mut window = window.single_mut();
let scale_factor = window.scale_factor() as f32;
window
.resolution
.set(width * scale_factor, height * scale_factor);
q_camera.single_mut().viewport = Some(Viewport {
physical_size: UVec2::new(
(width * scale_factor) as u32,
(height * scale_factor) as u32,
),
..Default::default()
});
info!("Viewport size: ({:.2}, {:.2})", width, height);
}
}
fn dispatch_resize_events(mut events: EventWriter<ViewportResizeEvent>) {
if let Some(resize) = RESIZE_BUF.take_message() {
events.send(ViewportResizeEvent(resize));
info!(
"Dispatch Viewport size: ({:.2}, {:.2})",
resize.width, resize.height
);
}
}
}
+1 -1
View File
@@ -153,7 +153,7 @@ fn take_screenshots(
src_buffer,
img.width(),
img.height(),
ColorType::Rgb8,
ColorType::Rgb8.into(),
) {
error!("{err}");
return;
+2 -5
View File
@@ -37,10 +37,7 @@ fn spawn_world_grid(
mut ra_meshes: ResMut<Assets<Mesh>>,
mut ra_mat_proc_grid: ResMut<Assets<ProceduralGridMaterial>>,
) {
let plane = ra_meshes.add(Mesh::from(shape::Plane {
size: 200.0,
subdivisions: 0,
}));
let plane = ra_meshes.add(Plane3d::default());
let proc_grid = ra_mat_proc_grid.add(ProceduralGridMaterial {});
cmd.spawn((
@@ -53,7 +50,7 @@ fn spawn_world_grid(
MaterialMeshBundle {
mesh: plane.clone(),
material: proc_grid.clone(),
transform: Transform::IDENTITY,
transform: Transform::IDENTITY.with_scale(Vec3::splat(200.0)),
..default()
},
NotShadowCaster,
Executable → Regular
+3 -1
View File
@@ -13,7 +13,9 @@ if [ ! -f "${full_path}" ]; then
wget "${scene_url}" -O "${full_path}"
fi
cargo run -p studio --bin studio-headless \
cargo run -p studio \
--bin studio-headless \
--features headless \
-- \
scene \
-o frames/ \