mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-29 09:08:58 +00:00
Exclude editor visuals from screen captures
This commit is contained in:
@@ -22,7 +22,6 @@ import "./app.element.scss"
|
|||||||
@customElement("app-root")
|
@customElement("app-root")
|
||||||
export class AppElement extends LitElement {
|
export class AppElement extends LitElement {
|
||||||
@state() imageCaptureUrl?: string;
|
@state() imageCaptureUrl?: string;
|
||||||
@state() realtimeCapture = true;
|
|
||||||
|
|
||||||
@state() hierarchyWidth = 256;
|
@state() hierarchyWidth = 256;
|
||||||
@state() resizing = false;
|
@state() resizing = false;
|
||||||
@@ -52,12 +51,9 @@ export class AppElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@on("entity-changes")
|
@on("entity-changes")
|
||||||
|
@debounce(1000 / 24)
|
||||||
onStudioChanges(): void {
|
onStudioChanges(): void {
|
||||||
if (this.realtimeCapture) {
|
studio.requestScreenshot();
|
||||||
this.requestScreenshot_realtime();
|
|
||||||
} else {
|
|
||||||
this.requestScreenshot_delayed();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@on("viewport-resized")
|
@on("viewport-resized")
|
||||||
@@ -67,16 +63,6 @@ export class AppElement extends LitElement {
|
|||||||
studio.requestScreenshot();
|
studio.requestScreenshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
@debounce(1000 / 24, { maxWait: 1000 / 5 })
|
|
||||||
requestScreenshot_realtime() {
|
|
||||||
studio.requestScreenshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
@debounce(1000 / 24)
|
|
||||||
requestScreenshot_delayed() {
|
|
||||||
studio.requestScreenshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
@on("screenshot-ready")
|
@on("screenshot-ready")
|
||||||
onScreenshotReady({ detail: screenshot }: ScreenshotReadyEvent) {
|
onScreenshotReady({ detail: screenshot }: ScreenshotReadyEvent) {
|
||||||
const prevUrl = this.imageCaptureUrl;
|
const prevUrl = this.imageCaptureUrl;
|
||||||
@@ -101,11 +87,6 @@ export class AppElement extends LitElement {
|
|||||||
this.hierarchyWidth = Math.max(Math.min(updated, 512), 128);
|
this.hierarchyWidth = Math.max(Math.min(updated, 512), 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleRealtimeCapture(event: InputEvent): void {
|
|
||||||
const { checked } = event.target as HTMLInputElement;
|
|
||||||
this.realtimeCapture = checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshSeed(): void {
|
refreshSeed(): void {
|
||||||
this.seed = randomSeed();
|
this.seed = randomSeed();
|
||||||
}
|
}
|
||||||
@@ -184,6 +165,11 @@ export class AppElement extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
${/*
|
||||||
|
FIXME: Real-time capture is not really feasible until we can come up
|
||||||
|
with a more elegant way to exclude the world grid, gizmos, etc.
|
||||||
|
from the captured image
|
||||||
|
|
||||||
<div class="config v-start h-end">
|
<div class="config v-start h-end">
|
||||||
<label class="config-checkbox">
|
<label class="config-checkbox">
|
||||||
<input
|
<input
|
||||||
@@ -194,6 +180,8 @@ export class AppElement extends LitElement {
|
|||||||
Realtime Capture
|
Realtime Capture
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
*/
|
||||||
|
nothing}
|
||||||
|
|
||||||
${this.loading ? html`
|
${this.loading ? html`
|
||||||
<!-- TODO: Animated spinner or something -- talk to our designers -->
|
<!-- TODO: Animated spinner or something -- talk to our designers -->
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::interaction::TransformSpace;
|
|||||||
use super::material::GizmoMaterial;
|
use super::material::GizmoMaterial;
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
pub(super) struct TransformGizmo {
|
pub struct TransformGizmo {
|
||||||
pub(super) target: Entity,
|
pub(super) target: Entity,
|
||||||
pub(super) transform_space: TransformSpace,
|
pub(super) transform_space: TransformSpace,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use self::{
|
|||||||
rotation::{spawn_rotation_gizmo, update_ss_ring_rotation},
|
rotation::{spawn_rotation_gizmo, update_ss_ring_rotation},
|
||||||
translation::spawn_translation_gizmo,
|
translation::spawn_translation_gizmo,
|
||||||
};
|
};
|
||||||
|
pub use common::TransformGizmo;
|
||||||
|
|
||||||
use super::{Selection, TransformMode, TransformType};
|
use super::{Selection, TransformMode, TransformType};
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ use bevy::{prelude::*, render::view::screenshot::ScreenshotManager, window::Prim
|
|||||||
use image::{codecs::png::PngEncoder, ColorType, ImageEncoder};
|
use image::{codecs::png::PngEncoder, ColorType, ImageEncoder};
|
||||||
use wasm_bindgen::prelude::{wasm_bindgen, JsValue};
|
use wasm_bindgen::prelude::{wasm_bindgen, JsValue};
|
||||||
|
|
||||||
|
use crate::{interaction::gizmos::TransformGizmo, world_grid::WorldGridPlane};
|
||||||
|
|
||||||
use super::{AsJsValue, StaticBuffer, WasmMessageBuffer};
|
use super::{AsJsValue, StaticBuffer, WasmMessageBuffer};
|
||||||
|
|
||||||
static SCREENSHOT_REQUEST: WasmMessageBuffer<()> = WasmMessageBuffer::new();
|
static SCREENSHOT_REQUEST: WasmMessageBuffer<()> = WasmMessageBuffer::new();
|
||||||
@@ -22,16 +24,25 @@ pub struct ScreenshotPlugin;
|
|||||||
|
|
||||||
impl Plugin for ScreenshotPlugin {
|
impl Plugin for ScreenshotPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_event::<ScreenshotRequestedEvent>();
|
||||||
app.add_systems(
|
app.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(
|
(
|
||||||
take_screenshots.map(bevy::utils::error),
|
(
|
||||||
|
prepare_screenshot,
|
||||||
|
apply_deferred,
|
||||||
|
take_screenshots.map(bevy::utils::error),
|
||||||
|
)
|
||||||
|
.chain(),
|
||||||
send_screenshots.map(bevy::utils::error),
|
send_screenshots.map(bevy::utils::error),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Event)]
|
||||||
|
pub struct ScreenshotRequestedEvent;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Screenshot {
|
pub struct Screenshot {
|
||||||
@@ -74,21 +85,46 @@ impl AsJsValue for Screenshot {
|
|||||||
|
|
||||||
#[wasm_bindgen(js_name = requestScreenshot)]
|
#[wasm_bindgen(js_name = requestScreenshot)]
|
||||||
pub fn request_screenshot() {
|
pub fn request_screenshot() {
|
||||||
|
info!("[wasm::screenshot] Screenshot request received");
|
||||||
SCREENSHOT_REQUEST.write_message(());
|
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(
|
fn take_screenshots(
|
||||||
mut r_screenshot_mgr: ResMut<ScreenshotManager>,
|
mut r_screenshot_mgr: ResMut<ScreenshotManager>,
|
||||||
q_window: Query<Entity, With<PrimaryWindow>>,
|
q_window: Query<Entity, With<PrimaryWindow>>,
|
||||||
|
mut er: EventReader<ScreenshotRequestedEvent>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if SCREENSHOT_REQUEST.take_message().is_none() {
|
if er.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(window_ent) = q_window.get_single() else {
|
let Ok(window_ent) = q_window.get_single() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
er.clear();
|
||||||
|
|
||||||
|
info!("[wasm::screenshot] Queueing screenshot request");
|
||||||
|
|
||||||
r_screenshot_mgr
|
r_screenshot_mgr
|
||||||
.take_screenshot(window_ent, |img| {
|
.take_screenshot(window_ent, |img| {
|
||||||
let img = match img.try_into_dynamic().map(|img| img.to_rgb8()) {
|
let img = match img.try_into_dynamic().map(|img| img.to_rgb8()) {
|
||||||
@@ -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 {
|
let Some(screenshot) = SCREENSHOT.take_message() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
@@ -157,5 +196,16 @@ fn send_screenshots(q_window: Query<&Window, With<PrimaryWindow>>) -> Result<(),
|
|||||||
|
|
||||||
let screenshot = Screenshot::from(screenshot);
|
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)]
|
#[derive(Component)]
|
||||||
struct WorldGridPlane;
|
pub struct WorldGridPlane;
|
||||||
|
|
||||||
#[derive(Asset, AsBindGroup, TypePath, Clone)]
|
#[derive(Asset, AsBindGroup, TypePath, Clone)]
|
||||||
struct ProceduralGridMaterial {}
|
struct ProceduralGridMaterial {}
|
||||||
|
|||||||
Reference in New Issue
Block a user