Debugging window/viewport scaling issues

This commit is contained in:
Danny McGee
2024-04-04 15:47:16 -04:00
parent 691ff92d17
commit 1efa6a95a1
4 changed files with 79 additions and 42 deletions
+2 -1
View File
@@ -64,7 +64,7 @@ export class StudioElement extends LitElement {
#onResize(): void { #onResize(): void {
const { width, height } = this.getBoundingClientRect(); const { width, height } = this.getBoundingClientRect();
studio.resize(new ViewportSize(width, height)); studio.resize(new ViewportSize(width, height), window.devicePixelRatio);
this.dispatchEvent(new CustomEvent("viewport-resized", { this.dispatchEvent(new CustomEvent("viewport-resized", {
detail: { width, height }, detail: { width, height },
@@ -83,6 +83,7 @@ export class StudioElement extends LitElement {
canvasSelector: `#${this._canvasProvider?.canvasId}`, canvasSelector: `#${this._canvasProvider?.canvasId}`,
canvasAlt: "Storyteller Studio", canvasAlt: "Storyteller Studio",
viewportSize: new ViewportSize(width, height), viewportSize: new ViewportSize(width, height),
scaleFactor: window.devicePixelRatio,
}; };
try { try {
+21 -2
View File
@@ -10,9 +10,13 @@ use bevy::{
math::vec2, math::vec2,
pbr::ShadowFilteringMethod, pbr::ShadowFilteringMethod,
prelude::*, prelude::*,
render::{camera::RenderTarget, primitives::Aabb}, render::{
camera::{RenderTarget, Viewport},
primitives::Aabb,
},
scene::InstanceId, scene::InstanceId,
utils::HashSet, utils::HashSet,
window::PrimaryWindow,
}; };
#[cfg(feature = "wasm")] #[cfg(feature = "wasm")]
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
@@ -310,11 +314,19 @@ pub struct SceneData {
#[derive(Component)] #[derive(Component)]
pub struct SceneRoot; pub struct SceneRoot;
fn initialize(mut cmd: Commands) { fn initialize(mut cmd: Commands, q_window: Query<&Window, With<PrimaryWindow>>) {
// 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);
let window = q_window.single();
info!(
"Window: ({:.2} x {:.2}) [{:.2}]",
window.width(),
window.height(),
window.scale_factor(),
);
cmd.spawn(( cmd.spawn((
Name::new("Main Camera"), Name::new("Main Camera"),
MainCamera, MainCamera,
@@ -325,6 +337,13 @@ fn initialize(mut cmd: Commands) {
tonemapping: Tonemapping::BlenderFilmic, tonemapping: Tonemapping::BlenderFilmic,
camera: Camera { camera: Camera {
hdr: true, hdr: true,
viewport: Some(Viewport {
physical_size: UVec2::new(
(window.width() * window.scale_factor()) as u32,
(window.height() * window.scale_factor()) as u32,
),
..default()
}),
..default() ..default()
}, },
..default() ..default()
+22 -28
View File
@@ -1,6 +1,5 @@
use core::fmt; use core::fmt;
use bevy::window::WindowResolution;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@@ -21,21 +20,9 @@ impl ViewportSize {
impl Default for ViewportSize { impl Default for ViewportSize {
fn default() -> Self { fn default() -> Self {
WindowResolution::default().into()
}
}
impl From<ViewportSize> for WindowResolution {
fn from(value: ViewportSize) -> Self {
Self::new(value.width, value.height)
}
}
impl From<WindowResolution> for ViewportSize {
fn from(value: WindowResolution) -> Self {
Self { Self {
width: value.width(), width: 1280.,
height: value.height(), height: 720.,
} }
} }
} }
@@ -58,11 +45,11 @@ pub mod wasm {
use super::ViewportSize; use super::ViewportSize;
static RESIZE_BUF: WasmMessageBuffer<ViewportSize> = WasmMessageBuffer::new(); static RESIZE_BUF: WasmMessageBuffer<(ViewportSize, f32)> = WasmMessageBuffer::new();
#[wasm_bindgen] #[wasm_bindgen]
pub fn resize(viewport_size: ViewportSize) { pub fn resize(viewport_size: ViewportSize, scale_factor: f32) {
RESIZE_BUF.write_message(viewport_size); RESIZE_BUF.write_message((viewport_size, scale_factor));
} }
pub struct ViewportPlugin; pub struct ViewportPlugin;
@@ -77,30 +64,37 @@ pub mod wasm {
} }
#[derive(Event, Clone, Copy)] #[derive(Event, Clone, Copy)]
struct ViewportResizeEvent(ViewportSize); struct ViewportResizeEvent {
pub size: ViewportSize,
pub scale_factor: f32,
}
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>>, mut q_camera: Query<&mut Camera, With<MainCamera>>,
) { ) {
if let Some(event) = events.read().last() { if let Some(ViewportResizeEvent { size, scale_factor }) = events.read().last() {
let ViewportSize { width, height } = event.0; let ViewportSize { width, height } = size;
window.single_mut().resolution.set(width, height);
window.single_mut().resolution.set(*width, *height);
q_camera.single_mut().viewport = Some(Viewport { q_camera.single_mut().viewport = Some(Viewport {
physical_size: UVec2::new(width as u32, height as u32), physical_size: UVec2::new(
(width * scale_factor) as u32,
(height * scale_factor) as u32,
),
..Default::default() ..Default::default()
}); });
info!("Viewport size: ({:.2}, {:.2})", width, height); info!("Viewport size: ({width:.2}, {height:.2}) [{scale_factor:.2}]");
} }
} }
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((size, scale_factor)) = RESIZE_BUF.take_message() {
events.send(ViewportResizeEvent(resize)); events.send(ViewportResizeEvent { size, scale_factor });
info!( info!(
"Dispatch Viewport size: ({:.2}, {:.2})", "Dispatch Viewport size: ({:.2}, {:.2}) [{scale_factor:.2}]",
resize.width, resize.height size.width, size.height,
); );
} }
} }
+34 -11
View File
@@ -1,4 +1,4 @@
use bevy::prelude::*; use bevy::{prelude::*, window::WindowResolution};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@@ -13,6 +13,7 @@ export interface InitOptions {
canvasSelector: string; canvasSelector: string;
canvasAlt: string; canvasAlt: string;
viewportSize: ViewportSize; viewportSize: ViewportSize;
scaleFactor: number;
} }
export function initFromLibrary(objectId: string, options: InitOptions): void; export function initFromLibrary(objectId: string, options: InitOptions): void;
@@ -22,26 +23,28 @@ export function initFromSceneImport(scenePath: string, options: InitOptions): vo
export function initFromStorytellerScene(scenePath: string, options: InitOptions): void; export function initFromStorytellerScene(scenePath: string, options: InitOptions): void;
"#; "#;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct WasmInitOptions { struct WasmInitOptions {
#[serde(rename = "skyboxId")]
skybox_id: Option<String>, skybox_id: Option<String>,
mode: StudioMode, mode: StudioMode,
#[serde(rename = "canvasSelector")]
canvas_selector: String, canvas_selector: String,
#[serde(rename = "canvasAlt")]
canvas_alt: String, canvas_alt: String,
#[serde(rename = "viewportSize")]
viewport_size: ViewportSize, viewport_size: ViewportSize,
scale_factor: f32,
} }
#[wasm_bindgen(skip_typescript, js_name = initFromLibrary)] #[wasm_bindgen(skip_typescript, js_name = initFromLibrary)]
pub fn init_from_library(object_id: String, options: JsValue) -> Result<(), JsValue> { pub fn init_from_library(object_id: String, options: JsValue) -> Result<(), JsValue> {
let options = serde_wasm_bindgen::from_value::<WasmInitOptions>(options)?; let options = serde_wasm_bindgen::from_value::<WasmInitOptions>(options)?;
info!("wasm::init_from_library: {object_id} {options:#?}");
let skybox_id = options let skybox_id = options
.skybox_id .skybox_id
.expect("`skyboxId` is required for `initFromLibrary`"); .expect("`skyboxId` is required for `initFromLibrary`");
let ViewportSize { width, height } = options.viewport_size;
crate::start( crate::start(
InitParams::FromAsset { InitParams::FromAsset {
asset: InitAsset::LibraryObject(object_id), asset: InitAsset::LibraryObject(object_id),
@@ -49,7 +52,8 @@ pub fn init_from_library(object_id: String, options: JsValue) -> Result<(), JsVa
}, },
options.mode, options.mode,
Some(Window { Some(Window {
resolution: options.viewport_size.into(), resolution: WindowResolution::new(width, height)
.with_scale_factor_override(options.scale_factor),
title: options.canvas_alt, title: options.canvas_alt,
canvas: Some(options.canvas_selector), canvas: Some(options.canvas_selector),
..default() ..default()
@@ -63,10 +67,14 @@ pub fn init_from_library(object_id: String, options: JsValue) -> Result<(), JsVa
#[wasm_bindgen(skip_typescript, js_name = initFromBvh)] #[wasm_bindgen(skip_typescript, js_name = initFromBvh)]
pub fn init_from_bvh(anim_path: String, options: JsValue) -> Result<(), JsValue> { pub fn init_from_bvh(anim_path: String, options: JsValue) -> Result<(), JsValue> {
let options = serde_wasm_bindgen::from_value::<WasmInitOptions>(options)?; let options = serde_wasm_bindgen::from_value::<WasmInitOptions>(options)?;
info!("wasm::init_from_bvh: {anim_path} {options:#?}");
let skybox_id = options let skybox_id = options
.skybox_id .skybox_id
.expect("`skyboxId` is required for `initFromBvh`"); .expect("`skyboxId` is required for `initFromBvh`");
let ViewportSize { width, height } = options.viewport_size;
crate::start( crate::start(
InitParams::FromAsset { InitParams::FromAsset {
asset: InitAsset::BvhAnim(anim_path), asset: InitAsset::BvhAnim(anim_path),
@@ -74,7 +82,8 @@ pub fn init_from_bvh(anim_path: String, options: JsValue) -> Result<(), JsValue>
}, },
options.mode, options.mode,
Some(Window { Some(Window {
resolution: options.viewport_size.into(), resolution: WindowResolution::new(width, height)
.with_scale_factor_override(options.scale_factor),
title: options.canvas_alt, title: options.canvas_alt,
canvas: Some(options.canvas_selector), canvas: Some(options.canvas_selector),
..default() ..default()
@@ -88,10 +97,14 @@ pub fn init_from_bvh(anim_path: String, options: JsValue) -> Result<(), JsValue>
#[wasm_bindgen(skip_typescript, js_name = initFromMixamo)] #[wasm_bindgen(skip_typescript, js_name = initFromMixamo)]
pub fn init_from_mixamo(anim_path: String, options: JsValue) -> Result<(), JsValue> { pub fn init_from_mixamo(anim_path: String, options: JsValue) -> Result<(), JsValue> {
let options = serde_wasm_bindgen::from_value::<WasmInitOptions>(options)?; let options = serde_wasm_bindgen::from_value::<WasmInitOptions>(options)?;
info!("wasm::init_from_mixamo: {anim_path} {options:#?}");
let skybox_id = options let skybox_id = options
.skybox_id .skybox_id
.expect("`skyboxId` is required for `initFromMixamo`"); .expect("`skyboxId` is required for `initFromMixamo`");
let ViewportSize { width, height } = options.viewport_size;
crate::start( crate::start(
InitParams::FromAsset { InitParams::FromAsset {
asset: InitAsset::MixamoAnim(anim_path), asset: InitAsset::MixamoAnim(anim_path),
@@ -99,7 +112,8 @@ pub fn init_from_mixamo(anim_path: String, options: JsValue) -> Result<(), JsVal
}, },
options.mode, options.mode,
Some(Window { Some(Window {
resolution: options.viewport_size.into(), resolution: WindowResolution::new(width, height)
.with_scale_factor_override(options.scale_factor),
title: options.canvas_alt, title: options.canvas_alt,
canvas: Some(options.canvas_selector), canvas: Some(options.canvas_selector),
..default() ..default()
@@ -113,10 +127,14 @@ pub fn init_from_mixamo(anim_path: String, options: JsValue) -> Result<(), JsVal
#[wasm_bindgen(skip_typescript, js_name = initFromSceneImport)] #[wasm_bindgen(skip_typescript, js_name = initFromSceneImport)]
pub fn init_from_scene_import(scene_path: String, options: JsValue) -> Result<(), JsValue> { pub fn init_from_scene_import(scene_path: String, options: JsValue) -> Result<(), JsValue> {
let options = serde_wasm_bindgen::from_value::<WasmInitOptions>(options)?; let options = serde_wasm_bindgen::from_value::<WasmInitOptions>(options)?;
info!("wasm::init_from_scene_import: {scene_path} {options:#?}");
let skybox_id = options let skybox_id = options
.skybox_id .skybox_id
.expect("`skyboxId` is required for `initFromSceneImport`"); .expect("`skyboxId` is required for `initFromSceneImport`");
let ViewportSize { width, height } = options.viewport_size;
crate::start( crate::start(
InitParams::FromAsset { InitParams::FromAsset {
asset: InitAsset::ImportedScene(scene_path), asset: InitAsset::ImportedScene(scene_path),
@@ -124,7 +142,8 @@ pub fn init_from_scene_import(scene_path: String, options: JsValue) -> Result<()
}, },
options.mode, options.mode,
Some(Window { Some(Window {
resolution: options.viewport_size.into(), resolution: WindowResolution::new(width, height)
.with_scale_factor_override(options.scale_factor),
title: options.canvas_alt, title: options.canvas_alt,
canvas: Some(options.canvas_selector), canvas: Some(options.canvas_selector),
..default() ..default()
@@ -138,12 +157,16 @@ pub fn init_from_scene_import(scene_path: String, options: JsValue) -> Result<()
#[wasm_bindgen(skip_typescript, js_name = initFromStorytellerScene)] #[wasm_bindgen(skip_typescript, js_name = initFromStorytellerScene)]
pub fn init_from_storyteller_scene(scene_path: String, options: JsValue) -> Result<(), JsValue> { pub fn init_from_storyteller_scene(scene_path: String, options: JsValue) -> Result<(), JsValue> {
let options = serde_wasm_bindgen::from_value::<WasmInitOptions>(options)?; let options = serde_wasm_bindgen::from_value::<WasmInitOptions>(options)?;
info!("wasm::init_from_storyteller_scene: {scene_path} {options:#?}");
let ViewportSize { width, height } = options.viewport_size;
crate::start( crate::start(
InitParams::FromScene(scene_path), InitParams::FromScene(scene_path),
options.mode, options.mode,
Some(Window { Some(Window {
resolution: options.viewport_size.into(), resolution: WindowResolution::new(width, height)
.with_scale_factor_override(options.scale_factor),
title: options.canvas_alt, title: options.canvas_alt,
canvas: Some(options.canvas_selector), canvas: Some(options.canvas_selector),
..default() ..default()