From 7b5bdc915b6a9d8a4dc0ec2bf47a8b5d92f0351e Mon Sep 17 00:00:00 2001 From: wil Date: Wed, 22 May 2024 11:39:39 -0400 Subject: [PATCH] Pass Dom References to Editor instead of Editor querying for DOM-IDs (#412) - engine canvas is passed to editor as a DOM reference - camera view canvas is passed to editor as a DOM reference - scene container is passed to editor as a DOM reference - amended all the editor code that wants to do a DOM query using DOM ID to use the variable that stores the reference - added guard functions to query the DOM-ID just incase the reference is lost due to a re-render - the above change guarantees the reference to the DOM, fixes the bug where the editor cannot read the size of DOM elements, and increases stability of the app in general. Co-authored-by: wil --- app/pages/PageEnigma/Editor/editor.ts | 85 +++++++++++++------ .../PageEnigma/Editor/video_generation.ts | 19 +++-- app/pages/PageEnigma/PageEditor.tsx | 19 ++--- app/pages/PageEnigma/comps/EngineCanvases.tsx | 44 ++++++++++ .../comps/PreviewEngineCamera/index.tsx | 3 +- .../PageEnigma/comps/PreviewFrameImage.tsx | 2 +- app/pages/PageEnigma/comps/SceneContainer.tsx | 40 +++++++++ .../contexts/EngineContext/EngineContext.tsx | 14 ++- .../contexts/EngineContext/EngineProvider.tsx | 35 ++++++-- 9 files changed, 207 insertions(+), 54 deletions(-) create mode 100644 app/pages/PageEnigma/comps/EngineCanvases.tsx create mode 100644 app/pages/PageEnigma/comps/SceneContainer.tsx diff --git a/app/pages/PageEnigma/Editor/editor.ts b/app/pages/PageEnigma/Editor/editor.ts index 7fdfce0084..5c37fad2b9 100644 --- a/app/pages/PageEnigma/Editor/editor.ts +++ b/app/pages/PageEnigma/Editor/editor.ts @@ -39,8 +39,12 @@ import { import { updateObjectPanel } from "../signals"; import { GenerationOptions } from "../models/generationOptions"; import { toEngineActions } from "../Queue/toEngineActions"; + export type EditorInitializeConfig = { sceneToken: string; + editorCanvasEl: HTMLCanvasElement; + camViewCanvasEl: HTMLCanvasElement; + sceneContainerEl: HTMLDivElement; }; class Editor { @@ -51,8 +55,9 @@ class Editor { renderer: THREE.WebGLRenderer | undefined; rawRenderer: THREE.WebGLRenderer | undefined; clock: THREE.Clock | undefined; - canvReference: HTMLCanvasElement | undefined = undefined; - canvasRenderCamReference: HTMLElement | undefined = undefined; + canvReference: HTMLCanvasElement | null = null; + canvasRenderCamReference: HTMLCanvasElement | null = null; + composer: EffectComposer | undefined; outlinePass: OutlinePass | undefined; last_cam_pos: THREE.Vector3; @@ -229,7 +234,43 @@ class Editor { return value === null || value.trim().length === 0; } - initialize({ sceneToken }: EditorInitializeConfig) { + containerMayReset() { + if (!this.container) { + console.warn( + "Editor - Container does not exist, querying from DOM via document.getElementById", + ); + this.container = document.getElementById("video-scene-container"); + } + } + engineCanvasMayReset() { + if (!this.canvReference) { + console.warn( + "Editor - Engine Canbas does not exist, querying from DOM via document.getElementById", + ); + this.canvReference = document.getElementById( + "video-scene", + ) as HTMLCanvasElement; + } + } + camViewCanvasMayReset() { + if (!this.canvasRenderCamReference) { + console.warn( + "Editor - Cam View Canvas does not exist, querying from DOM via document.getElementById", + ); + this.canvasRenderCamReference = document.getElementById( + "camera-view", + ) as HTMLCanvasElement; + } + } + updateCamViewCanvas(newCanvas: HTMLCanvasElement) { + this.canvasRenderCamReference = newCanvas; + } + initialize({ + sceneToken, + editorCanvasEl, + camViewCanvasEl, + sceneContainerEl, + }: EditorInitializeConfig) { if (!this.can_initialize) { console.log("Editor Already Initialized"); return; @@ -237,21 +278,15 @@ class Editor { this.can_initialize = false; // Gets the canvas. - this.canvReference = (document.getElementById("video-scene") ?? - undefined) as HTMLCanvasElement | undefined; - this.canvasRenderCamReference = - document.getElementById("camera-view") ?? undefined; + this.canvReference = editorCanvasEl; + this.canvasRenderCamReference = camViewCanvasEl; // Find the container element - const container = document.getElementById("video-scene-container"); - - if (container == null) { - return; - } + this.container = sceneContainerEl; // Use the container's dimensions - const width = container.offsetWidth; - const height = container.offsetHeight; + const width = this.container.offsetWidth; + const height = this.container.offsetHeight; // Sets up camera and base position. this.camera = new THREE.PerspectiveCamera(70, width / height, 0.01, 200); @@ -674,9 +709,8 @@ class Editor { 1000 / (this.cap_fps * 2), ); // Get the most FPS we can out of the renderer. - if (this.container === undefined) { - this.container = document.getElementById("video-scene-container"); - } + this.containerMayReset(); + if (!this.rendering && this.container) { if ( this.container.clientWidth + this.container.clientHeight !== @@ -773,11 +807,14 @@ class Editor { this.switchPreviewToggle = false; editorState.value = EditorStates.EDIT; setTimeout(() => { - this.canvasRenderCamReference = - document.getElementById("camera-view") ?? undefined; + // if (!this.canvasRenderCamReference) { + // this.canvasRenderCamReference = + // document.getElementById("camera-view"); + // } + this.camViewCanvasMayReset(); this.rawRenderer = new THREE.WebGLRenderer({ antialias: false, - canvas: this.canvasRenderCamReference, + canvas: this.canvasRenderCamReference || undefined, preserveDrawingBuffer: true, }); if (this.camera_person_mode) { @@ -889,7 +926,7 @@ class Editor { // Automaticly resize scene. onWindowResize() { - this.container = document.getElementById("video-scene-container"); + this.containerMayReset(); if (!this.container) return; const width = this.container.clientWidth; @@ -918,8 +955,8 @@ class Editor { } setupResizeObserver() { - const container = document.getElementById("video-scene-container"); - if (!container) { + this.containerMayReset(); + if (!this.container) { return; } @@ -935,7 +972,7 @@ class Editor { } }); - resizeObserver.observe(container); + resizeObserver.observe(this.container); } getAssetType(selected: THREE.Object3D): AssetType { diff --git a/app/pages/PageEnigma/Editor/video_generation.ts b/app/pages/PageEnigma/Editor/video_generation.ts index ea1fa33f54..01f31e56e2 100644 --- a/app/pages/PageEnigma/Editor/video_generation.ts +++ b/app/pages/PageEnigma/Editor/video_generation.ts @@ -1,5 +1,5 @@ import Editor from "./editor"; -import { EditorStates } from "~/pages/PageEnigma/enums"; +import { EditorStates, ClipType } from "~/pages/PageEnigma/enums"; import { editorState, previewSrc } from "../signals/engine"; import Queue from "~/pages/PageEnigma/Queue/Queue"; import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames"; @@ -7,7 +7,6 @@ import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions"; import { ToastTypes } from "~/enums"; import { createFFmpeg, fetchFile, FFmpeg } from "@ffmpeg/ffmpeg"; import { ClipUI } from "../datastructures/clips/clip_ui.js"; -import { ClipType } from "~/pages/PageEnigma/enums"; import { Visibility } from "./api_manager.js"; import * as THREE from "three"; import { getSceneSignals } from "~/signals"; @@ -70,13 +69,17 @@ export class VideoGeneration { previewSrc.value = url; return Promise.resolve(url); - } catch (err: any) { + } catch (err: unknown) { + const errorMessage = + err instanceof Error + ? err.message + : "Unknown Error in Generate Frame"; Queue.publish({ queueName: QueueNames.FROM_ENGINE, action: fromEngineActions.POP_A_TOAST, data: { type: ToastTypes.ERROR, - message: err.message, + message: errorMessage, }, }); } @@ -259,7 +262,7 @@ export class VideoGeneration { } const upload_token = data["media_file_token"]; - const result = await this.editor.api_manager + await this.editor.api_manager .stylizeVideo( upload_token, this.editor.art_style, @@ -287,11 +290,11 @@ export class VideoGeneration { ); } - this.editor.canvasRenderCamReference = - document.getElementById("camera-view"); + this.editor.camViewCanvasMayReset(); + this.editor.rawRenderer = new THREE.WebGLRenderer({ antialias: false, - canvas: this.editor.canvasRenderCamReference, + canvas: this.editor.canvasRenderCamReference || undefined, preserveDrawingBuffer: true, }); this.editor.activeScene.renderMode(false); diff --git a/app/pages/PageEnigma/PageEditor.tsx b/app/pages/PageEnigma/PageEditor.tsx index d23cb454f0..6ebb4b6071 100644 --- a/app/pages/PageEnigma/PageEditor.tsx +++ b/app/pages/PageEnigma/PageEditor.tsx @@ -23,6 +23,9 @@ import { editorLoader, } from "~/pages/PageEnigma/signals"; +import { EditorCanvas } from "./comps/EngineCanvases"; +import { SceneContainer } from "./comps/SceneContainer"; + export const PageEditor = () => { useSignals(); @@ -68,20 +71,10 @@ export const PageEditor = () => { }} >
-
- + + -
+ {/* Top controls */}
{ + const editorEngine = useContext(EngineContext); + + const canvasCallbackRef = useCallback( + (node: HTMLCanvasElement) => { + if (node && editorEngine && editorEngine.setEditorCanvas) { + editorEngine.setEditorCanvas(node); + } + }, + [editorEngine], + ); + + return ( + + ); +}; + +export const CameraViewCanvas = () => { + const editorEngine = useContext(EngineContext); + + const canvasCallbackRef = useCallback( + (node: HTMLCanvasElement) => { + if (node && editorEngine) { + if (!editorEngine.canvasRenderCamReference) { + editorEngine.setCamViewCanvas(node); + } else { + editorEngine.updateCamViewCanvas(node); + } + } + //else just do nothing + }, + [editorEngine], + ); + + return ; +}; diff --git a/app/pages/PageEnigma/comps/PreviewEngineCamera/index.tsx b/app/pages/PageEnigma/comps/PreviewEngineCamera/index.tsx index 6a7c5f2a8a..39ceed8bd9 100644 --- a/app/pages/PageEnigma/comps/PreviewEngineCamera/index.tsx +++ b/app/pages/PageEnigma/comps/PreviewEngineCamera/index.tsx @@ -9,6 +9,7 @@ import { editorState } from "~/pages/PageEnigma/signals/engine"; import { useSignals } from "@preact/signals-react/runtime"; import { sidePanelHeight } from "../../signals"; import { EditorStates } from "~/pages/PageEnigma/enums"; +import { CameraViewCanvas } from "../EngineCanvases"; export const PreviewEngineCamera = () => { useSignals(); @@ -60,7 +61,7 @@ export const PreviewEngineCamera = () => {
- +
diff --git a/app/pages/PageEnigma/comps/PreviewFrameImage.tsx b/app/pages/PageEnigma/comps/PreviewFrameImage.tsx index 335561c0f2..5c50944c2b 100644 --- a/app/pages/PageEnigma/comps/PreviewFrameImage.tsx +++ b/app/pages/PageEnigma/comps/PreviewFrameImage.tsx @@ -40,9 +40,9 @@ export const PreviewFrameImage = () => { } else { return ( preview of the art style that renders over the 3d scene { + useSignals(); + const editorEngine = useContext(EngineContext); + + const callbackRef = useCallback( + (node: HTMLDivElement) => { + if (node && editorEngine && editorEngine.setSceneContainer) { + editorEngine.setSceneContainer(node); + } + }, + [editorEngine], + ); + + return ( +
+ {children} +
+ ); +}; diff --git a/app/pages/PageEnigma/contexts/EngineContext/EngineContext.tsx b/app/pages/PageEnigma/contexts/EngineContext/EngineContext.tsx index e15c567e1c..c0f0c2a3c7 100644 --- a/app/pages/PageEnigma/contexts/EngineContext/EngineContext.tsx +++ b/app/pages/PageEnigma/contexts/EngineContext/EngineContext.tsx @@ -1,4 +1,16 @@ import { createContext } from "react"; import Editor from "~/pages/PageEnigma/Editor/editor"; -export const EngineContext = createContext(null); +export interface EditorExpandedI extends Editor { + setSceneContainer: React.Dispatch< + React.SetStateAction + >; + setEditorCanvas: React.Dispatch< + React.SetStateAction + >; + setCamViewCanvas: React.Dispatch< + React.SetStateAction + >; +} + +export const EngineContext = createContext(null); diff --git a/app/pages/PageEnigma/contexts/EngineContext/EngineProvider.tsx b/app/pages/PageEnigma/contexts/EngineContext/EngineProvider.tsx index 55ce6a5f14..50b435ad92 100644 --- a/app/pages/PageEnigma/contexts/EngineContext/EngineProvider.tsx +++ b/app/pages/PageEnigma/contexts/EngineContext/EngineProvider.tsx @@ -1,5 +1,5 @@ import { ReactNode, useEffect, useState } from "react"; -import { EngineContext } from "./EngineContext"; +import { EngineContext, EditorExpandedI } from "./EngineContext"; import Editor from "~/pages/PageEnigma/Editor/editor"; @@ -9,25 +9,48 @@ interface Props { } export const EngineProvider = ({ sceneToken, children }: Props) => { - const [editor, setEditor] = useState(null); - + const [editor, setEditor] = useState(null); + const [sceneContainer, setSceneContainer] = useState( + null, + ); + const [editorCanvas, setEditorCanvas] = useState( + null, + ); + const [camViewCanvas, setCamViewCanvas] = useState( + null, + ); useEffect(() => { setEditor((curr) => { if (curr !== null) { console.warn("Editor Engine already exist"); return curr; } - return new Editor(); + + class EditorExpanded extends Editor implements EditorExpandedI { + setSceneContainer = setSceneContainer; + setEditorCanvas = setEditorCanvas; + setCamViewCanvas = setCamViewCanvas; + } + return new EditorExpanded(); }); }, []); useEffect(() => { - if (editor && editor.can_initialize) { + if ( + editor && + editor.can_initialize && + editorCanvas && + camViewCanvas && + sceneContainer + ) { editor.initialize({ sceneToken: sceneToken || "", + sceneContainerEl: sceneContainer, + editorCanvasEl: editorCanvas, + camViewCanvasEl: camViewCanvas, }); } - }, [editor, sceneToken]); + }, [editor, sceneToken, editorCanvas, camViewCanvas, sceneContainer]); return ( {children}