Skip to content

Commit 6cacd99

Browse files
authored
Merge pull request #140 from mkkellogg/progressive_loading
Progressive loading
2 parents 5a76416 + 0dadc44 commit 6cacd99

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+3997
-1469
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ const viewer = new GaussianSplats3D.Viewer({
114114
});
115115
viewer.addSplatScene('<path to .ply, .ksplat, or .splat file>', {
116116
'splatAlphaRemovalThreshold': 5,
117-
'showLoadingSpinner': true,
117+
'showLoadingUI': true,
118118
'position': [0, 1, 0],
119119
'rotation': [0, 0, 0, 1],
120120
'scale': [1.5, 1.5, 1.5]
@@ -140,10 +140,11 @@ Parameters for `addSplatScene()`
140140
| Parameter | Purpose
141141
| --- | ---
142142
| `splatAlphaRemovalThreshold` | Tells `addSplatScene()` to ignore any splats with an alpha less than the specified value (valid range: 0 - 255). Defaults to `1`.
143-
| `showLoadingSpinner` | Displays a loading spinner while the scene is loading. Defaults to `true`.
143+
| `showLoadingUI` | Displays a loading spinner and/or loading progress bar while the scene is loading. Defaults to `true`.
144144
| `position` | Position of the scene, acts as an offset from its default position. Defaults to `[0, 0, 0]`.
145145
| `rotation` | Rotation of the scene represented as a quaternion, defaults to `[0, 0, 0, 1]` (identity quaternion).
146146
| `scale` | Scene's scale, defaults to `[1, 1, 1]`.
147+
| `streamView` | Stream the scene's splat data and allow the scene to be rendered and viewed as the splats are loaded. Option is only valid for `addSplatScene()`, and not for `addSplatScenes()`.
147148

148149
<br>
149150

@@ -301,7 +302,7 @@ const splatAlphaRemovalThreshold = 5; // out of 255
301302
const plyLoader = new GaussianSplats3D.PlyLoader();
302303
plyLoader.loadFromURL('<path to .ply or .splat file>', compressionLevel, splatAlphaRemovalThreshold)
303304
.then((splatBuffer) => {
304-
new GaussianSplats3D.SplatLoader(splatBuffer).downloadFile('converted_file.ksplat');
305+
GaussianSplats3D.KSplatLoader.downloadFile(splatBuffer, 'converted_file.ksplat');
305306
});
306307
```
307308
Both of the above methods will prompt your browser to automatically start downloading the converted `.ksplat` file.

demo/bonsai.html

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<meta http-equiv="x-ua-compatible" content="ie=edge">
8+
<title>3D Gaussian Splat Demo - Truck</title>
9+
<script type="text/javascript" src="js/util.js"></script>
10+
<script type="importmap">
11+
{
12+
"imports": {
13+
"three": "./lib/three.module.js",
14+
"@mkkellogg/gaussian-splats-3d": "./lib/gaussian-splats-3d.module.js"
15+
}
16+
}
17+
</script>
18+
<style>
19+
20+
body {
21+
background-color: #000000;
22+
height: 100vh;
23+
margin: 0px;
24+
}
25+
26+
</style>
27+
28+
</head>
29+
30+
<body>
31+
<script type="module">
32+
import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';
33+
import * as THREE from 'three';
34+
const viewer = new GaussianSplats3D.Viewer({
35+
'cameraUp': [0.01933, -0.75830, -0.65161],
36+
'initialCameraPosition': [1.54163, 2.68515, -6.37228],
37+
'initialCameraLookAt': [0.45622, 1.95338, 1.51278],
38+
});
39+
let path = 'assets/data/bonsai/bonsai';
40+
path += isMobile() ? '.ksplat' : '_high.ksplat';
41+
viewer.addSplatScene(path, {
42+
'streamView': true
43+
})
44+
.then(() => {
45+
viewer.start();
46+
});
47+
</script>
48+
</body>
49+
50+
</html>

demo/dropin.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
const viewer = new GaussianSplats3D.DropInViewer();
9595
viewer.addSplatScenes([
9696
{
97-
'path': 'assets/data/garden/garden.ksplat',
97+
'path': 'assets/data/garden/garden_high.ksplat',
9898
'splatAlphaRemovalThreshold': 20,
9999
},
100100
{

demo/dynamic_scenes.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
'initialCameraLookAt': [1.52976, 2.27776, 1.65898],
3838
'dynamicScene': true
3939
});
40-
viewer.addSplatScenes([
40+
const lp = viewer.addSplatScenes([
4141
{
4242
'path': 'assets/data/garden/garden.ksplat',
4343
'splatAlphaRemovalThreshold': 20,

demo/garden.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,21 @@
3131
<script type="module">
3232
import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';
3333
import * as THREE from 'three';
34+
3435
const viewer = new GaussianSplats3D.Viewer({
3536
'cameraUp': [0, -1, -0.54],
3637
'initialCameraPosition': [-3.15634, -0.16946, -0.51552],
3738
'initialCameraLookAt': [1.52976, 2.27776, 1.65898]
3839
});
3940
let path = 'assets/data/garden/garden';
4041
path += isMobile() ? '.ksplat' : '_high.ksplat';
41-
viewer.addSplatScene(path)
42+
viewer.addSplatScene(path, {
43+
'streamView': true
44+
})
4245
.then(() => {
4346
viewer.start();
4447
});
48+
4549
</script>
4650
</body>
4751

demo/index.html

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -252,31 +252,36 @@
252252
</script>
253253
<script type="module">
254254
import * as GaussianSplats3D from 'gaussian-splats-3d';
255+
import * as THREE from 'three';
255256

256-
function convertPLYToSplatBuffer(plyBuffer, compressionLevel, alphaRemovalThreshold, blockSize, bucketSize) {
257-
const plyParser = new GaussianSplats3D.PlyParser(plyBuffer);
258-
return plyParser.parseToSplatBuffer(compressionLevel, alphaRemovalThreshold, blockSize, bucketSize);
257+
function convertPLYToSplatBuffer(plyBufferData, compressionLevel, alphaRemovalThreshold, sectionSize, sceneCenter, blockSize, bucketSize) {
258+
const plyParser = new GaussianSplats3D.PlyParser(plyBufferData.data);
259+
const splatArray = plyParser.parseToUncompressedSplatArray();
260+
plyBufferData.data = null;
261+
const splatBufferGenerator = GaussianSplats3D.SplatBufferGenerator.getStandardGenerator(alphaRemovalThreshold, compressionLevel, sectionSize, sceneCenter, blockSize, bucketSize);
262+
return splatBufferGenerator.generateFromUncompressedSplatArray(splatArray);
259263
}
260264

261-
function convertStandardSplatToSplatBuffer(bufferData, compressionLevel, alphaRemovalThreshold, blockSize, bucketSize){
262-
const splatArray = GaussianSplats3D.SplatLoader.parseStandardSplatToUncompressedSplatArray(bufferData);
263-
const splatCompressor = new GaussianSplats3D.SplatCompressor(compressionLevel, alphaRemovalThreshold, blockSize, bucketSize);
264-
return splatCompressor.uncompressedSplatArrayToSplatBuffer(splatArray);
265+
function convertStandardSplatToSplatBuffer(bufferData, compressionLevel, alphaRemovalThreshold, sectionSize, blockSize, bucketSize){
266+
const splatArray = GaussianSplats3D.SplatParser.parseStandardSplatToUncompressedSplatArray(bufferData);
267+
const splatBufferGenerator = GaussianSplats3D.SplatBufferGenerator.getStandardGenerator(alphaRemovalThreshold, compressionLevel, sectionSize, sceneCenter, blockSize, bucketSize);
268+
return splatBufferGenerator.generateFromUncompressedSplatArray(splatArray);
265269
}
266270

267271
function isPlyFile(fileName) {
268272
return fileName.toLowerCase().trim().endsWith('.ply');
269273
}
270274

271-
function fileBufferToSplatBuffer(fileBufferData, isPly, isStandardSplat, compressionLevel, alphaRemovalThreshold, blockSize, bucketSize) {
275+
function fileBufferToSplatBuffer(fileBufferData, format, compressionLevel, alphaRemovalThreshold, sectionSize, sceneCenter, blockSize, bucketSize) {
272276
let splatBuffer;
273-
if (isPly) {
274-
splatBuffer = convertPLYToSplatBuffer(fileBufferData, compressionLevel, alphaRemovalThreshold, blockSize, bucketSize);
277+
if (format === GaussianSplats3D.SceneFormat.Ply) {
278+
splatBuffer = convertPLYToSplatBuffer(fileBufferData, compressionLevel, alphaRemovalThreshold, sectionSize, sceneCenter, blockSize, bucketSize);
275279
} else {
276-
if (isStandardSplat) {
277-
splatBuffer = convertStandardSplatToSplatBuffer(fileBufferData, compressionLevel, alphaRemovalThreshold, blockSize, bucketSize);
280+
if (format === GaussianSplats3D.SceneFormat.Splat) {
281+
splatBuffer = convertStandardSplatToSplatBuffer(fileBufferData.data, compressionLevel, alphaRemovalThreshold, sectionSize, sceneCenter, blockSize, bucketSize);
278282
} else {
279-
splatBuffer = new GaussianSplats3D.SplatBuffer(fileBufferData);
283+
GaussianSplats3D.KSplatLoader.checkVersion(fileBufferData.data);
284+
splatBuffer = new GaussianSplats3D.SplatBuffer(fileBufferData.data);
280285
}
281286
}
282287
return splatBuffer;
@@ -313,9 +318,29 @@
313318
const conversionFile = document.getElementById("conversionFile");
314319
const compressionLevel = parseInt(document.getElementById("compressionLevel").value);
315320
const alphaRemovalThreshold = parseInt(document.getElementById("alphaRemovalThreshold").value);
321+
const sectionSize = 0;
322+
let sceneCenterArray = document.getElementById("sceneCenter").value;
316323
const blockSize = parseFloat(document.getElementById("blockSize").value);
317324
const bucketSize = parseInt(document.getElementById("bucketSize").value);
318325

326+
sceneCenterArray = sceneCenterArray.split(',');
327+
328+
if (sceneCenterArray.length !== 3) {
329+
setViewError("Scene center must contain 3 elements.");
330+
return;
331+
}
332+
333+
for (let i = 0; i < 3; i++) {
334+
sceneCenterArray[i] = parseFloat(sceneCenterArray[i]);
335+
336+
if (isNaN(sceneCenterArray[i])) {
337+
setViewError("Invalid scene center.");
338+
return;
339+
}
340+
}
341+
342+
const sceneCenter = new THREE.Vector3().fromArray(sceneCenterArray);
343+
319344
if (isNaN(compressionLevel) || compressionLevel < 0 || compressionLevel > 1) {
320345
setConversionError("Invalid compression level.");
321346
return;
@@ -356,14 +381,14 @@
356381
setConversionStatus("Parsing file...");
357382
setConversionLoadingIconVisibility(true);
358383
setConversionCheckIconVisibility(false);
359-
const conversionFileName = conversionFile.files[0].name;
360-
const isPly = isPlyFile(conversionFileName);
361-
const isStandardSplat = GaussianSplats3D.SplatLoader.isStandardSplatFormat(conversionFileName);
384+
const conversionFileName = conversionFile.files[0].name.trim();
385+
const format = GaussianSplats3D.LoaderUtils.sceneFormatFromPath(conversionFileName);
386+
const fileData = {data: fileReader.result};
362387
window.setTimeout(() => {
363388
try {
364-
const splatBuffer = fileBufferToSplatBuffer(fileReader.result, isPly, isStandardSplat, compressionLevel,
365-
alphaRemovalThreshold, blockSize, bucketSize);
366-
new GaussianSplats3D.SplatLoader(splatBuffer).downloadFile('converted_file.ksplat');
389+
const splatBuffer = fileBufferToSplatBuffer(fileData, format, compressionLevel,
390+
alphaRemovalThreshold, sectionSize, sceneCenter, blockSize, bucketSize);
391+
GaussianSplats3D.KSplatLoader.downloadFile(splatBuffer, 'converted_file.ksplat');
367392
conversionDone();
368393
} catch (e) {
369394
conversionDone(e);
@@ -456,9 +481,8 @@
456481
}
457482
}
458483

459-
const viewFileName = viewFile.files[0].name;
460-
const isPly = isPlyFile(viewFileName);
461-
const isStandardSplat = GaussianSplats3D.SplatLoader.isStandardSplatFormat(viewFileName);
484+
const viewFileName = viewFile.files[0].name.trim();
485+
const format = GaussianSplats3D.LoaderUtils.sceneFormatFromPath(viewFileName);
462486

463487
currentAlphaRemovalThreshold = alphaRemovalThreshold;
464488
currentCameraUpArray = cameraUpArray;
@@ -468,8 +492,9 @@
468492
const fileReader = new FileReader();
469493
fileReader.onload = function(){
470494
try {
471-
runViewer(fileReader.result, isPly, isStandardSplat, alphaRemovalThreshold, cameraUpArray, cameraPositionArray, cameraLookAtArray);
495+
runViewer(fileReader.result, format, alphaRemovalThreshold, cameraUpArray, cameraPositionArray, cameraLookAtArray);
472496
} catch (e) {
497+
console.error(e);
473498
setViewError("Could not view scene.");
474499
}
475500
}
@@ -505,7 +530,7 @@
505530
}
506531
});
507532

508-
function runViewer(splatBufferData, isPly, isStandardSplat, alphaRemovalThreshold, cameraUpArray, cameraPositionArray, cameraLookAtArray) {
533+
function runViewer(splatBufferData, format, alphaRemovalThreshold, cameraUpArray, cameraPositionArray, cameraLookAtArray) {
509534
const viewerOptions = {
510535
'cameraUp': cameraUpArray,
511536
'initialCameraPosition': cameraPositionArray,
@@ -516,7 +541,7 @@
516541
'splatAlphaRemovalThreshold': alphaRemovalThreshold
517542
};
518543

519-
const splatBuffer = fileBufferToSplatBuffer(splatBufferData, isPly, isStandardSplat, 0, alphaRemovalThreshold);
544+
const splatBuffer = fileBufferToSplatBuffer({data: splatBufferData}, format, 0, alphaRemovalThreshold);
520545
document.getElementById("demo-content").style.display = 'none';
521546
document.body.style.backgroundColor = "#000000";
522547
history.pushState("ViewSplat", null);
@@ -575,6 +600,10 @@
575600
<img src="assets/images/stump.png" class="demo-scene-panel-image">
576601
<span class="small-title">Stump</span>
577602
</div>
603+
<div class="demo-scene-panel" onclick="openDemo('bonsai')">
604+
<img src="assets/images/bonsai.png" class="demo-scene-panel-image">
605+
<span class="small-title">Bonsai</span>
606+
</div>
578607
<div class="demo-scene-panel" onclick="openDemo('dynamic_scenes')">
579608
<img src="assets/images/dynamic_scenes.png" class="demo-scene-panel-image">
580609
<span class="small-title">Dynamic scenes</span>
@@ -656,7 +685,7 @@
656685
<span id="viewError" style="color: #ff0000"></span>
657686
</div>
658687

659-
<div id ="conversion-panel" class="splat-panel" style="height:360px;">
688+
<div id ="conversion-panel" class="splat-panel" style="height:380px;">
660689
<br>
661690
<span class="small-title">Convert a <span class="file-ext">.ply</span> or <span class="file-ext">.splat</span> to <span class="file-ext">.ksplat</span></span>
662691
<br>
@@ -682,7 +711,15 @@
682711
<input id="alphaRemovalThreshold" type="text" class="text-input" style="width: 50px" value="1"></input>
683712
<span style="color:#888888">(1 - 255)</span>
684713
</td>
685-
</tr>
714+
</tr>
715+
<tr>
716+
<td>
717+
Scene center:&nbsp;
718+
</td>
719+
<td>
720+
<input id="sceneCenter" type="text" class="text-input" style="width: 50px" value="0, 0, 0"></input>
721+
</td>
722+
</tr>
686723
<tr>
687724
<td>
688725
Compression level:
@@ -739,7 +776,7 @@
739776
<span id="conversionError" style="color: #ff0000"></span>
740777
</div>
741778

742-
<div id ="controls-panel" class="splat-panel" style="height:360px;">
779+
<div id ="controls-panel" class="splat-panel" style="height:380px;">
743780

744781
<br>
745782
<span class="small-title">Mouse input</span>

demo/stump.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@
3333
const viewer = new GaussianSplats3D.Viewer({
3434
'cameraUp': [0, -1, -1.0],
3535
'initialCameraPosition': [-3.3816, 1.96931, -1.71890],
36-
'initialCameraLookAt': [0.60910, 1.42099, 2.02511]
36+
'initialCameraLookAt': [-0.04979, 1.37519, 1.13443]
3737
});
3838
let path = 'assets/data/stump/stump';
3939
path += isMobile() ? '.ksplat' : '_high.ksplat';
40-
viewer.addSplatScene(path)
40+
viewer.addSplatScene(path, {
41+
'streamView': true
42+
})
4143
.then(() => {
4244
viewer.start();
4345
});

demo/truck.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@
3333
const viewer = new GaussianSplats3D.Viewer({
3434
'cameraUp': [0, -1, -.17],
3535
'initialCameraPosition': [-5, -1, -1],
36-
'initialCameraLookAt': [1, 1, 0]
36+
'initialCameraLookAt': [-1.72477, 0.05395, -0.00147]
3737
});
3838
let path = 'assets/data/truck/truck';
3939
path += isMobile() ? '.ksplat' : '_high.ksplat';
40-
viewer.addSplatScene(path)
40+
viewer.addSplatScene(path, {
41+
'streamView': true
42+
})
4143
.then(() => {
4244
viewer.start();
4345
});

demo/vr.html

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,14 @@
3232
import * as GaussianSplats3D from '@mkkellogg/gaussian-splats-3d';
3333
import * as THREE from 'three';
3434
const viewer = new GaussianSplats3D.Viewer({
35-
'initialCameraPosition': [-0.77493, -0.54359, 1.63199],
3635
'initialCameraLookAt': [0.20786, -0.68154, -0.27311],
37-
'webXRMode': GaussianSplats3D.WebXRMode.AR,
38-
'gpuAcceleratesSort': false,
39-
'sharedMemoryForWebWorkers': false
36+
'webXRMode': GaussianSplats3D.WebXRMode.AR
4037
});
41-
let path = 'assets/data/bonsai/bonsai.ksplat';
38+
let path = 'assets/data/bonsai/bonsai_high.ksplat';
4239
viewer.addSplatScene(path, {
4340
'rotation': new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0.01933, -0.75830, -0.65161).normalize(), new THREE.Vector3(0, 1, 0)).toArray(),
4441
'scale': [0.25, 0.25, 0.25],
45-
'position': [0, 1, 0]
42+
'position': [0, 0.5, 0]
4643
})
4744
.then(() => {
4845
viewer.start();

package-lock.json

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)