From 8ccffff1a8ad73e4592fe213c7dced708ba57cd3 Mon Sep 17 00:00:00 2001 From: "Yuichiro Tachibana (Tsuchiya)" Date: Tue, 6 Feb 2024 20:12:07 +0900 Subject: [PATCH] Create and use it from and --- js/model3D/shared/Canvas3D.svelte | 148 +++++++++++++++++++++++++ js/model3D/shared/Model3D.svelte | 121 ++++---------------- js/model3D/shared/Model3DUpload.svelte | 100 +++-------------- js/model3D/shared/utils.ts | 85 -------------- 4 files changed, 183 insertions(+), 271 deletions(-) create mode 100644 js/model3D/shared/Canvas3D.svelte delete mode 100644 js/model3D/shared/utils.ts diff --git a/js/model3D/shared/Canvas3D.svelte b/js/model3D/shared/Canvas3D.svelte new file mode 100644 index 0000000000000..90c137dc3b999 --- /dev/null +++ b/js/model3D/shared/Canvas3D.svelte @@ -0,0 +1,148 @@ + + + diff --git a/js/model3D/shared/Model3D.svelte b/js/model3D/shared/Model3D.svelte index f29a32061c36d..c127c11fb9c7b 100644 --- a/js/model3D/shared/Model3D.svelte +++ b/js/model3D/shared/Model3D.svelte @@ -2,12 +2,8 @@ import type { FileData } from "@gradio/client"; import { BlockLabel, IconButton } from "@gradio/atoms"; import { File, Download, Undo } from "@gradio/icons"; - import { add_new_model, reset_camera_position } from "./utils"; - import { onMount } from "svelte"; - import * as BABYLON from "babylonjs"; - import * as BABYLON_LOADERS from "babylonjs-loaders"; + import Canvas3D from "./Canvas3D.svelte"; import type { I18nFormatter } from "@gradio/utils"; - import { resolve_wasm_src } from "@gradio/wasm/svelte"; import { dequal } from "dequal"; export let value: FileData | null; @@ -26,101 +22,20 @@ let current_settings = { camera_position, zoom_speed, pan_speed }; - $: { - if ( - BABYLON_LOADERS.OBJFileLoader != undefined && - !BABYLON_LOADERS.OBJFileLoader.IMPORT_VERTEX_COLORS - ) { - BABYLON_LOADERS.OBJFileLoader.IMPORT_VERTEX_COLORS = true; - } - } - - let canvas: HTMLCanvasElement; - let scene: BABYLON.Scene; - let engine: BABYLON.Engine | null; - let mounted = false; - - let resolved_value: typeof value; - /* URL resolution for the Wasm mode. */ - // The `value` prop can be updated before the Promise from `resolve_wasm_src` is resolved. - // In such a case, the resolved value for the old `value` has to be discarded, - // This variable `latest_value` is used to pick up only the value resolved for the latest `value` prop. - let latest_value: typeof value; - $: { - // In normal (non-Wasm) Gradio, the original `value` should be used immediately - // without waiting for `resolve_wasm_src()` to resolve. - // If it waits, a blank element is displayed until the async task finishes - // and it leads to undesirable flickering. - // So set `resolved_value` immediately above, and update it with the resolved values below later. - resolved_value = value; - - if (value?.url) { - latest_value = value; - const resolving_value = value; - resolve_wasm_src(value.url).then((resolved_url) => { - if (latest_value === resolving_value) { - resolved_value = { - ...resolving_value, - url: resolved_url ?? undefined - }; - } else { - resolved_url && URL.revokeObjectURL(resolved_url); - } - }); - } - } - - onMount(() => { - engine = new BABYLON.Engine(canvas, true); - window.addEventListener("resize", () => { - engine?.resize(); - }); - mounted = true; - }); - - $: ({ path } = resolved_value || { - path: undefined - }); - - $: canvas && mounted && path && resolved_value && dispose(); - - function dispose(): void { - if (scene && !scene.isDisposed) { - scene.dispose(); - engine?.stopRenderLoop(); - engine?.dispose(); - engine = null; - engine = new BABYLON.Engine(canvas, true); - window.addEventListener("resize", () => { - engine?.resize(); - }); - } - if (engine !== null) { - scene = add_new_model( - canvas, - scene, - engine, - resolved_value, - clear_color, - camera_position, - zoom_speed, - pan_speed - ); - } - } + let canvas3d: Canvas3D; + let resolved_url: string | undefined; function handle_undo(): void { - reset_camera_position(scene, camera_position, zoom_speed, pan_speed); + canvas3d.reset_camera_position(camera_position, zoom_speed, pan_speed); } $: { if ( - scene && - (!dequal(current_settings.camera_position, camera_position) || - current_settings.zoom_speed !== zoom_speed || - current_settings.pan_speed !== pan_speed) + !dequal(current_settings.camera_position, camera_position) || + current_settings.zoom_speed !== zoom_speed || + current_settings.pan_speed !== pan_speed ) { - reset_camera_position(scene, camera_position, zoom_speed, pan_speed); + canvas3d.reset_camera_position(camera_position, zoom_speed, pan_speed); current_settings = { camera_position, zoom_speed, pan_speed }; } } @@ -131,22 +46,28 @@ Icon={File} label={label || i18n("3D_model.3d_model")} /> -{#if resolved_value} +{#if value}
handle_undo()} />
- +
{/if} @@ -157,7 +78,7 @@ width: var(--size-full); height: var(--size-full); } - canvas { + .model3D :global(canvas) { width: var(--size-full); height: var(--size-full); object-fit: contain; diff --git a/js/model3D/shared/Model3DUpload.svelte b/js/model3D/shared/Model3DUpload.svelte index 19ea9b10ca869..fd03c46c84883 100644 --- a/js/model3D/shared/Model3DUpload.svelte +++ b/js/model3D/shared/Model3DUpload.svelte @@ -1,11 +1,11 @@ @@ -155,7 +76,14 @@ on:undo={handle_undo} absolute /> - + {/if} @@ -169,7 +97,7 @@ height: var(--size-full); } - canvas { + .input-model :global(canvas) { width: var(--size-full); height: var(--size-full); object-fit: contain; diff --git a/js/model3D/shared/utils.ts b/js/model3D/shared/utils.ts deleted file mode 100644 index 1511f7458dec8..0000000000000 --- a/js/model3D/shared/utils.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { FileData } from "@gradio/client"; -import * as BABYLON from "babylonjs"; - -const create_camera = ( - scene: BABYLON.Scene, - camera_position: [number | null, number | null, number | null], - zoom_speed: number, - pan_speed: number -): void => { - scene.createDefaultCamera(true, true, true); - var helperCamera = scene.activeCamera! as BABYLON.ArcRotateCamera; - if (camera_position[0] !== null) { - helperCamera.alpha = BABYLON.Tools.ToRadians(camera_position[0]); - } - if (camera_position[1] !== null) { - helperCamera.beta = BABYLON.Tools.ToRadians(camera_position[1]); - } - if (camera_position[2] !== null) { - helperCamera.radius = camera_position[2]; - } - helperCamera.lowerRadiusLimit = 0.1; - const updateCameraSensibility = (): void => { - helperCamera.wheelPrecision = 250 / (helperCamera.radius * zoom_speed); - helperCamera.panningSensibility = (10000 * pan_speed) / helperCamera.radius; - }; - updateCameraSensibility(); - helperCamera.attachControl(true); - helperCamera.onAfterCheckInputsObservable.add(updateCameraSensibility); -}; - -export const add_new_model = ( - canvas: HTMLCanvasElement, - scene: BABYLON.Scene, - engine: BABYLON.Engine, - value: FileData | null, - clear_color: [number, number, number, number], - camera_position: [number | null, number | null, number | null], - zoom_speed: number, - pan_speed: number -): BABYLON.Scene => { - if (scene && !scene.isDisposed && engine) { - scene.dispose(); - engine.dispose(); - } - - engine = new BABYLON.Engine(canvas, true); - scene = new BABYLON.Scene(engine); - scene.createDefaultCameraOrLight(); - scene.clearColor = scene.clearColor = new BABYLON.Color4(...clear_color); - - engine.runRenderLoop(() => { - scene.render(); - }); - - window.addEventListener("resize", () => { - engine.resize(); - }); - - if (!value) return scene; - let url: string; - - url = value.url!; - - BABYLON.SceneLoader.ShowLoadingScreen = false; - BABYLON.SceneLoader.Append( - url, - "", - scene, - () => create_camera(scene, camera_position, zoom_speed, pan_speed), - undefined, - undefined, - "." + value.path.split(".")[1] - ); - return scene; -}; - -export const reset_camera_position = ( - scene: BABYLON.Scene, - camera_position: [number | null, number | null, number | null], - zoom_speed: number, - pan_speed: number -): void => { - scene.removeCamera(scene.activeCamera!); - create_camera(scene, camera_position, zoom_speed, pan_speed); -};