Merge branch 'main' into add-persistance

This commit is contained in:
rewin
2024-01-18 21:59:36 +03:00
6 changed files with 148 additions and 31 deletions
+1
View File
@@ -26,6 +26,7 @@ node_modules
# misc # misc
/.sass-cache /.sass-cache
/.nx
/connect.lock /connect.lock
/coverage /coverage
/libpeerconnection.log /libpeerconnection.log
+29 -7
View File
@@ -9,6 +9,7 @@ import {
TransformSpace, TransformSpace,
TransformType, TransformType,
SceneState, SceneState,
StudioMode,
} from "@storyteller/studio"; } from "@storyteller/studio";
import { CanvasProvider } from "@storyteller/studio-web/canvas-provider"; import { CanvasProvider } from "@storyteller/studio-web/canvas-provider";
import { match } from "@storyteller/utility"; import { match } from "@storyteller/utility";
@@ -43,6 +44,8 @@ export class AppElement extends LitElement {
@state() sceneObjects: SceneObject[] = []; @state() sceneObjects: SceneObject[] = [];
@state() selection?: string | null; @state() selection?: string | null;
@state() mode?: StudioMode;
#sceneObjects: studio.SceneObject[] = []; #sceneObjects: studio.SceneObject[] = [];
#entityMap = new Map<string, SceneObject>(); #entityMap = new Map<string, SceneObject>();
#canvasProvider: Ref<CanvasProvider> = createRef(); #canvasProvider: Ref<CanvasProvider> = createRef();
@@ -54,6 +57,18 @@ export class AppElement extends LitElement {
return document.getElementById(provider.canvasId) as HTMLCanvasElement | null; return document.getElementById(provider.canvasId) as HTMLCanvasElement | null;
} }
override connectedCallback(): void {
super.connectedCallback();
const params = new URLSearchParams(document.location.search);
this.mode = match (params.get("mode"), {
"studio": () => StudioMode.Editor,
"viewer": () => StudioMode.Viewer,
_: () => StudioMode.Viewer,
});
}
protected override createRenderRoot(): Element | ShadowRoot { protected override createRenderRoot(): Element | ShadowRoot {
return this; return this;
} }
@@ -169,9 +184,14 @@ export class AppElement extends LitElement {
class="app-root" class="app-root"
${ref(this.#canvasProvider as any)} ${ref(this.#canvasProvider as any)}
> >
${
// TODO: See notes in studio-web/src/lib/viewer.element.ts
nothing
}
<sts-viewer <sts-viewer
objectId="sample-room" objectId="sample-room"
skyboxId="test_scene" skyboxId="test_scene"
.mode=${this.mode}
@scene-state=${this.onSceneStateChange} @scene-state=${this.onSceneStateChange}
></sts-viewer> ></sts-viewer>
@@ -187,13 +207,15 @@ export class AppElement extends LitElement {
</sts-toolbar-group> </sts-toolbar-group>
</sts-toolbar> </sts-toolbar>
<sts-transform-toolbar ${this.mode === StudioMode.Editor ? html`
class="v-start h-center" <sts-transform-toolbar
.type=${this.transformType} class="v-start h-center"
@type-change=${this.onTransformTypeChange} .type=${this.transformType}
.space=${this.transformSpace} @type-change=${this.onTransformTypeChange}
@space-change=${this.onTransformSpaceChange} .space=${this.transformSpace}
></sts-transform-toolbar> @space-change=${this.onTransformSpaceChange}
></sts-transform-toolbar>
` : nothing}
<div class="hierarchy v-start h-end"> <div class="hierarchy v-start h-end">
${this.sceneObjects.map(obj => html` ${this.sceneObjects.map(obj => html`
+14 -2
View File
@@ -1,5 +1,5 @@
import * as studio from "@storyteller/studio"; import * as studio from "@storyteller/studio";
import { ViewportSize } from "@storyteller/studio"; import { StudioMode, ViewportSize } from "@storyteller/studio";
import { inject } from "@storyteller/framework"; import { inject } from "@storyteller/framework";
import { LitElement, PropertyValues, html, unsafeCSS } from "lit"; import { LitElement, PropertyValues, html, unsafeCSS } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
@@ -12,8 +12,18 @@ import styles from "./viewer.element.scss?inline";
export class ViewerElement extends LitElement { export class ViewerElement extends LitElement {
static override styles = unsafeCSS(styles); static override styles = unsafeCSS(styles);
// TODO: `<sts-viewer />` and `<sts-studio />` should be separate element
// types with different inputs. `<sts-viewer />` should take a BVH
// animation and load a simple mannequin asset playing the given
// animation in a loop, while `<sts-studio />` should take a scene
// asset handle and load it with full editing capabilities.
//
// Both elements should share most of the core functionality here,
// which could easily be extracted to a base class for inheritance by
// making `#startViewer` an abstract method.
@property() objectId?: string; @property() objectId?: string;
@property() skyboxId?: string; @property() skyboxId?: string;
@property({ type: Number }) mode?: StudioMode;
@inject(CANVAS_PROVIDER) @inject(CANVAS_PROVIDER)
_canvasProvider?: CanvasProvider; _canvasProvider?: CanvasProvider;
@@ -68,15 +78,17 @@ export class ViewerElement extends LitElement {
if (!this.#canvas) return; if (!this.#canvas) return;
if (!this.objectId) return; if (!this.objectId) return;
if (!this.skyboxId) return; if (!this.skyboxId) return;
if (this.mode == null) return;
const { width, height } = this.getBoundingClientRect(); const { width, height } = this.getBoundingClientRect();
try { try {
studio.startViewer( studio.start(
this.objectId, this.objectId,
this.skyboxId, this.skyboxId,
`#${this._canvasProvider!.canvasId}`, `#${this._canvasProvider!.canvasId}`,
"Storyteller Studio", "Storyteller Studio",
new ViewportSize(width, height), new ViewportSize(width, height),
this.mode,
); );
} catch (err) { } catch (err) {
if (!(err as Error).message?.startsWith("Using exceptions for control flow")) if (!(err as Error).message?.startsWith("Using exceptions for control flow"))
+4 -2
View File
@@ -8,7 +8,7 @@ use space_editor::{space_editor_ui::{EditorPluginGroup, EditorPickingPlugin, Edi
use crate::{ use crate::{
camera_controller::{CameraController, CameraMotionSystem}, camera_controller::{CameraController, CameraMotionSystem},
input::{Action, InputAction, Lifecycle}, input::{Action, InputAction, Lifecycle},
MainCamera, interaction::gizmos::camera::GizmoCamera, is_editor, MainCamera,
}; };
#[derive(Resource, Deref, DerefMut)] #[derive(Resource, Deref, DerefMut)]
@@ -36,7 +36,9 @@ impl Plugin for DebugPlugin {
Update, Update,
( (
toggle_debug_mode, toggle_debug_mode,
update_debug_picking_mode.run_if(resource_changed::<DebugMode>()), update_debug_picking_mode
.run_if(is_editor)
.run_if(resource_changed::<DebugMode>()),
update_gizmo_line_thickness, update_gizmo_line_thickness,
(draw_world_axes, draw_debug_points) (draw_world_axes, draw_debug_points)
.after(CameraMotionSystem) .after(CameraMotionSystem)
+3 -1
View File
@@ -5,6 +5,8 @@ use bevy::{
}; };
use space_editor::prelude::space_undo::UndoRedo; use space_editor::prelude::space_undo::UndoRedo;
use crate::is_editor;
#[derive(SystemSet, Hash, Debug, PartialEq, Eq, Clone)] #[derive(SystemSet, Hash, Debug, PartialEq, Eq, Clone)]
pub struct StudioInputSystem; pub struct StudioInputSystem;
@@ -31,7 +33,7 @@ impl Plugin for StudioInputPlugin {
translate_debug_toggle, translate_debug_toggle,
translate_dcc_camera_events, translate_dcc_camera_events,
translate_fps_camera_events, translate_fps_camera_events,
send_undo_event, send_undo_event.run_if(is_editor),
) )
.in_set(StudioInputSystem) .in_set(StudioInputSystem)
.after(bevy::input::InputSystem) .after(bevy::input::InputSystem)
+96 -18
View File
@@ -1,6 +1,6 @@
#![allow(clippy::type_complexity, clippy::too_many_arguments)] #![allow(clippy::type_complexity, clippy::too_many_arguments)]
use bevy::prelude::*; use bevy::{app::PluginGroupBuilder, prelude::*};
use bevy_mod_picking::DefaultPickingPlugins; use bevy_mod_picking::DefaultPickingPlugins;
use space_editor::{ use space_editor::{
@@ -44,6 +44,46 @@ mod wasm;
#[derive(Component)] #[derive(Component)]
pub(crate) struct MainCamera; pub(crate) struct MainCamera;
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Resource, Clone, Copy, PartialEq, Eq, Debug)]
pub enum StudioMode {
Editor,
Viewer,
}
pub(crate) fn is_editor(r_studio_mode: Res<StudioMode>) -> bool {
matches!(*r_studio_mode, StudioMode::Editor)
}
/// The collection of plugins that should be loaded for both viewer-mode and
/// editor-mode instances of the app.
struct CommonPlugins;
impl PluginGroup for CommonPlugins {
fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>()
.add(CameraControllerPlugin)
.add(DebugPlugin)
.add(InspectorPlugin)
.add(InterpolationPlugin)
.add(ScenePlugin)
.add(SkyboxPlugin)
.add(StudioInputPlugin)
.add(UiLayerPlugin)
.add(WorldGridPlugin)
}
}
/// The collection of plugins that should be loaded only for editor-mode
/// instances of the app.
struct EditorPlugins;
impl PluginGroup for EditorPlugins {
fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>()
.add(InteractionPlugin)
.add(UndoPlugin)
}
}
#[cfg_attr(feature = "wasm", wasm_bindgen)] #[cfg_attr(feature = "wasm", wasm_bindgen)]
pub fn init(viewport_size: ViewportSize) { pub fn init(viewport_size: ViewportSize) {
#[cfg(feature = "wasm")] #[cfg(feature = "wasm")]
@@ -119,6 +159,46 @@ pub fn start_viewer(
canvas_selector: &str, canvas_selector: &str,
canvas_alt: &str, canvas_alt: &str,
viewport_size: ViewportSize, viewport_size: ViewportSize,
) {
start_internal(
object_id,
skybox_id,
canvas_selector,
canvas_alt,
viewport_size,
StudioMode::Viewer,
);
}
#[cfg_attr(feature = "wasm", wasm_bindgen(js_name = startEditor))]
pub fn start_editor(
object_id: &str,
skybox_id: &str,
canvas_selector: &str,
canvas_alt: &str,
viewport_size: ViewportSize,
) {
start_internal(
object_id,
skybox_id,
canvas_selector,
canvas_alt,
viewport_size,
StudioMode::Editor,
);
}
// TODO: These `start_` functiions should be refactored to better support the
// needs of the "reaodnly viewer" vs "full studio editor" use cases. See
// notes in `studio-web/src/lib/viewer.element.ts` for more info.
#[cfg_attr(feature = "wasm", wasm_bindgen(js_name = start))]
pub fn start_internal(
object_id: &str,
skybox_id: &str,
canvas_selector: &str,
canvas_alt: &str,
viewport_size: ViewportSize,
studio_mode: StudioMode,
) { ) {
#[cfg(feature = "wasm")] #[cfg(feature = "wasm")]
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
@@ -127,6 +207,9 @@ pub fn start_viewer(
let skybox_id = skybox_id.to_string(); let skybox_id = skybox_id.to_string();
let mut app = App::new(); let mut app = App::new();
app.insert_resource(studio_mode);
app.add_plugins(( app.add_plugins((
#[cfg(feature = "wasm")] #[cfg(feature = "wasm")]
viewport::wasm::ViewportPlugin, viewport::wasm::ViewportPlugin,
@@ -139,19 +222,18 @@ pub fn start_viewer(
}), }),
..default() ..default()
}), }),
DefaultPickingPlugins, CommonPlugins,
DebugPlugin, ));
InteractionPlugin,
InspectorPlugin, if matches!(studio_mode, StudioMode::Editor) {
StudioInputPlugin, app.add_plugins((DefaultPickingPlugins, EditorPlugins));
InterpolationPlugin,
CameraControllerPlugin, // Only for registering components for undo
ScenePlugin, use space_editor::prelude::space_undo::AppAutoUndo;
SkyboxPlugin, app.auto_reflected_undo::<Transform>();
UiLayerPlugin, }
WorldGridPlugin,
)) app.add_systems(
.add_systems(
Startup, Startup,
move |mut cmd: Commands, mut ew: EventWriter<LoadSceneEvent>| { move |mut cmd: Commands, mut ew: EventWriter<LoadSceneEvent>| {
cmd.insert_resource(SkyboxId(skybox_id.clone())); cmd.insert_resource(SkyboxId(skybox_id.clone()));
@@ -159,10 +241,6 @@ pub fn start_viewer(
}, },
); );
// Only for registering components for undo
use space_editor::prelude::space_undo::AppAutoUndo;
app.auto_reflected_undo::<Transform>();
#[cfg(feature = "wasm")] #[cfg(feature = "wasm")]
wasm::initialize(); wasm::initialize();