diff --git a/.changeset/silly-apples-wait.md b/.changeset/silly-apples-wait.md new file mode 100644 index 000000000000..33bca2a0cd58 --- /dev/null +++ b/.changeset/silly-apples-wait.md @@ -0,0 +1,6 @@ +--- +"@gradio/model3d": minor +"gradio": minor +--- + +feat:Lite: Wasm-compatible Model3D diff --git a/js/model3D/package.json b/js/model3D/package.json index d066ffb85f26..3f9ff60e9202 100644 --- a/js/model3D/package.json +++ b/js/model3D/package.json @@ -13,6 +13,7 @@ "@gradio/statustracker": "workspace:^", "@gradio/upload": "workspace:^", "@gradio/utils": "workspace:^", + "@gradio/wasm": "workspace:^", "@types/babylon": "^6.16.6", "babylonjs": "^4.2.1", "babylonjs-loaders": "^4.2.1", diff --git a/js/model3D/shared/Canvas3D.svelte b/js/model3D/shared/Canvas3D.svelte new file mode 100644 index 000000000000..3279c59a1aba --- /dev/null +++ b/js/model3D/shared/Canvas3D.svelte @@ -0,0 +1,149 @@ + + + diff --git a/js/model3D/shared/Model3D.svelte b/js/model3D/shared/Model3D.svelte index 8890fdb6561a..c127c11fb9c7 100644 --- a/js/model3D/shared/Model3D.svelte +++ b/js/model3D/shared/Model3D.svelte @@ -2,10 +2,7 @@ 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 { dequal } from "dequal"; @@ -25,71 +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; - - onMount(() => { - engine = new BABYLON.Engine(canvas, true); - window.addEventListener("resize", () => { - engine?.resize(); - }); - mounted = true; - }); - - $: ({ path } = value || { - path: undefined - }); - - $: canvas && mounted && path && 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, - 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 }; } } @@ -105,7 +51,7 @@
handle_undo()} /> @@ -113,7 +59,15 @@
- + {/if} @@ -124,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 b4941e8acfb6..fd03c46c8488 100644 --- a/js/model3D/shared/Model3DUpload.svelte +++ b/js/model3D/shared/Model3DUpload.svelte @@ -1,10 +1,11 @@ @@ -123,7 +76,14 @@ on:undo={handle_undo} absolute /> - + {/if} @@ -137,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 1511f7458dec..000000000000 --- 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); -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99c96ba87ed3..175bd7f142f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1167,6 +1167,9 @@ importers: '@gradio/utils': specifier: workspace:^ version: link:../utils + '@gradio/wasm': + specifier: workspace:^ + version: link:../wasm '@types/babylon': specifier: ^6.16.6 version: 6.16.6