Exclude editor visuals from screen captures

This commit is contained in:
Danny McGee
2024-02-28 02:30:19 -05:00
parent c57f0588af
commit 04ba5bad69
5 changed files with 67 additions and 28 deletions
+9 -21
View File
@@ -22,7 +22,6 @@ import "./app.element.scss"
@customElement("app-root")
export class AppElement extends LitElement {
@state() imageCaptureUrl?: string;
@state() realtimeCapture = true;
@state() hierarchyWidth = 256;
@state() resizing = false;
@@ -52,12 +51,9 @@ export class AppElement extends LitElement {
}
@on("entity-changes")
@debounce(1000 / 24)
onStudioChanges(): void {
if (this.realtimeCapture) {
this.requestScreenshot_realtime();
} else {
this.requestScreenshot_delayed();
}
studio.requestScreenshot();
}
@on("viewport-resized")
@@ -67,16 +63,6 @@ export class AppElement extends LitElement {
studio.requestScreenshot();
}
@debounce(1000 / 24, { maxWait: 1000 / 5 })
requestScreenshot_realtime() {
studio.requestScreenshot();
}
@debounce(1000 / 24)
requestScreenshot_delayed() {
studio.requestScreenshot();
}
@on("screenshot-ready")
onScreenshotReady({ detail: screenshot }: ScreenshotReadyEvent) {
const prevUrl = this.imageCaptureUrl;
@@ -101,11 +87,6 @@ export class AppElement extends LitElement {
this.hierarchyWidth = Math.max(Math.min(updated, 512), 128);
}
toggleRealtimeCapture(event: InputEvent): void {
const { checked } = event.target as HTMLInputElement;
this.realtimeCapture = checked;
}
refreshSeed(): void {
this.seed = randomSeed();
}
@@ -184,6 +165,11 @@ export class AppElement extends LitElement {
</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">
<label class="config-checkbox">
<input
@@ -194,6 +180,8 @@ export class AppElement extends LitElement {
Realtime Capture
</label>
</div>
*/
nothing}
${this.loading ? html`
<!-- TODO: Animated spinner or something -- talk to our designers -->
+1 -1
View File
@@ -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,
}
+1
View File
@@ -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};
+55 -5
View File
@@ -13,6 +13,8 @@ use bevy::{prelude::*, render::view::screenshot::ScreenshotManager, window::Prim
use image::{codecs::png::PngEncoder, 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()) {
@@ -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(())
}
+1 -1
View File
@@ -17,7 +17,7 @@ impl Plugin for WorldGridPlugin {
}
#[derive(Component)]
struct WorldGridPlane;
pub struct WorldGridPlane;
#[derive(Asset, AsBindGroup, TypePath, Clone)]
struct ProceduralGridMaterial {}