dylanebert HF staff commited on
Commit
31a2d08
β€’
1 Parent(s): df89600

initial leaderboard iteration

Browse files
This view is limited to 50 files because it contains too many changes. Β  See raw diff
Files changed (50) hide show
  1. viewer/.gitignore β†’ .gitignore +0 -0
  2. viewer/.npmrc β†’ .npmrc +0 -0
  3. Dockerfile +3 -3
  4. viewer/package-lock.json β†’ package-lock.json +7 -0
  5. viewer/package.json β†’ package.json +1 -0
  6. {viewer/src β†’ src}/app.d.ts +0 -0
  7. {viewer/src β†’ src}/app.html +0 -0
  8. {viewer/src β†’ src}/lib/index.ts +0 -0
  9. src/routes/+page.svelte +62 -0
  10. src/routes/Leaderboard.svelte +58 -0
  11. src/routes/ModelDetails.svelte +73 -0
  12. src/routes/Viewer.svelte +76 -0
  13. {viewer/src β†’ src}/routes/store.js +0 -0
  14. {viewer/src/routes/viewer/[slug] β†’ src/routes/viewers}/BabylonViewer.ts +0 -0
  15. {viewer/src/routes/viewer/[slug] β†’ src/routes/viewers}/IViewer.ts +0 -0
  16. {viewer/src/routes/viewer/[slug] β†’ src/routes/viewers}/SplatViewer.ts +12 -3
  17. src/routes/viewers/ViewerFactory.ts +19 -0
  18. {viewer/static β†’ static}/favicon.png +0 -0
  19. {viewer/static β†’ static}/global.css +129 -26
  20. viewer/svelte.config.js β†’ svelte.config.js +0 -0
  21. viewer/tsconfig.json β†’ tsconfig.json +0 -0
  22. viewer/README.md +0 -38
  23. viewer/src/lib/data/dataLoader.ts +0 -10
  24. viewer/src/lib/data/models.json +0 -137
  25. viewer/src/lib/data/scenes.json +0 -255
  26. viewer/src/lib/placeholder.png +0 -0
  27. viewer/src/routes/+page.svelte +0 -57
  28. viewer/src/routes/components/ModelsView.svelte +0 -42
  29. viewer/src/routes/components/ScenesView.svelte +0 -30
  30. viewer/src/routes/models/[slug]/+page.server.ts +0 -14
  31. viewer/src/routes/models/[slug]/+page.svelte +0 -182
  32. viewer/src/routes/viewer/[slug]/+page.server.ts +0 -17
  33. viewer/src/routes/viewer/[slug]/+page.svelte +0 -374
  34. viewer/static/thumbnails/3dgs-bicycle.png +0 -0
  35. viewer/static/thumbnails/3dgs-bonsai.png +0 -0
  36. viewer/static/thumbnails/3dgs-counter.png +0 -0
  37. viewer/static/thumbnails/3dgs-flowers.png +0 -0
  38. viewer/static/thumbnails/3dgs-garden.png +0 -0
  39. viewer/static/thumbnails/3dgs-kitchen.png +0 -0
  40. viewer/static/thumbnails/3dgs-playroom.png +0 -0
  41. viewer/static/thumbnails/3dgs-room.png +0 -0
  42. viewer/static/thumbnails/3dgs-stump.png +0 -0
  43. viewer/static/thumbnails/3dgs-treehill.png +0 -0
  44. viewer/static/thumbnails/dreamfusion-chick.png +0 -0
  45. viewer/static/thumbnails/dreamfusion-corgi.png +0 -0
  46. viewer/static/thumbnails/dreamfusion-crab.png +0 -0
  47. viewer/static/thumbnails/dreamfusion-eagle.png +0 -0
  48. viewer/static/thumbnails/dreamfusion-ghost.png +0 -0
  49. viewer/static/thumbnails/dreamfusion-lemur.png +0 -0
  50. viewer/static/thumbnails/dreamfusion-pig.png +0 -0
viewer/.gitignore β†’ .gitignore RENAMED
File without changes
viewer/.npmrc β†’ .npmrc RENAMED
File without changes
Dockerfile CHANGED
@@ -1,11 +1,11 @@
1
  FROM node:alpine
2
 
3
  WORKDIR /app
4
- COPY viewer/package.json package.json
5
  RUN npm install
6
 
7
- COPY viewer/ /app
8
  RUN npm run build
9
 
10
  EXPOSE 3000
11
- CMD ["npm", "start"]
 
1
  FROM node:alpine
2
 
3
  WORKDIR /app
4
+ COPY package.json package.json
5
  RUN npm install
6
 
7
+ COPY / /app
8
  RUN npm run build
9
 
10
  EXPOSE 3000
11
+ CMD ["npm", "start"]
viewer/package-lock.json β†’ package-lock.json RENAMED
@@ -16,6 +16,7 @@
16
  "@sveltejs/adapter-auto": "^2.0.0",
17
  "@sveltejs/adapter-node": "^1.3.1",
18
  "@sveltejs/kit": "^1.20.4",
 
19
  "svelte": "^4.0.5",
20
  "svelte-check": "^3.4.3",
21
  "tslib": "^2.4.1",
@@ -829,6 +830,12 @@
829
  "node": ">=6"
830
  }
831
  },
 
 
 
 
 
 
832
  "node_modules/chokidar": {
833
  "version": "3.5.3",
834
  "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
 
16
  "@sveltejs/adapter-auto": "^2.0.0",
17
  "@sveltejs/adapter-node": "^1.3.1",
18
  "@sveltejs/kit": "^1.20.4",
19
+ "carbon-icons-svelte": "^12.8.0",
20
  "svelte": "^4.0.5",
21
  "svelte-check": "^3.4.3",
22
  "tslib": "^2.4.1",
 
830
  "node": ">=6"
831
  }
832
  },
833
+ "node_modules/carbon-icons-svelte": {
834
+ "version": "12.8.0",
835
+ "resolved": "https://registry.npmjs.org/carbon-icons-svelte/-/carbon-icons-svelte-12.8.0.tgz",
836
+ "integrity": "sha512-ops12PG2NucXc4fWaDP9ZmsN4oA4ofjwQvd3yJAbrRzLfrUw9uMyC5u7FVSujS6gLkPRIbdd+LekfZt+1kY/zg==",
837
+ "dev": true
838
+ },
839
  "node_modules/chokidar": {
840
  "version": "3.5.3",
841
  "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
viewer/package.json β†’ package.json RENAMED
@@ -14,6 +14,7 @@
14
  "@sveltejs/adapter-auto": "^2.0.0",
15
  "@sveltejs/adapter-node": "^1.3.1",
16
  "@sveltejs/kit": "^1.20.4",
 
17
  "svelte": "^4.0.5",
18
  "svelte-check": "^3.4.3",
19
  "tslib": "^2.4.1",
 
14
  "@sveltejs/adapter-auto": "^2.0.0",
15
  "@sveltejs/adapter-node": "^1.3.1",
16
  "@sveltejs/kit": "^1.20.4",
17
+ "carbon-icons-svelte": "^12.8.0",
18
  "svelte": "^4.0.5",
19
  "svelte-check": "^3.4.3",
20
  "tslib": "^2.4.1",
{viewer/src β†’ src}/app.d.ts RENAMED
File without changes
{viewer/src β†’ src}/app.html RENAMED
File without changes
{viewer/src β†’ src}/lib/index.ts RENAMED
File without changes
src/routes/+page.svelte ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import Leaderboard from "./Leaderboard.svelte";
3
+ import ModelDetails from "./ModelDetails.svelte";
4
+ import Viewer from "./Viewer.svelte";
5
+
6
+ interface Scene {
7
+ name: string;
8
+ url: string;
9
+ thumbnail: string;
10
+ }
11
+
12
+ let currentView: "Leaderboard" | "Vote" | "ModelDetails" | "Viewer" = "Leaderboard";
13
+ let selectedEntry: { name: string; path: string } | null = null;
14
+ let selectedScene: Scene | null = null;
15
+
16
+ function goHome() {
17
+ window.location.href = "/";
18
+ }
19
+
20
+ function showModelDetails(entry: { name: string; path: string }) {
21
+ selectedEntry = entry;
22
+ currentView = "ModelDetails";
23
+ }
24
+
25
+ function showScene(scene: Scene) {
26
+ selectedScene = scene;
27
+ currentView = "Viewer";
28
+ }
29
+ </script>
30
+
31
+ <div class="container">
32
+ <div on:pointerdown={goHome} class="banner">
33
+ <h1>IGF</h1>
34
+ <p>Generative 3D Leaderboard</p>
35
+ </div>
36
+
37
+ {#if currentView === "Leaderboard" || currentView === "Vote"}
38
+ <div class="tabs">
39
+ <button on:click={() => (currentView = "Vote")} class={currentView === "Vote" ? "active" : ""}>Vote</button>
40
+ <button on:click={() => (currentView = "Leaderboard")} class={currentView === "Leaderboard" ? "active" : ""}
41
+ >Leaderboard</button
42
+ >
43
+ </div>
44
+ {/if}
45
+
46
+ {#if currentView === "Leaderboard"}
47
+ <Leaderboard onEntryClick={showModelDetails} />
48
+ {:else if currentView === "Vote"}
49
+ <div class="loading-container">
50
+ <div class="loading-text">Coming Soon</div>
51
+ </div>
52
+ {:else if currentView === "ModelDetails" && selectedEntry}
53
+ <ModelDetails
54
+ modelName={selectedEntry.name}
55
+ modelPath={selectedEntry.path}
56
+ onBack={() => (currentView = "Leaderboard")}
57
+ onSceneClick={showScene}
58
+ />
59
+ {:else if currentView === "Viewer" && selectedScene && selectedEntry}
60
+ <Viewer modelName={selectedEntry.name} scene={selectedScene} onBack={() => (currentView = "ModelDetails")} />
61
+ {/if}
62
+ </div>
src/routes/Leaderboard.svelte ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+ import { ProgressBarRound } from "carbon-icons-svelte";
4
+
5
+ interface Entry {
6
+ name: string;
7
+ path: string;
8
+ thumbnail: string;
9
+ }
10
+
11
+ export let onEntryClick: (entry: Entry) => void;
12
+
13
+ let leaderboard: Entry[] = [];
14
+
15
+ const fetchLeaderboardData = async () => {
16
+ const baseUrl = "https://huggingface.co";
17
+ const repoId = "dylanebert/3d-arena";
18
+ const url = `${baseUrl}/api/datasets/${repoId}`;
19
+ const response = await fetch(url);
20
+ const data = await response.json();
21
+
22
+ const entries: Entry[] = [];
23
+
24
+ for (const item of data.siblings) {
25
+ const filename = item.rfilename;
26
+ if (!filename.endsWith(".json")) continue;
27
+
28
+ const directory = filename.split("/").slice(0, -1).join("/");
29
+ const name = directory.split("/").pop();
30
+ const thumbnail = `${baseUrl}/datasets/${repoId}/resolve/main/${directory}/thumbnail.png`;
31
+
32
+ entries.push({ name, path: filename, thumbnail });
33
+ }
34
+
35
+ leaderboard = entries;
36
+ };
37
+
38
+ onMount(async () => {
39
+ await fetchLeaderboardData();
40
+ });
41
+ </script>
42
+
43
+ {#if leaderboard.length > 0}
44
+ <div class="grid">
45
+ {#each leaderboard as entry, index}
46
+ <button class="grid-item" on:click={() => onEntryClick(entry)}>
47
+ <img src={entry.thumbnail} alt={entry.name} class="thumbnail" />
48
+ <div class="ranking">{index + 1}</div>
49
+ <div class="title">{entry.name}</div>
50
+ </button>
51
+ {/each}
52
+ </div>
53
+ {:else}
54
+ <div class="loading-container">
55
+ <ProgressBarRound class="loading-icon" />
56
+ <div class="loading-text">Loading...</div>
57
+ </div>
58
+ {/if}
src/routes/ModelDetails.svelte ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+ import { ProgressBarRound, ArrowLeft } from "carbon-icons-svelte";
4
+
5
+ interface Scene {
6
+ name: string;
7
+ url: string;
8
+ thumbnail: string;
9
+ }
10
+
11
+ export let modelName: string;
12
+ export let modelPath: string;
13
+ export let onBack: () => void;
14
+ export let onSceneClick: (scene: Scene) => void;
15
+
16
+ let scenes: Scene[] = [];
17
+
18
+ async function fetchScenes() {
19
+ scenes = [];
20
+
21
+ const baseUrl = "https://huggingface.co";
22
+ const repoId = "dylanebert/3d-arena";
23
+ const url = `${baseUrl}/api/datasets/${repoId}`;
24
+ const response = await fetch(url);
25
+ const responseData = await response.json();
26
+
27
+ const extensions = ["obj", "glb", "ply", "splat"];
28
+ const directory = modelPath.split("/").slice(0, -1).join("/");
29
+
30
+ scenes = responseData.siblings
31
+ .filter((scene: any) => {
32
+ const fileExtension = scene.rfilename.split(".").pop();
33
+ return scene.rfilename.startsWith(directory) && extensions.includes(fileExtension);
34
+ })
35
+ .reduce((acc: Scene[], scene: any) => {
36
+ const name = scene.rfilename.split("/").pop().split(".").slice(0, -1).join(".");
37
+ const url = `${baseUrl}/datasets/${repoId}/resolve/main/${scene.rfilename}`;
38
+ const thumbnail = url.replace(/\.[^.]+$/, ".png");
39
+ acc.push({ name, url, thumbnail });
40
+ return acc;
41
+ }, []);
42
+
43
+ scenes = [...scenes];
44
+ }
45
+
46
+ onMount(fetchScenes);
47
+ </script>
48
+
49
+ <div class="header">
50
+ <div class="back" aria-label="Back" aria-hidden="true" on:click={onBack}>
51
+ <ArrowLeft size={24} />
52
+ </div>
53
+ <div class="spacer" />
54
+ <button class="title-button" on:click={fetchScenes}>
55
+ <h2 class="muted">{modelName}</h2>
56
+ </button>
57
+ <div class="spacer" />
58
+ </div>
59
+ {#if scenes.length > 0}
60
+ <div class="grid">
61
+ {#each scenes as scene}
62
+ <button class="grid-item" on:click={() => onSceneClick(scene)}>
63
+ <img src={scene.thumbnail} alt={scene.name} class="thumbnail" />
64
+ <div class="title">{modelName}</div>
65
+ </button>
66
+ {/each}
67
+ </div>
68
+ {:else}
69
+ <div class="loading-container">
70
+ <ProgressBarRound class="loading-icon" />
71
+ <div class="loading-text">Loading...</div>
72
+ </div>
73
+ {/if}
src/routes/Viewer.svelte ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from "svelte";
3
+ import type { IViewer } from "./viewers/IViewer";
4
+ import { createViewer } from "./viewers/ViewerFactory";
5
+ import ArrowLeft from "carbon-icons-svelte/lib/ArrowLeft.svelte";
6
+
7
+ interface Scene {
8
+ name: string;
9
+ url: string;
10
+ thumbnail: string;
11
+ }
12
+
13
+ export let modelName: string;
14
+ export let scene: Scene;
15
+ export let onBack: () => void;
16
+
17
+ let container: HTMLDivElement;
18
+ let canvas: HTMLCanvasElement;
19
+
20
+ let viewer: IViewer;
21
+
22
+ async function loadScene() {
23
+ viewer = await createViewer(scene.url, canvas);
24
+ window.addEventListener("resize", handleResize);
25
+ window.addEventListener("keydown", handleKeyDown);
26
+ handleResize();
27
+ }
28
+
29
+ function handleResize() {
30
+ if (!canvas || !container) return;
31
+ const maxWidth = container.clientHeight * (16 / 9);
32
+ const maxHeight = container.clientWidth * (9 / 16);
33
+ canvas.width = Math.min(container.clientWidth, maxWidth);
34
+ canvas.height = Math.min(container.clientHeight, maxHeight);
35
+ }
36
+
37
+ function handleKeyDown(e: KeyboardEvent) {
38
+ if (e.code === "KeyP") {
39
+ capture();
40
+ }
41
+ }
42
+
43
+ async function capture() {
44
+ const data = await viewer.capture();
45
+ if (!data) {
46
+ console.error("Failed to capture screenshot");
47
+ return;
48
+ }
49
+ const a = document.createElement("a");
50
+ a.href = data;
51
+ a.download = "screenshot.png";
52
+ a.click();
53
+ }
54
+
55
+ onMount(loadScene);
56
+
57
+ onDestroy(() => {
58
+ viewer?.dispose();
59
+ window.removeEventListener("resize", handleResize);
60
+ window.removeEventListener("keydown", handleKeyDown);
61
+ });
62
+ </script>
63
+
64
+ <div class="header">
65
+ <div class="back" aria-label="Back" aria-hidden="true" on:click={onBack}>
66
+ <ArrowLeft size={24} />
67
+ </div>
68
+ <button class="title-button" on:click={loadScene}>
69
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
70
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
71
+ <h2><span class="muted" on:click={onBack}>{modelName}/</span>{scene.name}</h2>
72
+ </button>
73
+ </div>
74
+ <div class="canvas-container" bind:this={container}>
75
+ <canvas bind:this={canvas} width={800} height={600} />
76
+ </div>
{viewer/src β†’ src}/routes/store.js RENAMED
File without changes
{viewer/src/routes/viewer/[slug] β†’ src/routes/viewers}/BabylonViewer.ts RENAMED
File without changes
{viewer/src/routes/viewer/[slug] β†’ src/routes/viewers}/IViewer.ts RENAMED
File without changes
{viewer/src/routes/viewer/[slug] β†’ src/routes/viewers}/SplatViewer.ts RENAMED
@@ -18,14 +18,23 @@ export class SplatViewer implements IViewer {
18
  this.scene = new SPLAT.Scene();
19
  this.camera = new SPLAT.Camera();
20
  this.controls = new SPLAT.OrbitControls(this.camera, canvas);
 
21
 
22
  this.handleResize = this.handleResize.bind(this);
23
  }
24
 
25
  async loadScene(url: string, loadingBarCallback?: (progress: number) => void) {
26
- await SPLAT.Loader.LoadAsync(url, this.scene, (progress) => {
27
- loadingBarCallback?.(progress);
28
- });
 
 
 
 
 
 
 
 
29
 
30
  const frame = () => {
31
  this.controls.update();
 
18
  this.scene = new SPLAT.Scene();
19
  this.camera = new SPLAT.Camera();
20
  this.controls = new SPLAT.OrbitControls(this.camera, canvas);
21
+ this.controls.orbitSpeed = 3.0;
22
 
23
  this.handleResize = this.handleResize.bind(this);
24
  }
25
 
26
  async loadScene(url: string, loadingBarCallback?: (progress: number) => void) {
27
+ if (url.endsWith(".splat")) {
28
+ await SPLAT.Loader.LoadAsync(url, this.scene, (progress) => {
29
+ loadingBarCallback?.(progress);
30
+ });
31
+ } else if (url.endsWith(".ply")) {
32
+ await SPLAT.PLYLoader.LoadAsync(url, this.scene, (progress) => {
33
+ loadingBarCallback?.(progress);
34
+ });
35
+ } else {
36
+ throw new Error("Unsupported file format");
37
+ }
38
 
39
  const frame = () => {
40
  this.controls.update();
src/routes/viewers/ViewerFactory.ts ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { IViewer } from "./IViewer";
2
+ import { BabylonViewer } from "./BabylonViewer";
3
+ import { SplatViewer } from "./SplatViewer";
4
+
5
+ const meshFormats = ["obj", "stl", "gltf", "glb"];
6
+ const splatFormats = ["splat", "ply"];
7
+
8
+ export async function createViewer(url: string, canvas: HTMLCanvasElement): Promise<IViewer> {
9
+ let viewer: IViewer;
10
+ if (meshFormats.some((format) => url.endsWith(format))) {
11
+ viewer = new BabylonViewer(canvas);
12
+ } else if (splatFormats.some((format) => url.endsWith(format))) {
13
+ viewer = new SplatViewer(canvas);
14
+ } else {
15
+ throw new Error("Unsupported file format");
16
+ }
17
+ await viewer.loadScene(url);
18
+ return viewer;
19
+ }
{viewer/static β†’ static}/favicon.png RENAMED
File without changes
{viewer/static β†’ static}/global.css RENAMED
@@ -1,6 +1,6 @@
1
  html,
2
  body {
3
- font-family: 'Roboto', sans-serif;
4
  background-color: #1a1b1e;
5
  color: white;
6
  margin: 0;
@@ -32,11 +32,45 @@ body {
32
  cursor: pointer;
33
  }
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  .container {
36
  padding: 10px 15px 80px 15px;
37
  margin-left: auto;
38
  margin-right: auto;
39
- max-height: 100vh;
40
  overflow-y: auto;
41
  position: relative;
42
  box-sizing: border-box;
@@ -58,31 +92,47 @@ body {
58
  }
59
  }
60
 
61
- .exit-button {
 
 
62
  display: flex;
 
63
  justify-content: center;
64
  align-items: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  position: absolute;
66
- top: 10px;
67
- right: 16px;
68
- width: 36px;
69
- height: 36px;
70
- color: #aaa;
71
- font-size: 24px;
72
  cursor: pointer;
73
- font-weight: bold;
74
- transition: scale 0.2s ease;
75
  }
76
 
77
- .exit-button:hover {
78
- scale: 1.1;
79
  }
80
 
81
- .header {
82
- padding: 20px 0;
83
- font-size: 24px;
84
- font-weight: bold;
85
- color: #aaa;
 
 
 
 
 
86
  }
87
 
88
  .viewer {
@@ -129,6 +179,12 @@ body {
129
  padding-top: 100%;
130
  }
131
 
 
 
 
 
 
 
132
  .grid-item:hover {
133
  cursor: pointer;
134
  }
@@ -153,18 +209,65 @@ body {
153
  height: 48px;
154
  }
155
 
156
- .thumbnail {
157
  position: absolute;
158
- top: -10px;
159
- ;
160
  left: 0;
161
- width: 100%;
162
- height: auto;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  overflow: hidden;
164
  scale: 1;
165
- transition: scale 0.2s ease;
 
166
  }
167
 
168
  .grid-item:hover .thumbnail {
169
- scale: 1.1;
170
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  html,
2
  body {
3
+ font-family: "Roboto", sans-serif;
4
  background-color: #1a1b1e;
5
  color: white;
6
  margin: 0;
 
32
  cursor: pointer;
33
  }
34
 
35
+ .muted {
36
+ color: #aaa;
37
+ }
38
+
39
+ .loading-container {
40
+ display: flex;
41
+ flex-direction: column;
42
+ justify-content: center;
43
+ align-items: center;
44
+ width: 100%;
45
+ height: 196px;
46
+ }
47
+
48
+ .loading-icon {
49
+ width: 32px;
50
+ height: 32px;
51
+ animation: spin 0.5s linear infinite;
52
+ color: #aaa;
53
+ }
54
+
55
+ .loading-text {
56
+ color: #aaa;
57
+ margin-top: 10px;
58
+ }
59
+
60
+ @keyframes spin {
61
+ from {
62
+ transform: rotate(0deg);
63
+ }
64
+ to {
65
+ transform: rotate(360deg);
66
+ }
67
+ }
68
+
69
  .container {
70
  padding: 10px 15px 80px 15px;
71
  margin-left: auto;
72
  margin-right: auto;
73
+ height: 100vh;
74
  overflow-y: auto;
75
  position: relative;
76
  box-sizing: border-box;
 
92
  }
93
  }
94
 
95
+ .canvas-container {
96
+ position: relative;
97
+ box-sizing: border-box;
98
  display: flex;
99
+ flex-direction: column;
100
  justify-content: center;
101
  align-items: center;
102
+ width: 100%;
103
+ max-height: 50%;
104
+ overflow: hidden;
105
+ }
106
+
107
+ .header {
108
+ display: flex;
109
+ align-items: center;
110
+ justify-content: center;
111
+ position: relative;
112
+ }
113
+
114
+ .back {
115
  position: absolute;
116
+ left: 0;
 
 
 
 
 
117
  cursor: pointer;
118
+ transition: transform 0.1s;
119
+ padding: 16px;
120
  }
121
 
122
+ .back:hover {
123
+ transform: scale(1.2);
124
  }
125
 
126
+ .spacer {
127
+ flex: 1;
128
+ }
129
+
130
+ .title-button {
131
+ background: none;
132
+ border: none;
133
+ font: inherit;
134
+ color: inherit;
135
+ cursor: pointer;
136
  }
137
 
138
  .viewer {
 
179
  padding-top: 100%;
180
  }
181
 
182
+ @media (max-width: 575px) {
183
+ .grid-item::before {
184
+ padding-top: 56.25%;
185
+ }
186
+ }
187
+
188
  .grid-item:hover {
189
  cursor: pointer;
190
  }
 
209
  height: 48px;
210
  }
211
 
212
+ .grid-item .ranking {
213
  position: absolute;
214
+ top: 0;
 
215
  left: 0;
216
+ background-color: #1a1b1e;
217
+ color: #aaa;
218
+ padding: 10px;
219
+ box-sizing: border-box;
220
+ font-size: 16px;
221
+ overflow: hidden;
222
+ transition: background-color 0.2s ease;
223
+ }
224
+
225
+ .grid-item:hover .ranking {
226
+ background-color: #444;
227
+ }
228
+
229
+ .thumbnail {
230
+ position: absolute;
231
+ top: 50%;
232
+ left: 50%;
233
+ width: auto;
234
+ height: 100%;
235
  overflow: hidden;
236
  scale: 1;
237
+ transform: translate(-50%, -50%);
238
+ transition: transform 0.2s ease;
239
  }
240
 
241
  .grid-item:hover .thumbnail {
242
+ transform: translate(-50%, -50%) scale(1.1);
243
+ }
244
+
245
+ .tabs {
246
+ display: flex;
247
+ justify-content: left;
248
+ margin-top: 10px;
249
+ margin-bottom: 10px;
250
+ gap: 10px;
251
+ width: 100%;
252
+ }
253
+
254
+ .tabs button {
255
+ background-color: #1a1b1e;
256
+ color: #ddd;
257
+ border: none;
258
+ outline: none;
259
+ padding: 10px 10px 10px 10px;
260
+ font-family: "Roboto", sans-serif;
261
+ font-size: 14px;
262
+ font-weight: 600;
263
+ transition: background-color 0.2s ease;
264
+ }
265
+
266
+ .tabs button:hover {
267
+ cursor: pointer;
268
+ background-color: #555;
269
+ }
270
+
271
+ .tabs button.active {
272
+ background-color: #444;
273
+ }
viewer/svelte.config.js β†’ svelte.config.js RENAMED
File without changes
viewer/tsconfig.json β†’ tsconfig.json RENAMED
File without changes
viewer/README.md DELETED
@@ -1,38 +0,0 @@
1
- # create-svelte
2
-
3
- Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
4
-
5
- ## Creating a project
6
-
7
- If you're seeing this, you've probably already done this step. Congrats!
8
-
9
- ```bash
10
- # create a new project in the current directory
11
- npm create svelte@latest
12
-
13
- # create a new project in my-app
14
- npm create svelte@latest my-app
15
- ```
16
-
17
- ## Developing
18
-
19
- Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
20
-
21
- ```bash
22
- npm run dev
23
-
24
- # or start the server and open the app in a new browser tab
25
- npm run dev -- --open
26
- ```
27
-
28
- ## Building
29
-
30
- To create a production version of your app:
31
-
32
- ```bash
33
- npm run build
34
- ```
35
-
36
- You can preview the production build with `npm run preview`.
37
-
38
- > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
viewer/src/lib/data/dataLoader.ts DELETED
@@ -1,10 +0,0 @@
1
- import scenes from "$lib/data/scenes.json";
2
- import models from "$lib/data/models.json";
3
-
4
- export async function getScenes() {
5
- return scenes;
6
- }
7
-
8
- export async function getModels() {
9
- return models;
10
- }
 
 
 
 
 
 
 
 
 
 
 
viewer/src/lib/data/models.json DELETED
@@ -1,137 +0,0 @@
1
- [
2
- {
3
- "slug": "3dgs",
4
- "title": "3D Gaussian Splatting",
5
- "paper": "https://huggingface.co/papers/2308.04079",
6
- "project": "https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/",
7
- "code": "https://github.com/graphdeco-inria/gaussian-splatting",
8
- "spaces": []
9
- },
10
- {
11
- "slug": "sync-dreamer",
12
- "title": "SyncDreamer",
13
- "paper": "https://huggingface.co/papers/2309.03453",
14
- "project": "https://liuyuan-pal.github.io/SyncDreamer/",
15
- "code": "https://github.com/liuyuan-pal/SyncDreamer",
16
- "spaces": []
17
- },
18
- {
19
- "slug": "shap-e",
20
- "title": "shap-e",
21
- "paper": "https://arxiv.org/abs/2305.02463",
22
- "code": "https://github.com/openai/shap-e",
23
- "spaces": [
24
- "https://huggingface.co/spaces/hysts/Shap-E"
25
- ]
26
- },
27
- {
28
- "slug": "dreamfusion",
29
- "title": "DreamFusion",
30
- "paper": "https://arxiv.org/abs/2209.14988",
31
- "project": "https://dreamfusion3d.github.io/index.html",
32
- "spaces": []
33
- },
34
- {
35
- "slug": "mv-dream",
36
- "title": "MVDream",
37
- "paper": "https://huggingface.co/papers/2308.16512",
38
- "project": "https://mv-dream.github.io/",
39
- "code": "https://github.com/bytedance/MVDream",
40
- "spaces": []
41
- },
42
- {
43
- "slug": "wonder3d",
44
- "title": "Wonder3D",
45
- "paper": "https://huggingface.co/papers/2310.15008",
46
- "project": "https://www.xxlong.site/Wonder3D/",
47
- "code": "https://github.com/xxlong0/Wonder3D",
48
- "spaces": []
49
- },
50
- {
51
- "slug": "gaussian-dreamer",
52
- "title": "GaussianDreamer",
53
- "paper": "https://huggingface.co/papers/2310.08529",
54
- "project": "https://taoranyi.com/gaussiandreamer/",
55
- "code": "https://github.com/hustvl/GaussianDreamer",
56
- "spaces": []
57
- },
58
- {
59
- "slug": "dream-gaussian",
60
- "title": "DreamGaussian",
61
- "paper": "https://huggingface.co/papers/2309.16653",
62
- "project": "https://dreamgaussian.github.io/",
63
- "code": "https://github.com/dreamgaussian/dreamgaussian",
64
- "spaces": [
65
- "https://huggingface.co/spaces/jiawei011/dreamgaussian"
66
- ]
67
- },
68
- {
69
- "slug": "panic3d-anime-reconstruction",
70
- "title": "PAniC-3D",
71
- "paper": "https://arxiv.org/abs/2303.14587",
72
- "project": "",
73
- "code": "https://github.com/ShuhongChen/panic3d-anime-reconstruction",
74
- "spaces": []
75
- },
76
- {
77
- "slug": "eg3d",
78
- "title": "EG3D",
79
- "paper": "https://arxiv.org/abs/2112.07945",
80
- "project": "https://nvlabs.github.io/eg3d/",
81
- "code": "https://github.com/NVlabs/eg3d",
82
- "spaces": []
83
- },
84
- {
85
- "slug": "ag3d",
86
- "title": "AG3D",
87
- "paper": "https://zj-dong.github.io/AG3D/assets/paper.pdf",
88
- "project": "https://zj-dong.github.io/AG3D/",
89
- "code": "https://github.com/zj-dong/AG3D",
90
- "spaces": []
91
- },
92
- {
93
- "slug": "scene-dreamer",
94
- "title": "SceneDreamer",
95
- "paper": "https://arxiv.org/abs/2302.01330",
96
- "project": "https://scene-dreamer.github.io/",
97
- "code": "https://github.com/FrozenBurning/SceneDreamer",
98
- "spaces": ["https://huggingface.co/spaces/FrozenBurning/SceneDreamer"]
99
- },
100
- {
101
- "slug": "ldm3d-vr",
102
- "title": "LDM3D-VR",
103
- "paper": "https://arxiv.org/abs/2311.03226",
104
- "spaces": []
105
- },
106
- {
107
- "slug": "d3ga",
108
- "title": "D3GA",
109
- "paper": "https://huggingface.co/papers/2311.08581",
110
- "project": "https://zielon.github.io/d3ga/",
111
- "spaces": []
112
- },
113
- {
114
- "slug": "dmv3d",
115
- "title": "DMV3D",
116
- "paper": "https://huggingface.co/papers/2311.09217",
117
- "project": "https://justimyhxu.github.io/projects/dmv3d/",
118
- "spaces": []
119
- },
120
- {
121
- "slug": "instant3d",
122
- "title": "Instant3D",
123
- "paper": "https://huggingface.co/papers/2311.08403",
124
- "project": "https://instant-3d.github.io/",
125
- "spaces": []
126
- },
127
- {
128
- "slug": "one2345++",
129
- "title": "One-2-3-45++",
130
- "paper": "https://huggingface.co/papers/2311.07885",
131
- "project": "https://one-2-3-45.github.io/",
132
- "code": "https://github.com/One-2-3-45/One-2-3-45",
133
- "spaces": [
134
- "https://huggingface.co/spaces/One-2-3-45/One-2-3-45"
135
- ]
136
- }
137
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
viewer/src/lib/data/scenes.json DELETED
@@ -1,255 +0,0 @@
1
- [
2
- {
3
- "slug": "3dgs-bicycle",
4
- "model": "3dgs",
5
- "title": "Bicycle",
6
- "type": "splat",
7
- "url": "https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/bicycle/bicycle-7k.splat",
8
- "pipeline": [
9
- "Gaussian Splatting"
10
- ]
11
- },
12
- {
13
- "slug": "3dgs-bonsai",
14
- "model": "3dgs",
15
- "title": "Bonsai",
16
- "type": "splat",
17
- "url": "https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/bonsai/bonsai-7k.splat",
18
- "pipeline": [
19
- "Gaussian Splatting"
20
- ]
21
- },
22
- {
23
- "slug": "3dgs-counter",
24
- "model": "3dgs",
25
- "title": "Counter",
26
- "type": "splat",
27
- "url": "https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/counter/counter-7k.splat",
28
- "pipeline": [
29
- "Gaussian Splatting"
30
- ]
31
- },
32
- {
33
- "slug": "3dgs-garden",
34
- "model": "3dgs",
35
- "title": "Garden",
36
- "type": "splat",
37
- "url": "https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/garden/garden-7k.splat",
38
- "pipeline": [
39
- "Gaussian Splatting"
40
- ]
41
- },
42
- {
43
- "slug": "3dgs-kitchen",
44
- "model": "3dgs",
45
- "title": "Kitchen",
46
- "type": "splat",
47
- "url": "https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/kitchen/kitchen-7k.splat",
48
- "pipeline": [
49
- "Gaussian Splatting"
50
- ]
51
- },
52
- {
53
- "slug": "3dgs-playroom",
54
- "model": "3dgs",
55
- "title": "Playroom",
56
- "type": "splat",
57
- "url": "https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/playroom/playroom-7k.splat",
58
- "pipeline": [
59
- "Gaussian Splatting"
60
- ]
61
- },
62
- {
63
- "slug": "3dgs-room",
64
- "model": "3dgs",
65
- "title": "Room",
66
- "type": "splat",
67
- "url": "https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/room/room-7k.splat",
68
- "pipeline": [
69
- "Gaussian Splatting"
70
- ]
71
- },
72
- {
73
- "slug": "3dgs-stump",
74
- "model": "3dgs",
75
- "title": "Stump",
76
- "type": "splat",
77
- "url": "https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/stump/stump-7k.splat",
78
- "pipeline": [
79
- "Gaussian Splatting"
80
- ]
81
- },
82
- {
83
- "slug": "sync-dreamer-armor",
84
- "model": "sync-dreamer",
85
- "title": "armor",
86
- "type": "mesh",
87
- "url": "https://huggingface.co/datasets/dylanebert/igf-results/resolve/main/sync-dreamer/armor.glb",
88
- "pipeline": [
89
- "Multi-view Diffusion",
90
- "NeuS"
91
- ]
92
- },
93
- {
94
- "slug": "sync-dreamer-deer",
95
- "model": "sync-dreamer",
96
- "title": "deer",
97
- "type": "mesh",
98
- "url": "https://huggingface.co/datasets/dylanebert/igf-results/resolve/main/sync-dreamer/deer.glb",
99
- "pipeline": [
100
- "Multi-view Diffusion",
101
- "NeuS"
102
- ]
103
- },
104
- {
105
- "slug": "sync-dreamer-drum",
106
- "model": "sync-dreamer",
107
- "title": "drum",
108
- "type": "mesh",
109
- "url": "https://huggingface.co/datasets/dylanebert/igf-results/resolve/main/sync-dreamer/drum.glb",
110
- "pipeline": [
111
- "Multi-view Diffusion",
112
- "NeuS"
113
- ]
114
- },
115
- {
116
- "slug": "sync-dreamer-forest",
117
- "model": "sync-dreamer",
118
- "title": "forest",
119
- "type": "mesh",
120
- "url": "https://huggingface.co/datasets/dylanebert/igf-results/resolve/main/sync-dreamer/forest.glb",
121
- "pipeline": [
122
- "Multi-view Diffusion",
123
- "NeuS"
124
- ]
125
- },
126
- {
127
- "slug": "sync-dreamer-monkey",
128
- "model": "sync-dreamer",
129
- "title": "monkey",
130
- "type": "mesh",
131
- "url": "https://huggingface.co/datasets/dylanebert/igf-results/resolve/main/sync-dreamer/monkey.glb",
132
- "pipeline": [
133
- "Multi-view Diffusion",
134
- "NeuS"
135
- ]
136
- },
137
- {
138
- "slug": "sync-dreamer-poro",
139
- "model": "sync-dreamer",
140
- "title": "poro",
141
- "type": "mesh",
142
- "url": "https://huggingface.co/datasets/dylanebert/igf-results/resolve/main/sync-dreamer/poro.glb",
143
- "pipeline": [
144
- "Multi-view Diffusion",
145
- "NeuS"
146
- ]
147
- },
148
- {
149
- "slug": "sync-dreamer-train",
150
- "model": "sync-dreamer",
151
- "title": "train",
152
- "type": "mesh",
153
- "url": "https://huggingface.co/datasets/dylanebert/igf-results/resolve/main/sync-dreamer/train.glb",
154
- "pipeline": [
155
- "Multi-view Diffusion",
156
- "NeuS"
157
- ]
158
- },
159
- {
160
- "slug": "dreamfusion-sweaterfrog",
161
- "model": "dreamfusion",
162
- "title": "sweater frog",
163
- "type": "mesh",
164
- "url": "https://dreamfusion3d.github.io/assets/meshes2/sweaterfrog_1step.glb",
165
- "prompt": "frog wearing a sweater",
166
- "pipeline": [
167
- "Multi-view Diffusion",
168
- "NeuS"
169
- ]
170
- },
171
- {
172
- "slug": "dreamfusion-chick",
173
- "model": "dreamfusion",
174
- "title": "chick",
175
- "type": "mesh",
176
- "url": "https://dreamfusion3d.github.io/assets/meshes2/44855521_sept18_hero16_047a_DSLR_photo_of_an_eggshell_broken_in_two_with_an_adorable_chick_standing_next_to_it_1step.glb",
177
- "prompt": "eggshell broken in two with an adorable chick standing next to it",
178
- "pipeline": [
179
- "Multi-view Diffusion",
180
- "Marching Cubes"
181
- ]
182
- },
183
- {
184
- "slug": "dreamfusion-ghost",
185
- "model": "dreamfusion",
186
- "title": "ghost",
187
- "type": "mesh",
188
- "url": "https://dreamfusion3d.github.io/assets/meshes2/44934035_sept18_hero19_113a_DSLR_photo_of_a_ghost_eating_a_hamburger_1step.glb",
189
- "prompt": "ghost eating a hamburger",
190
- "pipeline": [
191
- "Multi-view Diffusion",
192
- "Marching Cubes"
193
- ]
194
- },
195
- {
196
- "slug": "dreamfusion-pig",
197
- "model": "dreamfusion",
198
- "title": "pig",
199
- "type": "mesh",
200
- "url": "https://dreamfusion3d.github.io/assets/meshes2/44844973_sept18_hero14_076a_pig_wearing_a_backpack_1step.glb",
201
- "prompt": "a pig wearing a backback",
202
- "pipeline": [
203
- "Multi-view Diffusion",
204
- "Marching Cubes"
205
- ]
206
- },
207
- {
208
- "slug": "dreamfusion-eagle",
209
- "model": "dreamfusion",
210
- "title": "eagle",
211
- "type": "mesh",
212
- "url": "https://dreamfusion3d.github.io/assets/meshes2/44853505_sept18_hero15_145a_bald_eagle_carved_out_of_wood_1step.glb",
213
- "prompt": "a bald eagle carved out of wood",
214
- "pipeline": [
215
- "Multi-view Diffusion",
216
- "Marching Cubes"
217
- ]
218
- },
219
- {
220
- "slug": "dreamfusion-crab",
221
- "model": "dreamfusion",
222
- "title": "crab",
223
- "type": "mesh",
224
- "url": "https://dreamfusion3d.github.io/assets/meshes2/44930695_sept18_hero18_103a_crab,_low_poly_1step.glb",
225
- "prompt": "a crab, low poly",
226
- "pipeline": [
227
- "Multi-view Diffusion",
228
- "Marching Cubes"
229
- ]
230
- },
231
- {
232
- "slug": "dreamfusion-lemur",
233
- "model": "dreamfusion",
234
- "title": "lemur",
235
- "type": "mesh",
236
- "url": "https://dreamfusion3d.github.io/assets/meshes2/44853505_sept18_hero15_124a_lemur_taking_notes_in_a_journal_1step.glb",
237
- "prompt": "a lemur taking notes in a journal",
238
- "pipeline": [
239
- "Multi-view Diffusion",
240
- "Marching Cubes"
241
- ]
242
- },
243
- {
244
- "slug": "dreamfusion-corgi",
245
- "model": "dreamfusion",
246
- "title": "corgi",
247
- "type": "mesh",
248
- "url": "https://dreamfusion3d.github.io/assets/meshes2/44960400_sept18_hero20peter_117a_plush_toy_of_a_corgi_nurse_1step.glb",
249
- "prompt": "a plush toy of a corgi nurse",
250
- "pipeline": [
251
- "Multi-view Diffusion",
252
- "Marching Cubes"
253
- ]
254
- }
255
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
viewer/src/lib/placeholder.png DELETED
Binary file (4.03 kB)
 
viewer/src/routes/+page.svelte DELETED
@@ -1,57 +0,0 @@
1
- <script lang="ts">
2
- import { activeTab } from "./store.js";
3
- import ModelsView from "./components/ModelsView.svelte";
4
- import ScenesView from "./components/ScenesView.svelte";
5
-
6
- function goHome() {
7
- window.location.href = "/";
8
- }
9
- </script>
10
-
11
- <div class="container">
12
- <div on:pointerdown={goHome} class="banner">
13
- <h1>IGF</h1>
14
- <p>Comparative 3D Research Browser</p>
15
- </div>
16
- <div class="tabs">
17
- <button on:click={() => activeTab.set("Models")} class={$activeTab === "Models" ? "active" : ""}>Models</button>
18
- <button on:click={() => activeTab.set("Scenes")} class={$activeTab === "Scenes" ? "active" : ""}>Scenes</button>
19
- </div>
20
- {#if $activeTab === "Models"}
21
- <ModelsView />
22
- {:else}
23
- <ScenesView />
24
- {/if}
25
- </div>
26
-
27
- <style>
28
- .tabs {
29
- display: flex;
30
- justify-content: left;
31
- margin-top: 10px;
32
- margin-bottom: 10px;
33
- gap: 10px;
34
- width: 100%;
35
- }
36
-
37
- .tabs button {
38
- background-color: #1a1b1e;
39
- color: #ddd;
40
- border: none;
41
- outline: none;
42
- padding: 10px 10px 10px 10px;
43
- font-family: "Roboto", sans-serif;
44
- font-size: 14px;
45
- font-weight: 600;
46
- transition: background-color 0.2s ease;
47
- }
48
-
49
- .tabs button:hover {
50
- cursor: pointer;
51
- background-color: #555;
52
- }
53
-
54
- .tabs button.active {
55
- background-color: #444;
56
- }
57
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
viewer/src/routes/components/ModelsView.svelte DELETED
@@ -1,42 +0,0 @@
1
- <script lang="ts">
2
- import { onMount } from "svelte";
3
- import { getModels, getScenes } from "$lib/data/dataLoader";
4
- import placeholderImage from "$lib/placeholder.png";
5
-
6
- let models: any[] = [];
7
- let sceneMap: any = {};
8
-
9
- onMount(async () => {
10
- models = await getModels();
11
- const scenes = await getScenes();
12
- for (let model of models) {
13
- for (let scene of scenes) {
14
- if (scene.model === model.slug) {
15
- sceneMap[model.slug] = scene.slug;
16
- break;
17
- }
18
- }
19
- }
20
- });
21
-
22
- function handleImageError(event: Event) {
23
- const image = event.currentTarget as HTMLImageElement;
24
- image.src = placeholderImage;
25
- }
26
- </script>
27
-
28
- <div class="grid">
29
- {#each models as model}
30
- {#if sceneMap[model.slug] !== undefined}
31
- <a href={`/models/${model.slug}`} class="grid-item">
32
- <img
33
- src={`/thumbnails/${sceneMap[model.slug]}.png`}
34
- alt={model.title}
35
- class="thumbnail"
36
- on:error={(event) => handleImageError(event)}
37
- />
38
- <div class="title">{model.title}</div>
39
- </a>
40
- {/if}
41
- {/each}
42
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
viewer/src/routes/components/ScenesView.svelte DELETED
@@ -1,30 +0,0 @@
1
- <script lang="ts">
2
- import { onMount } from "svelte";
3
- import { getScenes } from "$lib/data/dataLoader";
4
- import placeholderImage from "$lib/placeholder.png";
5
-
6
- let scenes: any[] = [];
7
-
8
- onMount(async () => {
9
- scenes = await getScenes();
10
- });
11
-
12
- function handleImageError(event: Event) {
13
- const image = event.currentTarget as HTMLImageElement;
14
- image.src = placeholderImage;
15
- }
16
- </script>
17
-
18
- <div class="grid">
19
- {#each scenes as scene}
20
- <a href={`/viewer/${scene.slug}`} class="grid-item">
21
- <img
22
- src={`/thumbnails/${scene.slug}.png`}
23
- alt={scene.title}
24
- class="thumbnail"
25
- on:error={(event) => handleImageError(event)}
26
- />
27
- <div class="title">{scene.title}</div>
28
- </a>
29
- {/each}
30
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
viewer/src/routes/models/[slug]/+page.server.ts DELETED
@@ -1,14 +0,0 @@
1
- import { getModels, getScenes } from "$lib/data/dataLoader";
2
-
3
- export async function load({ params }) {
4
- const models = await getModels();
5
- const scenes = await getScenes();
6
-
7
- const model = models.find((model: any) => model.slug === params.slug);
8
- const modelScenes = scenes.filter((scene: any) => scene.model === params.slug);
9
-
10
- return {
11
- model: model,
12
- scenes: modelScenes,
13
- };
14
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
viewer/src/routes/models/[slug]/+page.svelte DELETED
@@ -1,182 +0,0 @@
1
- <script lang="ts">
2
- import placeholderImage from "$lib/placeholder.png";
3
-
4
- export let data: {
5
- model: {
6
- title: string;
7
- paper: string;
8
- project: string;
9
- code: string;
10
- };
11
- scenes: {
12
- slug: string;
13
- title: string;
14
- }[];
15
- };
16
-
17
- function handleImageError(event: Event) {
18
- const image = event.currentTarget as HTMLImageElement;
19
- image.src = placeholderImage;
20
- }
21
-
22
- function goHome() {
23
- window.location.href = "/";
24
- }
25
- </script>
26
-
27
- <div class="container">
28
- <div on:pointerdown={goHome} class="banner">
29
- <h1>IGF</h1>
30
- <p>Comparative 3D Research Browser</p>
31
- </div>
32
- <div class="header">{data.model.title}</div>
33
- <div class="model-container">
34
- <div class="model-info">
35
- <p class="model-header">Info</p>
36
- <table class="table">
37
- {#if data.model.paper}
38
- <tr>
39
- <td>Paper</td>
40
- <td><a href={data.model.paper} target="_blank">{data.model.paper}</a></td>
41
- </tr>
42
- {/if}
43
- {#if data.model.project}
44
- <tr>
45
- <td>Project</td>
46
- <td><a href={data.model.project} target="_blank">{data.model.project}</a></td>
47
- </tr>
48
- {/if}
49
- {#if data.model.code}
50
- <tr>
51
- <td>Code</td>
52
- <td><a href={data.model.code} target="_blank">{data.model.code}</a></td>
53
- </tr>
54
- {/if}
55
- </table>
56
- </div>
57
- <div class="grid-container">
58
- {#if data.scenes.length > 0}
59
- <div class="grid">
60
- {#each data.scenes as scene}
61
- <a href={`/viewer/${scene.slug}`} class="grid-item">
62
- <img
63
- src={`/thumbnails/${scene.slug}.png`}
64
- alt={scene.title}
65
- class="thumbnail"
66
- on:error={(event) => handleImageError(event)}
67
- />
68
- <div class="title">{scene.title}</div>
69
- </a>
70
- {/each}
71
- </div>
72
- {:else}
73
- <div class="grid">
74
- <div class="warning">No scenes found</div>
75
- </div>
76
- {/if}
77
- </div>
78
- </div>
79
- </div>
80
-
81
- <style>
82
- .model-header {
83
- padding: 10px;
84
- font-size: 16px;
85
- color: #aaa;
86
- margin: 0;
87
- }
88
-
89
- .model-info {
90
- border: 1px solid #333;
91
- box-sizing: border-box;
92
- width: 100%;
93
- margin: 0;
94
-
95
- @media (min-width: 576px) {
96
- width: 384px;
97
- }
98
- }
99
-
100
- .table {
101
- table-layout: fixed;
102
- width: 100%;
103
- margin: 0;
104
- padding: 0;
105
- border-collapse: collapse;
106
- }
107
-
108
- .table td {
109
- width: 100%;
110
- margin: 0;
111
- padding: 10px;
112
- border-top: 1px solid #333;
113
- white-space: nowrap;
114
- }
115
-
116
- .table td:first-child {
117
- width: 128px;
118
- background-color: #222;
119
- border-right: 1px solid #333;
120
- font-size: 14px;
121
- font-weight: bold;
122
- color: #aaa;
123
- }
124
-
125
- .table td:last-child {
126
- width: 100%;
127
- font-size: 14px;
128
- overflow: hidden;
129
- }
130
-
131
- .table a {
132
- color: #6d90b6;
133
- }
134
-
135
- .model-container {
136
- display: flex;
137
- flex-wrap: wrap;
138
- align-items: flex-start;
139
- }
140
-
141
- .grid-container {
142
- flex: 1;
143
- display: flex;
144
- flex-wrap: wrap;
145
- justify-content: center;
146
- }
147
-
148
- .grid {
149
- margin-top: 10px;
150
- margin-left: 0;
151
-
152
- @media (min-width: 576px) {
153
- margin-top: 0;
154
- margin-left: 10px;
155
- }
156
- }
157
-
158
- .grid-item {
159
- @media (min-width: 576px) {
160
- width: 100%;
161
- }
162
-
163
- @media (min-width: 768px) {
164
- width: calc(50% - 10px);
165
- }
166
-
167
- @media (min-width: 992px) {
168
- width: calc(33.333% - 10px);
169
- }
170
-
171
- @media (min-width: 1200px) {
172
- width: calc(25% - 10px);
173
- }
174
- }
175
-
176
- .warning {
177
- width: 100%;
178
- margin-top: 20px;
179
- text-align: center;
180
- color: #aaa;
181
- }
182
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
viewer/src/routes/viewer/[slug]/+page.server.ts DELETED
@@ -1,17 +0,0 @@
1
- import { error } from "@sveltejs/kit";
2
- import { getModels, getScenes } from "$lib/data/dataLoader";
3
-
4
- export async function load({ params }) {
5
- const models = await getModels();
6
- const scenes = await getScenes();
7
-
8
- const scene = scenes.find((scene: any) => scene.slug === params.slug);
9
- const model = models.find((model: any) => model.slug === scene!.model);
10
-
11
- if (!scene) throw error(404);
12
-
13
- return {
14
- scene: scene,
15
- model: model,
16
- };
17
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
viewer/src/routes/viewer/[slug]/+page.svelte DELETED
@@ -1,374 +0,0 @@
1
- <script lang="ts">
2
- import { onMount, onDestroy } from "svelte";
3
- import type { IViewer } from "./IViewer";
4
- import { BabylonViewer } from "./BabylonViewer";
5
- import { SplatViewer } from "./SplatViewer";
6
-
7
- export let data: {
8
- scene: {
9
- title: string;
10
- model: string;
11
- type: string;
12
- url: string;
13
- prompt: string;
14
- pipeline: string[];
15
- };
16
- model: {
17
- title: string;
18
- };
19
- };
20
-
21
- let stats: { name: string; value: any }[] = [];
22
-
23
- let viewer: IViewer;
24
- let overlay: HTMLDivElement;
25
- let container: HTMLDivElement;
26
- let hudToggleBtn: HTMLButtonElement;
27
- let canvas: HTMLCanvasElement;
28
- let loadingBarFill: HTMLDivElement;
29
- let collapsed = false;
30
-
31
- onMount(initViewer);
32
- onDestroy(destroyViewer);
33
-
34
- async function initViewer() {
35
- document.body.classList.add("viewer");
36
- if (data.scene.type == "mesh") {
37
- viewer = new BabylonViewer(canvas);
38
- } else if (data.scene.type == "splat") {
39
- viewer = new SplatViewer(canvas);
40
- } else {
41
- console.error(`Unsupported scene type: ${data.scene.type}`);
42
- }
43
- handleMobileView();
44
- await loadScene(data.scene.url);
45
- window.addEventListener("resize", () => {
46
- updateCanvasSize();
47
- });
48
- updateStats();
49
- setInterval(updateStats, 1000);
50
- }
51
-
52
- function destroyViewer() {
53
- document.body.classList.remove("viewer");
54
- viewer.dispose();
55
- }
56
-
57
- function handleMobileView() {
58
- const isMobile = window.innerWidth < 768;
59
- if (isMobile) toggleHUD();
60
- }
61
-
62
- function toggleHUD() {
63
- collapsed = !collapsed;
64
- hudToggleBtn.textContent = collapsed ? ")" : "(";
65
- if (collapsed) {
66
- container.classList.remove("hud-expanded");
67
- } else {
68
- container.classList.add("hud-expanded");
69
- }
70
- }
71
-
72
- function setRenderMode(event: PointerEvent) {
73
- const babylonViewer = viewer as BabylonViewer;
74
- if (!babylonViewer) {
75
- console.error("Can only set render mode for BabylonViewer");
76
- return;
77
- }
78
-
79
- document.querySelectorAll(".mode-item").forEach((item) => {
80
- item.classList.remove("active");
81
- });
82
-
83
- const modeItem = event.currentTarget as HTMLElement;
84
- modeItem.classList.add("active");
85
-
86
- const mode = modeItem.dataset.mode as string;
87
- babylonViewer.setRenderMode(mode);
88
- }
89
-
90
- async function loadScene(url: string) {
91
- overlay.style.display = "flex";
92
- await viewer.loadScene(url, (progress) => {
93
- loadingBarFill.style.width = `${progress * 100}%`;
94
- });
95
- updateCanvasSize();
96
- overlay.style.display = "none";
97
- }
98
-
99
- function updateCanvasSize() {
100
- if (!canvas || !container) return;
101
- canvas.width = container.clientWidth;
102
- canvas.height = container.clientHeight;
103
- }
104
-
105
- async function capture() {
106
- const data = await viewer.capture();
107
- if (!data) {
108
- console.error("Failed to capture screenshot");
109
- return;
110
- }
111
- const a = document.createElement("a");
112
- a.href = data;
113
- a.download = "screenshot.png";
114
- a.click();
115
- }
116
-
117
- function updateStats() {
118
- stats = viewer.getStats();
119
- }
120
-
121
- function exit() {
122
- window.history.back();
123
- }
124
- </script>
125
-
126
- <div bind:this={container} class="canvas-container hud-expanded">
127
- <div bind:this={overlay} class="loading-overlay">
128
- <div class="loading-bar">
129
- <div bind:this={loadingBarFill} class="loading-bar-fill" />
130
- </div>
131
- </div>
132
- <canvas bind:this={canvas} width="512" height="512" />
133
- <div class="exit-button" on:pointerdown={exit}>x</div>
134
- <div class="hud" class:collapsed>
135
- <button bind:this={hudToggleBtn} on:click={toggleHUD} class="hud-toggle-btn">(</button>
136
- <div class="section">
137
- <div class="title">{data.scene.title}</div>
138
- </div>
139
- <div class="section">
140
- <div class="section-title">Model</div>
141
- <div class="info-panel">
142
- {#if data.scene.model}
143
- <a href={`/models/${data.scene.model}`} class="section-label">{data.model.title}</a>
144
- {#if data.scene.pipeline}
145
- <ol class="pipeline">
146
- {#each data.scene.pipeline as step}
147
- <li>{step}</li>
148
- {/each}
149
- </ol>
150
- {/if}
151
- {:else}
152
- <div class="section-label">None</div>
153
- {/if}
154
- </div>
155
- </div>
156
- {#if data.scene.prompt}
157
- <div class="section">
158
- <div class="section-title">Prompt</div>
159
- <div class="info-panel">
160
- <div class="section-label">{data.scene.prompt}</div>
161
- </div>
162
- </div>
163
- {/if}
164
- {#if stats.length > 0}
165
- <div class="section">
166
- <div class="section-title">Stats</div>
167
- <div class="info-panel">
168
- {#each stats as stat}
169
- <div>{stat.name}: {stat.value}</div>
170
- {/each}
171
- </div>
172
- </div>
173
- {/if}
174
- {#if data.scene.type === "mesh"}
175
- <div class="section">
176
- <div class="section-title">Render Mode</div>
177
- <div class="button-group mode-list">
178
- <div on:pointerdown={setRenderMode} class="hud-button mode-item active" data-mode="rendered">
179
- Rendered
180
- </div>
181
- <div on:pointerdown={setRenderMode} class="hud-button mode-item" data-mode="wireframe">
182
- Wireframe
183
- </div>
184
- </div>
185
- </div>
186
- {/if}
187
- <div class="section">
188
- <div class="section-title">Actions</div>
189
- <div class="button-group">
190
- <div class="hud-button" on:pointerdown={capture}>Capture</div>
191
- </div>
192
- </div>
193
- </div>
194
- </div>
195
-
196
- <style>
197
- .canvas-container {
198
- position: relative;
199
- box-sizing: border-box;
200
- transition: padding-left 0.2s ease;
201
- display: flex;
202
- flex-direction: column;
203
- justify-content: center;
204
- align-items: center;
205
- width: 100wh;
206
- height: 100vh;
207
- overflow: hidden;
208
- }
209
-
210
- .canvas-container.hud-expanded {
211
- padding-left: 256px;
212
- }
213
-
214
- .loading-overlay {
215
- position: absolute;
216
- top: 0;
217
- left: 0;
218
- width: 100%;
219
- height: 100%;
220
- background-color: #1a1b1e;
221
- display: flex;
222
- flex-direction: column;
223
- justify-content: center;
224
- align-items: center;
225
- z-index: 100;
226
- gap: 10px;
227
- }
228
-
229
- .loading-overlay::before {
230
- content: "Loading...";
231
- color: white;
232
- font-size: 16px;
233
- }
234
-
235
- .loading-bar {
236
- position: relative;
237
- width: 256px;
238
- height: 20px;
239
- border: 2px solid #aaa;
240
- background-color: #1a1b1e;
241
- }
242
-
243
- .loading-bar-fill {
244
- position: absolute;
245
- top: 0;
246
- left: 0;
247
- width: 0%;
248
- height: 100%;
249
- background-color: #555;
250
- transition: width 0.2s ease;
251
- }
252
-
253
- canvas {
254
- max-width: 100%;
255
- max-height: 100%;
256
- }
257
-
258
- canvas:focus {
259
- outline: none;
260
- }
261
-
262
- .hud {
263
- position: absolute;
264
- top: 0;
265
- left: 0;
266
- width: 286px;
267
- height: 100%;
268
- box-sizing: border-box;
269
- font-size: 14px;
270
- background-color: #1a1b1e;
271
- box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.3);
272
- transition: transform 0.2s ease;
273
- margin: 0;
274
- padding: 0 30px 0 0;
275
- overflow-x: hidden;
276
- overflow-y: auto;
277
-
278
- @media (max-width: 768px) {
279
- width: 100%;
280
- }
281
- }
282
-
283
- .hud-toggle-btn {
284
- position: absolute;
285
- right: 0;
286
- top: 50%;
287
- transform: translateY(-50%);
288
- background-color: #1a1b1e;
289
- border: none;
290
- color: #aaa;
291
- font-size: 16px;
292
- cursor: pointer;
293
- outline: none;
294
- width: 29px;
295
- height: 100%;
296
- box-sizing: border-box;
297
- transition: background-color 0.2s ease;
298
- border-left: 1px solid #444;
299
- }
300
-
301
- .hud-toggle-btn:hover {
302
- background-color: #444;
303
- }
304
-
305
- .hud.collapsed {
306
- transform: translateX(calc(-100% + 30px));
307
- }
308
-
309
- .section {
310
- width: 100%;
311
- padding: 10px;
312
- box-sizing: border-box;
313
- }
314
-
315
- .title {
316
- font-size: 16px;
317
- color: #aaa;
318
- font-weight: bold;
319
- padding: 4px;
320
- padding-top: 10px;
321
- }
322
-
323
- .section-title {
324
- font-size: 11px;
325
- font-weight: light;
326
- text-transform: uppercase;
327
- color: #aaa;
328
- width: 100%;
329
- padding: 4px;
330
- }
331
-
332
- .section-label {
333
- font-size: 14px;
334
- color: #ddd;
335
- }
336
-
337
- .info-panel {
338
- padding: 6px 10px 0px 10px;
339
- color: #ddd;
340
- }
341
-
342
- .pipeline {
343
- margin: 0;
344
- padding: 6px 10px 0px 20px;
345
- }
346
-
347
- .button-group {
348
- border: none;
349
- background-color: transparent;
350
- box-sizing: border-box;
351
- }
352
-
353
- .hud-button {
354
- padding: 10px 15px;
355
- cursor: pointer;
356
- background-color: #1a1b1e;
357
- border-bottom: 1px solid #444;
358
- transition: background-color 0.2s ease;
359
- box-sizing: border-box;
360
- }
361
-
362
- .hud-button:last-child {
363
- border-bottom: none;
364
- }
365
-
366
- .hud-button:hover {
367
- background-color: #555;
368
- }
369
-
370
- .hud-button.active {
371
- background-color: #444;
372
- color: white;
373
- }
374
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
viewer/static/thumbnails/3dgs-bicycle.png DELETED
Binary file (628 kB)
 
viewer/static/thumbnails/3dgs-bonsai.png DELETED
Binary file (578 kB)
 
viewer/static/thumbnails/3dgs-counter.png DELETED
Binary file (543 kB)
 
viewer/static/thumbnails/3dgs-flowers.png DELETED
Binary file (599 kB)
 
viewer/static/thumbnails/3dgs-garden.png DELETED
Binary file (659 kB)
 
viewer/static/thumbnails/3dgs-kitchen.png DELETED
Binary file (610 kB)
 
viewer/static/thumbnails/3dgs-playroom.png DELETED
Binary file (502 kB)
 
viewer/static/thumbnails/3dgs-room.png DELETED
Binary file (522 kB)
 
viewer/static/thumbnails/3dgs-stump.png DELETED
Binary file (682 kB)
 
viewer/static/thumbnails/3dgs-treehill.png DELETED
Binary file (546 kB)
 
viewer/static/thumbnails/dreamfusion-chick.png DELETED
Binary file (65.6 kB)
 
viewer/static/thumbnails/dreamfusion-corgi.png DELETED
Binary file (66.4 kB)
 
viewer/static/thumbnails/dreamfusion-crab.png DELETED
Binary file (34.3 kB)
 
viewer/static/thumbnails/dreamfusion-eagle.png DELETED
Binary file (73.5 kB)
 
viewer/static/thumbnails/dreamfusion-ghost.png DELETED
Binary file (73 kB)
 
viewer/static/thumbnails/dreamfusion-lemur.png DELETED
Binary file (44 kB)
 
viewer/static/thumbnails/dreamfusion-pig.png DELETED
Binary file (61.6 kB)