mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-16 00:03:29 +00:00
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:
@@ -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
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -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
@@ -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
@@ -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
@@ -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: (),
|
||||
),
|
||||
)
|
||||
@@ -1,4 +0,0 @@
|
||||
(
|
||||
meta_format_version: "1.0",
|
||||
asset: Ignore,
|
||||
)
|
||||
@@ -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,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: (),
|
||||
),
|
||||
)
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -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)}",
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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")]
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
),
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ fn take_screenshots(
|
||||
src_buffer,
|
||||
img.width(),
|
||||
img.height(),
|
||||
ColorType::Rgb8,
|
||||
ColorType::Rgb8.into(),
|
||||
) {
|
||||
error!("{err}");
|
||||
return;
|
||||
|
||||
@@ -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
@@ -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/ \
|
||||
|
||||
Reference in New Issue
Block a user