Juanfa commited on
Commit
d4e2238
·
verified ·
1 Parent(s): 47c14e0

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +354 -19
index.html CHANGED
@@ -1,19 +1,354 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Immersive 3D Gaussian Splat Viewer</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <style>
9
+ body { margin: 0; overflow: hidden; background-color: #050505; font-family: 'Inter', sans-serif; }
10
+ canvas { display: block; width: 100vw; height: 100vh; outline: none; }
11
+
12
+ /* Custom Scrollbar for the scene list */
13
+ .scroller::-webkit-scrollbar { height: 6px; }
14
+ .scroller::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.05); }
15
+ .scroller::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2); border-radius: 10px; }
16
+ .scroller::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.4); }
17
+
18
+ /* Glassmorphism utilities */
19
+ .glass {
20
+ background: rgba(20, 20, 20, 0.6);
21
+ backdrop-filter: blur(12px);
22
+ -webkit-backdrop-filter: blur(12px);
23
+ border: 1px solid rgba(255, 255, 255, 0.1);
24
+ }
25
+
26
+ .loading-overlay {
27
+ position: fixed; top: 0; left: 0; width: 100%; height: 100%;
28
+ background: #000; z-index: 50; display: flex; flex-direction: column;
29
+ align-items: center; justify-content: center; transition: opacity 0.5s ease;
30
+ }
31
+
32
+ .loader-bar {
33
+ width: 200px; height: 2px; background: #333; margin-top: 20px; position: relative; overflow: hidden;
34
+ }
35
+ .loader-progress {
36
+ position: absolute; top: 0; left: 0; height: 100%; width: 0%; background: #00d2ff; transition: width 0.2s;
37
+ }
38
+ </style>
39
+ <!-- Import Map for Three.js and Addons -->
40
+ <script type="importmap">
41
+ {
42
+ "imports": {
43
+ "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
44
+ "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
45
+ }
46
+ }
47
+ </script>
48
+ </head>
49
+ <body class="text-white antialiased selection:bg-cyan-500 selection:text-black">
50
+
51
+ <!-- Loading Screen -->
52
+ <div id="loader" class="loading-overlay">
53
+ <div class="text-2xl font-light tracking-widest uppercase text-cyan-400">Initializing Neural Renderer</div>
54
+ <div class="text-xs text-gray-500 mt-2">Loading Gaussian Splats...</div>
55
+ <div class="loader-bar"><div id="progress-bar" class="loader-progress"></div></div>
56
+ </div>
57
+
58
+ <!-- Main UI Overlay -->
59
+ <div class="absolute inset-0 pointer-events-none flex flex-col justify-between z-10 p-6">
60
+
61
+ <!-- Header -->
62
+ <header class="flex justify-between items-start pointer-events-auto">
63
+ <div>
64
+ <h1 class="text-3xl font-bold tracking-tighter bg-clip-text text-transparent bg-gradient-to-r from-cyan-400 to-blue-600">
65
+ SPLAT<span class="text-white">VIEWER</span>
66
+ </h1>
67
+ <p class="text-xs text-gray-400 mt-1 max-w-xs">
68
+ Explore photorealistic 3D captures using Gaussian Splatting technology.
69
+ <br>Drag to rotate • Scroll to zoom • Right-click to pan.
70
+ </p>
71
+ </div>
72
+ <div class="glass px-4 py-2 rounded-full flex items-center gap-2">
73
+ <div class="w-2 h-2 rounded-full bg-green-500 animate-pulse"></div>
74
+ <span class="text-xs font-mono text-gray-300">LIVE RENDER</span>
75
+ </div>
76
+ </header>
77
+
78
+ <!-- Controls & Info (Right Side) -->
79
+ <div class="absolute top-1/2 right-6 transform -translate-y-1/2 flex flex-col gap-3 pointer-events-auto">
80
+ <button id="btn-reset" class="glass w-10 h-10 rounded-full flex items-center justify-center hover:bg-white/10 transition text-cyan-400" title="Reset Camera">
81
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 12"/></svg>
82
+ </button>
83
+ <button id="btn-rotate" class="glass w-10 h-10 rounded-full flex items-center justify-center hover:bg-white/10 transition text-cyan-400 bg-white/5" title="Toggle Auto-Rotation">
84
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
85
+ </button>
86
+ <button id="btn-wireframe" class="glass w-10 h-10 rounded-full flex items-center justify-center hover:bg-white/10 transition text-cyan-400" title="Toggle Debug View">
87
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
88
+ </button>
89
+ </div>
90
+
91
+ <!-- Scene Selector (Bottom) -->
92
+ <div class="w-full max-w-4xl mx-auto pointer-events-auto">
93
+ <div class="glass p-4 rounded-2xl">
94
+ <div class="flex justify-between items-center mb-3">
95
+ <h3 class="text-sm font-semibold text-gray-300 uppercase tracking-wider">Select World</h3>
96
+ <span id="scene-desc" class="text-xs text-cyan-400 font-mono">Loading...</span>
97
+ </div>
98
+ <div class="scroller flex gap-4 overflow-x-auto pb-2" id="scene-list">
99
+ <!-- Scene items injected via JS -->
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ <!-- Canvas Container -->
106
+ <div id="canvas-container" class="absolute inset-0 z-0 bg-black"></div>
107
+
108
+ <!-- Application Logic -->
109
+ <script type="module">
110
+ import * as THREE from 'three';
111
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
112
+ import { GaussianSplattingMesh } from 'three/addons/objects/GaussianSplattingMesh.js';
113
+
114
+ // --- Configuration ---
115
+ // Using publicly available demo splats from Three.js examples or similar CDNs
116
+ const SCENES = [
117
+ {
118
+ id: 'garden',
119
+ name: 'The Garden',
120
+ description: 'A high-detail capture of a lush garden.',
121
+ // Using a standard demo file from three.js repo (approx 30MB)
122
+ url: 'https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/gaussian-splatting/garden.ply',
123
+ cameraPos: { x: 0, y: 1, z: 3 },
124
+ scale: 1,
125
+ splatAlphaRemovalThreshold: 0.1
126
+ },
127
+ {
128
+ id: 'bonsai',
129
+ name: 'Bonsai Tree',
130
+ description: 'Intricate details of a miniature tree.',
131
+ url: 'https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/gaussian-splatting/bonsai.ply',
132
+ cameraPos: { x: 1, y: 0.5, z: 1.5 },
133
+ scale: 2,
134
+ splatAlphaRemovalThreshold: 0.05
135
+ },
136
+ {
137
+ id: 'stump',
138
+ name: 'Old Stump',
139
+ description: 'Weathered wood textures in 3D.',
140
+ url: 'https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/gaussian-splatting/stump.ply',
141
+ cameraPos: { x: 2, y: 1, z: 2 },
142
+ scale: 1.5,
143
+ splatAlphaRemovalThreshold: 0.1
144
+ },
145
+ {
146
+ id: 'bicycle',
147
+ name: 'Vintage Bike',
148
+ description: 'A classic bicycle reconstruction.',
149
+ url: 'https://raw.githubusercontent.com/mrdoob/three.js/master/examples/models/gltf/gaussian-splatting/bicycle.ply',
150
+ cameraPos: { x: 1, y: 0.5, z: 2 },
151
+ scale: 1.2,
152
+ splatAlphaRemovalThreshold: 0.05
153
+ }
154
+ ];
155
+
156
+ // --- State ---
157
+ let camera, scene, renderer, controls;
158
+ let currentMesh = null;
159
+ let isAutoRotating = true;
160
+ let targetSceneIndex = 0;
161
+
162
+ // --- DOM Elements ---
163
+ const container = document.getElementById('canvas-container');
164
+ const loader = document.getElementById('loader');
165
+ const progressBar = document.getElementById('progress-bar');
166
+ const sceneListEl = document.getElementById('scene-list');
167
+ const sceneDescEl = document.getElementById('scene-desc');
168
+ const btnReset = document.getElementById('btn-reset');
169
+ const btnRotate = document.getElementById('btn-rotate');
170
+ const btnWireframe = document.getElementById('btn-wireframe');
171
+
172
+ // --- Initialization ---
173
+ init();
174
+ buildUI();
175
+ loadScene(0);
176
+
177
+ function init() {
178
+ // Scene
179
+ scene = new THREE.Scene();
180
+ scene.background = new THREE.Color(0x111111);
181
+
182
+ // Camera
183
+ camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
184
+ camera.position.set(0, 1, 3);
185
+
186
+ // Renderer
187
+ renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
188
+ renderer.setPixelRatio(window.devicePixelRatio);
189
+ renderer.setSize(window.innerWidth, window.innerHeight);
190
+ container.appendChild(renderer.domElement);
191
+
192
+ // Controls
193
+ controls = new OrbitControls(camera, renderer.domElement);
194
+ controls.enableDamping = true;
195
+ controls.dampingFactor = 0.05;
196
+ controls.autoRotate = true;
197
+ controls.autoRotateSpeed = 1.0;
198
+ controls.target.set(0, 0.5, 0);
199
+
200
+ // Event Listeners
201
+ window.addEventListener('resize', onWindowResize);
202
+
203
+ // Animation Loop
204
+ renderer.setAnimationLoop(animate);
205
+ }
206
+
207
+ function buildUI() {
208
+ SCENES.forEach((sceneData, index) => {
209
+ const btn = document.createElement('button');
210
+ btn.className = `
211
+ flex-shrink-0 w-32 h-20 rounded-xl border border-white/10 overflow-hidden relative group transition-all duration-300
212
+ hover:border-cyan-400 hover:shadow-[0_0_15px_rgba(34,211,238,0.3)]
213
+ ${index === 0 ? 'ring-2 ring-cyan-400' : 'opacity-70 hover:opacity-100'}
214
+ `;
215
+
216
+ // Generate a gradient placeholder for the thumbnail since we don't have images
217
+ const hue = (index * 60) % 360;
218
+ btn.innerHTML = `
219
+ <div class="absolute inset-0 bg-gradient-to-br from-gray-800 to-black group-hover:scale-110 transition-transform duration-500"></div>
220
+ <div class="absolute inset-0 flex flex-col items-center justify-center z-10">
221
+ <span class="text-xs font-bold text-white uppercase tracking-widest drop-shadow-md">${sceneData.name}</span>
222
+ </div>
223
+ <div class="absolute bottom-0 left-0 w-full h-1 bg-cyan-500 transform scale-x-0 group-hover:scale-x-100 transition-transform origin-left"></div>
224
+ `;
225
+
226
+ btn.onclick = () => {
227
+ // Update active state UI
228
+ Array.from(sceneListEl.children).forEach(c => {
229
+ c.classList.remove('ring-2', 'ring-cyan-400', 'opacity-100');
230
+ c.classList.add('opacity-70');
231
+ });
232
+ btn.classList.remove('opacity-70');
233
+ btn.classList.add('ring-2', 'ring-cyan-400', 'opacity-100');
234
+
235
+ loadScene(index);
236
+ };
237
+ sceneListEl.appendChild(btn);
238
+ });
239
+ }
240
+
241
+ async function loadScene(index) {
242
+ const data = SCENES[index];
243
+ targetSceneIndex = index;
244
+
245
+ // Update UI Text
246
+ sceneDescEl.textContent = `Loading: ${data.name}...`;
247
+
248
+ // Show loader if it's a new load (optional, but good for UX)
249
+ // loader.style.opacity = '1';
250
+ // loader.style.pointerEvents = 'all';
251
+
252
+ try {
253
+ // Remove old mesh
254
+ if (currentMesh) {
255
+ scene.remove(currentMesh);
256
+ currentMesh.dispose();
257
+ currentMesh = null;
258
+ }
259
+
260
+ // Create new Gaussian Splat Mesh
261
+ // Note: GaussianSplattingMesh handles the .ply loading internally via a static load method usually,
262
+ // but in newer three.js versions, we might need to load buffer and pass it.
263
+ // Let's check the API.
264
+ // The class usually expects a URL or buffer.
265
+
266
+ const mesh = new GaussianSplattingMesh();
267
+ mesh.splatAlphaRemovalThreshold = data.splatAlphaRemovalThreshold;
268
+
269
+ // Load the PLY file
270
+ await mesh.load(data.url);
271
+
272
+ // Adjust Scale and Position
273
+ // The raw data might be huge or tiny, we normalize slightly
274
+ mesh.scale.set(data.scale, data.scale, data.scale);
275
+
276
+ // Center the mesh roughly (some splats are centered, some aren't)
277
+ // Usually Gaussian Splat data is centered around 0,0,0 or needs bounding box calc.
278
+ // For these specific demo files, they are usually centered.
279
+
280
+ scene.add(mesh);
281
+ currentMesh = mesh;
282
+
283
+ // Camera Transition
284
+ // We smoothly move the camera to the predefined position for this scene
285
+ moveCamera(data.cameraPos);
286
+
287
+ sceneDescEl.textContent = `${data.name} • ${data.description}`;
288
+
289
+ // Hide loader
290
+ loader.style.opacity = '0';
291
+ setTimeout(() => { loader.style.pointerEvents = 'none'; }, 500);
292
+
293
+ } catch (error) {
294
+ console.error("Error loading splat:", error);
295
+ sceneDescEl.textContent = `Error loading ${data.name}. See console.`;
296
+ loader.style.opacity = '0';
297
+ }
298
+ }
299
+
300
+ function moveCamera(targetPos) {
301
+ // Simple interpolation could be added here, but for now direct set with controls update
302
+ // To make it fancy, we could use TWEEN, but let's stick to vanilla/three logic
303
+ camera.position.set(targetPos.x, targetPos.y, targetPos.z);
304
+ controls.target.set(0, 0.2, 0); // Look slightly above origin
305
+ controls.update();
306
+ }
307
+
308
+ function onWindowResize() {
309
+ camera.aspect = window.innerWidth / window.innerHeight;
310
+ camera.updateProjectionMatrix();
311
+ renderer.setSize(window.innerWidth, window.innerHeight);
312
+ }
313
+
314
+ function animate() {
315
+ controls.update(); // required if damping enabled
316
+
317
+ // Subtle floating animation for the mesh if desired,
318
+ // but splats are usually static scenes.
319
+
320
+ renderer.render(scene, camera);
321
+ }
322
+
323
+ // --- Interaction Logic ---
324
+
325
+ btnReset.addEventListener('click', () => {
326
+ const data = SCENES[targetSceneIndex];
327
+ moveCamera(data.cameraPos);
328
+ });
329
+
330
+ btnRotate.addEventListener('click', () => {
331
+ isAutoRotating = !isAutoRotating;
332
+ controls.autoRotate = isAutoRotating;
333
+ btnRotate.classList.toggle('bg-white/20');
334
+ btnRotate.classList.toggle('text-white');
335
+ });
336
+
337
+ btnWireframe.addEventListener('click', () => {
338
+ // Toggle a debug mode or just reset camera for now
339
+ // Gaussian Splatting doesn't have a "wireframe" mode easily accessible without custom shaders
340
+ // So we'll just trigger a flash effect or reset
341
+ scene.background = new THREE.Color(scene.background.getHex() === 0x111111 ? 0x222222 : 0x111111);
342
+ });
343
+
344
+ // Initial progress bar simulation
345
+ let p = 0;
346
+ const interval = setInterval(() => {
347
+ p += 5;
348
+ progressBar.style.width = p + '%';
349
+ if(p > 100) clearInterval(interval);
350
+ }, 50);
351
+
352
+ </script>
353
+ </body>
354
+ </html>