mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-17 00:13:17 +00:00
@@ -1,5 +1,7 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::scene::{SceneElement, SceneSpawnerSystem, SceneState};
|
||||
|
||||
pub struct ChangeTrackingPlugin;
|
||||
|
||||
impl Plugin for ChangeTrackingPlugin {
|
||||
@@ -8,6 +10,10 @@ impl Plugin for ChangeTrackingPlugin {
|
||||
|
||||
// Add more of these systems for each component that we want to track
|
||||
app.add_systems(Update, send_change_events::<Transform>);
|
||||
app.add_systems(
|
||||
OnEnter(SceneState::Active),
|
||||
track_scene_elements.after(SceneSpawnerSystem),
|
||||
);
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
app.add_systems(
|
||||
@@ -29,6 +35,15 @@ fn send_change_events<T: Component>(
|
||||
}
|
||||
}
|
||||
|
||||
fn track_scene_elements(
|
||||
mut cmd: Commands,
|
||||
q_scene_elements: Query<Entity, (With<Transform>, With<SceneElement>, Without<ChangeTracker>)>,
|
||||
) {
|
||||
for element in q_scene_elements.iter() {
|
||||
cmd.entity(element).insert(ChangeTracker);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event, Deref)]
|
||||
pub struct EntityChangedEvent(pub Entity);
|
||||
|
||||
|
||||
+11
-5
@@ -177,11 +177,17 @@ fn translate_dcc_camera_events(
|
||||
else if (keyboard.just_released(KeyCode::AltLeft) && !keyboard.pressed(KeyCode::AltRight))
|
||||
|| (keyboard.just_released(KeyCode::AltRight) && !keyboard.pressed(KeyCode::AltLeft))
|
||||
{
|
||||
input.send(InputAction(
|
||||
Lifecycle::Stop,
|
||||
CameraMotion::Orbit(Vec2::ZERO).into(),
|
||||
));
|
||||
input.send(InputAction(Lifecycle::Stop, CameraMotion::Zoom(0.).into()));
|
||||
// Stop Camera Orbit
|
||||
if mouse_buttons.pressed(MouseButton::Left) {
|
||||
input.send(InputAction(
|
||||
Lifecycle::Stop,
|
||||
CameraMotion::Orbit(Vec2::ZERO).into(),
|
||||
));
|
||||
}
|
||||
// Stop Camera Zoom
|
||||
else if mouse_buttons.pressed(MouseButton::Right) {
|
||||
input.send(InputAction(Lifecycle::Stop, CameraMotion::Zoom(0.).into()));
|
||||
}
|
||||
}
|
||||
// Camera Rotation
|
||||
else if mouse_buttons.pressed(MouseButton::Right) {
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::interaction::TransformSpace;
|
||||
use super::material::GizmoMaterial;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
pub(super) struct TransformGizmo {
|
||||
pub struct TransformGizmo {
|
||||
pub(super) target: Entity,
|
||||
pub(super) transform_space: TransformSpace,
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use self::{
|
||||
rotation::{spawn_rotation_gizmo, update_ss_ring_rotation},
|
||||
translation::spawn_translation_gizmo,
|
||||
};
|
||||
pub use common::TransformGizmo;
|
||||
|
||||
use super::{Selection, TransformMode, TransformType};
|
||||
|
||||
|
||||
+1
-1
@@ -158,7 +158,7 @@ pub fn start(init: InitParams, mode: StudioMode, window_config: Option<Window>)
|
||||
..default()
|
||||
}),
|
||||
CommonPlugins,
|
||||
#[cfg(all(feature = "wasm", feature = "wasm_screenshot"))]
|
||||
#[cfg(feature = "wasm")]
|
||||
wasm::ScreenshotPlugin,
|
||||
));
|
||||
|
||||
|
||||
+3
-17
@@ -23,7 +23,6 @@ use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wgpu::{TextureDescriptor, TextureUsages};
|
||||
|
||||
use crate::{
|
||||
anim::SkeletalJoint,
|
||||
camera_controller::{CameraController, CameraControllerBundle},
|
||||
change_tracking::{ChangeTracker, ChangeTrackingPlugin},
|
||||
debug::DebugPoint,
|
||||
@@ -63,13 +62,9 @@ impl Plugin for ScenePlugin {
|
||||
.add_systems(
|
||||
OnEnter(SceneState::Active),
|
||||
(
|
||||
(
|
||||
tag_scene_elements,
|
||||
frame_scene_in_view,
|
||||
start_capture.run_if(is_headless),
|
||||
),
|
||||
apply_deferred,
|
||||
track_scene_elements,
|
||||
tag_scene_elements,
|
||||
frame_scene_in_view,
|
||||
start_capture.run_if(is_headless),
|
||||
)
|
||||
.chain()
|
||||
.in_set(SceneSpawnerSystem),
|
||||
@@ -524,15 +519,6 @@ fn start_capture(mut captures: Query<&mut ScreenCapture>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn track_scene_elements(
|
||||
mut commands: Commands,
|
||||
scene_elements: Query<Entity, (With<Transform>, With<SkeletalJoint>)>,
|
||||
) {
|
||||
for element in scene_elements.iter() {
|
||||
commands.entity(element).insert(ChangeTracker);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
mod wasm {
|
||||
use bevy::{prelude::*, window::PrimaryWindow};
|
||||
|
||||
@@ -5,12 +5,10 @@ use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
mod conv;
|
||||
mod init;
|
||||
#[cfg(feature = "wasm_screenshot")]
|
||||
mod screenshot;
|
||||
mod util;
|
||||
|
||||
pub use conv::*;
|
||||
#[cfg(feature = "wasm_screenshot")]
|
||||
pub use screenshot::ScreenshotPlugin;
|
||||
pub(crate) use util::*;
|
||||
|
||||
|
||||
@@ -10,9 +10,11 @@
|
||||
//! to the frontend by converting the `Vec<u8>` to a [`js_sys::Uint8Array`]
|
||||
|
||||
use bevy::{prelude::*, render::view::screenshot::ScreenshotManager, window::PrimaryWindow};
|
||||
use image::{codecs::png::PngEncoder, ColorType, ImageEncoder};
|
||||
use image::{codecs::jpeg::JpegEncoder, ColorType, ImageEncoder};
|
||||
use wasm_bindgen::prelude::{wasm_bindgen, JsValue};
|
||||
|
||||
use crate::{interaction::gizmos::TransformGizmo, world_grid::WorldGridPlane};
|
||||
|
||||
use super::{AsJsValue, StaticBuffer, WasmMessageBuffer};
|
||||
|
||||
static SCREENSHOT_REQUEST: WasmMessageBuffer<()> = WasmMessageBuffer::new();
|
||||
@@ -22,16 +24,25 @@ pub struct ScreenshotPlugin;
|
||||
|
||||
impl Plugin for ScreenshotPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_event::<ScreenshotRequestedEvent>();
|
||||
app.add_systems(
|
||||
Update,
|
||||
(
|
||||
take_screenshots.map(bevy::utils::error),
|
||||
(
|
||||
prepare_screenshot,
|
||||
apply_deferred,
|
||||
take_screenshots.map(bevy::utils::error),
|
||||
)
|
||||
.chain(),
|
||||
send_screenshots.map(bevy::utils::error),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event)]
|
||||
pub struct ScreenshotRequestedEvent;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Clone)]
|
||||
pub struct Screenshot {
|
||||
@@ -74,21 +85,46 @@ impl AsJsValue for Screenshot {
|
||||
|
||||
#[wasm_bindgen(js_name = requestScreenshot)]
|
||||
pub fn request_screenshot() {
|
||||
info!("[wasm::screenshot] Screenshot request received");
|
||||
SCREENSHOT_REQUEST.write_message(());
|
||||
}
|
||||
|
||||
fn prepare_screenshot(
|
||||
mut q_editor_visuals: Query<&mut Visibility, Or<(With<WorldGridPlane>, With<TransformGizmo>)>>,
|
||||
mut ew: EventWriter<ScreenshotRequestedEvent>,
|
||||
) {
|
||||
if SCREENSHOT_REQUEST.take_message().is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
info!(
|
||||
"[wasm::screenshot] Hiding {} editor visuals",
|
||||
q_editor_visuals.iter().count()
|
||||
);
|
||||
|
||||
for mut visibility in q_editor_visuals.iter_mut() {
|
||||
*visibility = Visibility::Hidden;
|
||||
}
|
||||
|
||||
ew.send(ScreenshotRequestedEvent);
|
||||
}
|
||||
|
||||
fn take_screenshots(
|
||||
mut r_screenshot_mgr: ResMut<ScreenshotManager>,
|
||||
q_window: Query<Entity, With<PrimaryWindow>>,
|
||||
mut er: EventReader<ScreenshotRequestedEvent>,
|
||||
) -> Result<(), String> {
|
||||
if SCREENSHOT_REQUEST.take_message().is_none() {
|
||||
if er.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Ok(window_ent) = q_window.get_single() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
er.clear();
|
||||
|
||||
info!("[wasm::screenshot] Queueing screenshot request");
|
||||
|
||||
r_screenshot_mgr
|
||||
.take_screenshot(window_ent, |img| {
|
||||
let img = match img.try_into_dynamic().map(|img| img.to_rgb8()) {
|
||||
@@ -112,8 +148,8 @@ fn take_screenshots(
|
||||
};
|
||||
let src_buffer = &img.as_raw()[..buffer_len];
|
||||
|
||||
let mut png_bytes = vec![];
|
||||
if let Err(err) = PngEncoder::new(&mut png_bytes).write_image(
|
||||
let mut jpg_bytes = vec![];
|
||||
if let Err(err) = JpegEncoder::new_with_quality(&mut jpg_bytes, 70).write_image(
|
||||
src_buffer,
|
||||
img.width(),
|
||||
img.height(),
|
||||
@@ -124,7 +160,7 @@ fn take_screenshots(
|
||||
}
|
||||
|
||||
SCREENSHOT.write_message(ScreenshotNative {
|
||||
data: png_bytes,
|
||||
data: jpg_bytes,
|
||||
width: img.width() as usize,
|
||||
height: img.height() as usize,
|
||||
});
|
||||
@@ -146,7 +182,10 @@ declare global {
|
||||
}
|
||||
"#;
|
||||
|
||||
fn send_screenshots(q_window: Query<&Window, With<PrimaryWindow>>) -> Result<(), String> {
|
||||
fn send_screenshots(
|
||||
q_window: Query<&Window, With<PrimaryWindow>>,
|
||||
mut q_editor_visuals: Query<&mut Visibility, Or<(With<WorldGridPlane>, With<TransformGizmo>)>>,
|
||||
) -> Result<(), String> {
|
||||
let Some(screenshot) = SCREENSHOT.take_message() else {
|
||||
return Ok(());
|
||||
};
|
||||
@@ -157,5 +196,16 @@ fn send_screenshots(q_window: Query<&Window, With<PrimaryWindow>>) -> Result<(),
|
||||
|
||||
let screenshot = Screenshot::from(screenshot);
|
||||
|
||||
super::dispatch_to_js(window, "screenshot-ready", &screenshot)
|
||||
super::dispatch_to_js(window, "screenshot-ready", &screenshot)?;
|
||||
|
||||
info!(
|
||||
"[wasm::screenshot] Revealing {} previously hidden editor visuals",
|
||||
q_editor_visuals.iter().count()
|
||||
);
|
||||
|
||||
for mut visibility in q_editor_visuals.iter_mut() {
|
||||
*visibility = Visibility::Inherited;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ impl Plugin for WorldGridPlugin {
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct WorldGridPlane;
|
||||
pub struct WorldGridPlane;
|
||||
|
||||
#[derive(Asset, AsBindGroup, TypePath, Clone)]
|
||||
struct ProceduralGridMaterial {}
|
||||
|
||||
Reference in New Issue
Block a user