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