@gradio/model3d 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @gradio/model3d
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Features
6
+
7
+ - [#7406](https://github.com/gradio-app/gradio/pull/7406) [`3e886d8`](https://github.com/gradio-app/gradio/commit/3e886d8f0ac55c416dae51c1c2661e16eb34718e) - Model3D Gaussian Splatting. Thanks [@dylanebert](https://github.com/dylanebert)!
8
+
3
9
  ## 0.7.0
4
10
 
5
11
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/model3d",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -11,12 +11,13 @@
11
11
  "babylonjs": "^4.2.1",
12
12
  "babylonjs-loaders": "^4.2.1",
13
13
  "dequal": "^2.0.2",
14
- "@gradio/client": "^0.12.0",
15
- "@gradio/atoms": "^0.5.1",
14
+ "gsplat": "^1.0.5",
15
+ "@gradio/atoms": "^0.5.2",
16
+ "@gradio/client": "^0.12.1",
16
17
  "@gradio/icons": "^0.3.2",
17
- "@gradio/upload": "^0.7.2",
18
- "@gradio/utils": "^0.2.2",
19
- "@gradio/statustracker": "^0.4.5",
18
+ "@gradio/statustracker": "^0.4.6",
19
+ "@gradio/upload": "^0.7.3",
20
+ "@gradio/utils": "^0.3.0",
20
21
  "@gradio/wasm": "^0.6.0"
21
22
  },
22
23
  "main_changeset": true,
@@ -0,0 +1,130 @@
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+ import * as SPLAT from "gsplat";
4
+ import type { FileData } from "@gradio/client";
5
+ import { resolve_wasm_src } from "@gradio/wasm/svelte";
6
+
7
+ export let value: FileData;
8
+ export let zoom_speed: number;
9
+ export let pan_speed: number;
10
+
11
+ $: url = value.url;
12
+
13
+ /* URL resolution for the Wasm mode. */
14
+ export let resolved_url: typeof url = undefined; // Exposed to be bound to the download link in the parent component.
15
+ // The prop can be updated before the Promise from `resolve_wasm_src` is resolved.
16
+ // In such a case, the resolved url for the old `url` has to be discarded,
17
+ // This variable `latest_url` is used to pick up only the value resolved for the latest `url`.
18
+ let latest_url: typeof url;
19
+ $: {
20
+ // In normal (non-Wasm) Gradio, the original `url` should be used immediately
21
+ // without waiting for `resolve_wasm_src()` to resolve.
22
+ // If it waits, a blank element is displayed until the async task finishes
23
+ // and it leads to undesirable flickering.
24
+ // So set `resolved_url` immediately above, and update it with the resolved values below later.
25
+ resolved_url = url;
26
+
27
+ if (url) {
28
+ latest_url = url;
29
+ const resolving_url = url;
30
+ resolve_wasm_src(url).then((resolved) => {
31
+ if (latest_url === resolving_url) {
32
+ resolved_url = resolved ?? undefined;
33
+ } else {
34
+ resolved && URL.revokeObjectURL(resolved);
35
+ }
36
+ });
37
+ }
38
+ }
39
+
40
+ let canvas: HTMLCanvasElement;
41
+ let scene: SPLAT.Scene;
42
+ let camera: SPLAT.Camera;
43
+ let renderer: SPLAT.WebGLRenderer | null = null;
44
+ let controls: SPLAT.OrbitControls;
45
+ let mounted = false;
46
+ let frameId: number | null = null;
47
+
48
+ function reset_scene(): void {
49
+ if (frameId !== null) {
50
+ cancelAnimationFrame(frameId);
51
+ frameId = null;
52
+ }
53
+
54
+ if (renderer !== null) {
55
+ renderer.dispose();
56
+ renderer = null;
57
+ }
58
+
59
+ scene = new SPLAT.Scene();
60
+ camera = new SPLAT.Camera();
61
+ renderer = new SPLAT.WebGLRenderer(canvas);
62
+ controls = new SPLAT.OrbitControls(camera, canvas);
63
+ controls.zoomSpeed = zoom_speed;
64
+ controls.panSpeed = pan_speed;
65
+
66
+ if (!value) {
67
+ return;
68
+ }
69
+
70
+ let loading = false;
71
+ const load = async (): Promise<void> => {
72
+ if (loading) {
73
+ console.error("Already loading");
74
+ return;
75
+ }
76
+ if (!resolved_url) {
77
+ throw new Error("No resolved URL");
78
+ }
79
+ loading = true;
80
+ if (resolved_url.endsWith(".ply")) {
81
+ await SPLAT.PLYLoader.LoadAsync(resolved_url, scene, undefined);
82
+ } else if (resolved_url.endsWith(".splat")) {
83
+ await SPLAT.Loader.LoadAsync(resolved_url, scene, undefined);
84
+ } else {
85
+ throw new Error("Unsupported file type");
86
+ }
87
+ loading = false;
88
+ };
89
+
90
+ const frame = (): void => {
91
+ if (!renderer) {
92
+ return;
93
+ }
94
+
95
+ if (loading) {
96
+ frameId = requestAnimationFrame(frame);
97
+ return;
98
+ }
99
+
100
+ controls.update();
101
+ renderer.render(scene, camera);
102
+
103
+ frameId = requestAnimationFrame(frame);
104
+ };
105
+
106
+ load();
107
+ frameId = requestAnimationFrame(frame);
108
+ }
109
+
110
+ onMount(() => {
111
+ if (value != null) {
112
+ reset_scene();
113
+ }
114
+ mounted = true;
115
+
116
+ return () => {
117
+ if (renderer) {
118
+ renderer.dispose();
119
+ }
120
+ };
121
+ });
122
+
123
+ $: ({ path } = value || {
124
+ path: undefined
125
+ });
126
+
127
+ $: canvas && mounted && path && reset_scene();
128
+ </script>
129
+
130
+ <canvas bind:this={canvas}></canvas>
@@ -2,7 +2,6 @@
2
2
  import type { FileData } from "@gradio/client";
3
3
  import { BlockLabel, IconButton } from "@gradio/atoms";
4
4
  import { File, Download, Undo } from "@gradio/icons";
5
- import Canvas3D from "./Canvas3D.svelte";
6
5
  import type { I18nFormatter } from "@gradio/utils";
7
6
  import { dequal } from "dequal";
8
7
 
@@ -22,9 +21,21 @@
22
21
 
23
22
  let current_settings = { camera_position, zoom_speed, pan_speed };
24
23
 
25
- let canvas3d: Canvas3D;
24
+ let canvas3dgs: any;
25
+ let canvas3d: any;
26
+ let use_3dgs = false;
26
27
  let resolved_url: string | undefined;
27
28
 
29
+ async function loadCanvas3D(): Promise<any> {
30
+ const module = await import("./Canvas3D.svelte");
31
+ return module.default;
32
+ }
33
+
34
+ async function loadCanvas3DGS(): Promise<any> {
35
+ const module = await import("./Canvas3DGS.svelte");
36
+ return module.default;
37
+ }
38
+
28
39
  function handle_undo(): void {
29
40
  canvas3d.reset_camera_position(camera_position, zoom_speed, pan_speed);
30
41
  }
@@ -39,6 +50,21 @@
39
50
  current_settings = { camera_position, zoom_speed, pan_speed };
40
51
  }
41
52
  }
53
+
54
+ $: {
55
+ if (value) {
56
+ use_3dgs = value?.path.endsWith(".splat") || value?.path.endsWith(".ply");
57
+ if (use_3dgs) {
58
+ loadCanvas3DGS().then((module) => {
59
+ canvas3dgs = module;
60
+ });
61
+ } else {
62
+ loadCanvas3D().then((module) => {
63
+ canvas3d = module;
64
+ });
65
+ }
66
+ }
67
+ }
42
68
  </script>
43
69
 
44
70
  <BlockLabel
@@ -59,15 +85,25 @@
59
85
  </a>
60
86
  </div>
61
87
 
62
- <Canvas3D
63
- bind:this={canvas3d}
64
- bind:resolved_url
65
- {value}
66
- {clear_color}
67
- {camera_position}
68
- {zoom_speed}
69
- {pan_speed}
70
- />
88
+ {#if use_3dgs}
89
+ <svelte:component
90
+ this={canvas3dgs}
91
+ bind:resolved_url
92
+ {value}
93
+ {zoom_speed}
94
+ {pan_speed}
95
+ />
96
+ {:else}
97
+ <svelte:component
98
+ this={canvas3d}
99
+ bind:resolved_url
100
+ {value}
101
+ {clear_color}
102
+ {camera_position}
103
+ {zoom_speed}
104
+ {pan_speed}
105
+ />
106
+ {/if}
71
107
  </div>
72
108
  {/if}
73
109
 
@@ -5,7 +5,6 @@
5
5
  import { BlockLabel } from "@gradio/atoms";
6
6
  import { File } from "@gradio/icons";
7
7
  import type { I18nFormatter } from "@gradio/utils";
8
- import Canvas3D from "./Canvas3D.svelte";
9
8
 
10
9
  export let value: null | FileData;
11
10
  export let clear_color: [number, number, number, number] = [0, 0, 0, 0];
@@ -39,9 +38,22 @@
39
38
  dispatch("change");
40
39
  }
41
40
 
42
- let canvas3D: Canvas3D;
41
+ let canvas3d: any;
42
+ let canvas3dgs: any;
43
+ let use_3dgs = false;
44
+
45
+ async function loadCanvas3D(): Promise<any> {
46
+ const module = await import("./Canvas3D.svelte");
47
+ return module.default;
48
+ }
49
+
50
+ async function loadCanvas3DGS(): Promise<any> {
51
+ const module = await import("./Canvas3DGS.svelte");
52
+ return module.default;
53
+ }
54
+
43
55
  async function handle_undo(): Promise<void> {
44
- canvas3D.reset_camera_position(camera_position, zoom_speed, pan_speed);
56
+ canvas3d.reset_camera_position(camera_position, zoom_speed, pan_speed);
45
57
  }
46
58
 
47
59
  const dispatch = createEventDispatcher<{
@@ -54,6 +66,21 @@
54
66
  let dragging = false;
55
67
 
56
68
  $: dispatch("drag", dragging);
69
+
70
+ $: {
71
+ if (value) {
72
+ use_3dgs = value?.path.endsWith(".splat") || value?.path.endsWith(".ply");
73
+ if (use_3dgs) {
74
+ loadCanvas3DGS().then((module) => {
75
+ canvas3dgs = module;
76
+ });
77
+ } else {
78
+ loadCanvas3D().then((module) => {
79
+ canvas3d = module;
80
+ });
81
+ }
82
+ }
83
+ }
57
84
  </script>
58
85
 
59
86
  <BlockLabel {show_label} Icon={File} label={label || "3D Model"} />
@@ -62,7 +89,7 @@
62
89
  <Upload
63
90
  on:load={handle_upload}
64
91
  {root}
65
- filetype={[".stl", ".obj", ".gltf", ".glb", "model/obj"]}
92
+ filetype={[".stl", ".obj", ".gltf", ".glb", "model/obj", ".splat", ".ply"]}
66
93
  bind:dragging
67
94
  >
68
95
  <slot />
@@ -76,14 +103,19 @@
76
103
  on:undo={handle_undo}
77
104
  absolute
78
105
  />
79
- <Canvas3D
80
- bind:this={canvas3D}
81
- {value}
82
- {clear_color}
83
- {camera_position}
84
- {zoom_speed}
85
- {pan_speed}
86
- ></Canvas3D>
106
+
107
+ {#if use_3dgs}
108
+ <svelte:component this={canvas3dgs} {value} {zoom_speed} {pan_speed} />
109
+ {:else}
110
+ <svelte:component
111
+ this={canvas3d}
112
+ {value}
113
+ {clear_color}
114
+ {camera_position}
115
+ {zoom_speed}
116
+ {pan_speed}
117
+ />
118
+ {/if}
87
119
  </div>
88
120
  {/if}
89
121