Skip to content

Commit 2d80511

Browse files
authored
Merge pull request #310 from mkkellogg/dev
Release 0.4.4: General updates
2 parents ef524f4 + b3ec449 commit 2d80511

19 files changed

Lines changed: 638 additions & 322 deletions

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,13 @@ Both of the above methods will prompt your browser to automatically start downlo
354354
The third option is to use the included nodejs script:
355355

356356
```
357-
node util/create-ksplat.js [path to .PLY or .SPLAT] [output file] [compression level = 0] [alpha removal threshold = 1]
357+
node util/create-ksplat.js [path to .PLY or .SPLAT] [output file] [compression level = 0] [alpha removal threshold = 1] [scene center = "0,0,0"] [block size = 5.0] [bucket size = 256] [spherical harmonics level = 0]
358+
```
359+
360+
For the nodejs script, it may be necessary to increase the heap size for larger scenes. Use the parameter `--max-old-space-size=[heap size in MB]` to do so:
361+
362+
```
363+
node util/create-ksplat.js --max-old-space-size=8192 [... remaining arguments]
358364
```
359365

360366
Currently supported values for `compressionLevel` are `0`, `1`, or `2`. `0` means no compression and `1` means compression of scale, rotation, position, and spherical harmonics coefficient values from 32-bit to 16-bit. `2` is similar to `1` except spherical harmonics coefficients are compressed to 8-bit.

demo/dynamic_dropin.html

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
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 Splats - Drop-in example</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+
35+
function setupRenderer() {
36+
const renderWidth = 800;
37+
const renderHeight = 600;
38+
39+
const rootElement = document.createElement('div');
40+
rootElement.style.width = renderWidth + 'px';
41+
rootElement.style.height = renderHeight + 'px';
42+
rootElement.style.position = 'relative';
43+
rootElement.style.left = '50%';
44+
rootElement.style.top = '50%';
45+
rootElement.style.transform = 'translate(-50%, -50%)';
46+
document.body.appendChild(rootElement);
47+
48+
const renderer = new THREE.WebGLRenderer({
49+
antialias: false
50+
});
51+
renderer.setSize(renderWidth, renderHeight);
52+
rootElement.appendChild(renderer.domElement);
53+
54+
return {
55+
'renderer': renderer,
56+
'renderWidth': renderWidth,
57+
'renderHeight': renderHeight
58+
}
59+
}
60+
61+
function setupCamera(renderWidth, renderHeight) {
62+
const camera = new THREE.PerspectiveCamera(65, renderWidth / renderHeight, 0.1, 500);
63+
camera.position.copy(new THREE.Vector3().fromArray([-1, -4, 6]));
64+
camera.lookAt(new THREE.Vector3().fromArray([0, 4, -0]));
65+
camera.up = new THREE.Vector3().fromArray([0, -1, -0.6]).normalize();
66+
return camera;
67+
}
68+
69+
function setupThreeScene() {
70+
const threeScene = new THREE.Scene();
71+
const boxColor = 0xBBBBBB;
72+
const boxGeometry = new THREE.BoxGeometry(2, 2, 2);
73+
const boxMesh = new THREE.Mesh(boxGeometry, new THREE.MeshBasicMaterial({'color': boxColor}));
74+
threeScene.add(boxMesh);
75+
boxMesh.position.set(3, 2, 2);
76+
return threeScene;
77+
}
78+
79+
function setupControls(camera, renderer) {
80+
const controls = new GaussianSplats3D.OrbitControls(camera, renderer.domElement);
81+
controls.rotateSpeed = 0.5;
82+
controls.maxPolarAngle = Math.PI * .75;
83+
controls.minPolarAngle = 0.1;
84+
controls.enableDamping = true;
85+
controls.dampingFactor = 0.05;
86+
return controls;
87+
}
88+
89+
const {renderer, renderWidth, renderHeight} = setupRenderer();
90+
const camera = setupCamera(renderWidth, renderHeight);
91+
const threeScene = setupThreeScene();
92+
const controls = setupControls(camera, renderer);
93+
94+
const viewer = new GaussianSplats3D.DropInViewer({
95+
'dynamicScene': true
96+
});
97+
viewer.addSplatScenes([
98+
{
99+
'path': 'assets/data/garden/garden.ksplat',
100+
'splatAlphaRemovalThreshold': 20,
101+
},
102+
{
103+
'path': 'assets/data/bonsai/bonsai_trimmed.ksplat',
104+
'splatAlphaRemovalThreshold': 20,
105+
},
106+
{
107+
'path': 'assets/data/bonsai/bonsai_trimmed.ksplat',
108+
'splatAlphaRemovalThreshold': 20,
109+
}
110+
], true).then(() => {
111+
112+
threeScene.add(viewer);
113+
114+
const bonsaiCount = 2;
115+
const bonsaiStartIndex = 1;
116+
const rotationAxis = new THREE.Vector3(0, -1, -0.6).normalize();
117+
const baseQuaternion = new THREE.Quaternion(-0.147244, -0.07617, 0.14106, 0.9760);
118+
const rotationQuaternion = new THREE.Quaternion();
119+
const quaternion = new THREE.Quaternion();
120+
const orbitCenter = new THREE.Vector3(0.416161, 1.385, 1.145);
121+
const horizontalOffsetVector = new THREE.Vector3();
122+
const position = new THREE.Vector3();
123+
const scale = new THREE.Vector3(1.25, 1.25, 1.25);
124+
125+
// generate splat mesh parent objects
126+
const sphereGeometry = new THREE.SphereGeometry(0.25, 8, 8);
127+
const material = new THREE.MeshBasicMaterial({color: 0xff0000});
128+
const meshA = new THREE.Mesh(sphereGeometry, material);
129+
const meshB = new THREE.Mesh(sphereGeometry, material);
130+
131+
// add splat mesh parent objects to the scene
132+
threeScene.add(meshA);
133+
threeScene.add(meshB);
134+
135+
// You can modify the transform components (position, quaternion, scale) of a SplatScene
136+
// directly like any three.js object OR you can just attach them to another three.js object
137+
// and they will be transformed accordingly. Below we are going with the latter approach.
138+
// The splat scenes at index 1 & 2 are (by default) children of viewer.splatMesh, so we
139+
// re-parent them to meshA and meshB respectively.
140+
meshA.add(viewer.getSplatScene(1));
141+
meshB.add(viewer.getSplatScene(2));
142+
143+
let startTime = performance.now() / 1000.0;
144+
requestAnimationFrame(update);
145+
function update() {
146+
requestAnimationFrame(update);
147+
const timeDelta = performance.now() / 1000.0 - startTime;
148+
for (let i = bonsaiStartIndex; i < bonsaiStartIndex + bonsaiCount; i++) {
149+
150+
// calculate parent mesh positions & orientations
151+
const angle = timeDelta * 0.25 + (Math.PI * 2) * (i /bonsaiCount);
152+
const height = Math.cos(timeDelta + (Math.PI * 2) * (i / bonsaiCount)) * 0.5 + 3;
153+
rotationQuaternion.setFromAxisAngle(rotationAxis, angle);
154+
horizontalOffsetVector.set(3, 0, 0).applyQuaternion(rotationQuaternion);
155+
156+
// apply mesh position, orientation and scale
157+
const mesh = (i % 2 === 0) ? meshA : meshB;
158+
mesh.position.copy(rotationAxis).multiplyScalar(height).add(horizontalOffsetVector).add(orbitCenter);
159+
mesh.quaternion.copy(baseQuaternion).premultiply(rotationQuaternion);
160+
mesh.scale.copy(scale);
161+
162+
// perform standard three.js update and render
163+
controls.update();
164+
renderer.render(threeScene, camera);
165+
166+
}
167+
}
168+
});
169+
170+
</script>
171+
</body>
172+
173+
</html>

demo/dynamic_scenes.html

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
'initialCameraLookAt': [1.52976, 2.27776, 1.65898],
3838
'dynamicScene': true
3939
});
40-
const lp = viewer.addSplatScenes([
40+
41+
viewer.addSplatScenes([
4142
{
4243
'path': 'assets/data/garden/garden.ksplat',
4344
'splatAlphaRemovalThreshold': 20,
@@ -64,24 +65,40 @@
6465
const position = new THREE.Vector3();
6566
const scale = new THREE.Vector3(1.25, 1.25, 1.25);
6667

68+
// generate splat mesh parent objects
69+
const sphereGeometry = new THREE.SphereGeometry(0.25, 8, 8);
70+
const material = new THREE.MeshBasicMaterial({color: 0xff0000});
71+
const meshA = new THREE.Mesh(sphereGeometry, material);
72+
const meshB = new THREE.Mesh(sphereGeometry, material);
73+
74+
// add splat mesh parent objects to the scene
75+
viewer.splatMesh.add(meshA);
76+
viewer.splatMesh.add(meshB);
77+
78+
// You can modify the transform components (position, quaternion, scale) of a SplatScene
79+
// directly like any three.js object OR you can just attach them to another three.js object
80+
// and they will be transformed accordingly. Below we are going with the latter approach.
81+
// The splat scenes at index 1 & 2 are (by default) children of viewer.splatMesh, so we
82+
// re-parent them to meshA and meshB respectively.
83+
meshA.add(viewer.getSplatScene(1));
84+
meshB.add(viewer.getSplatScene(2));
85+
6786
let startTime = performance.now() / 1000.0;
6887
requestAnimationFrame(update);
6988
function update() {
7089
requestAnimationFrame(update);
7190
const timeDelta = performance.now() / 1000.0 - startTime;
7291
for (let i = bonsaiStartIndex; i < bonsaiStartIndex + bonsaiCount; i++) {
92+
// calculate parent mesh positions & orientations
7393
const angle = timeDelta * 0.25 + (Math.PI * 2) * (i /bonsaiCount);
7494
const height = Math.cos(timeDelta + (Math.PI * 2) * (i / bonsaiCount)) * 0.5 + 3;
75-
7695
rotationQuaternion.setFromAxisAngle(rotationAxis, angle);
7796
horizontalOffsetVector.set(3, 0, 0).applyQuaternion(rotationQuaternion);
78-
position.copy(rotationAxis).multiplyScalar(height).add(horizontalOffsetVector).add(orbitCenter);
79-
quaternion.copy(baseQuaternion).premultiply(rotationQuaternion);
80-
81-
const splatScene = viewer.getSplatScene(i);
82-
splatScene.position.copy(position);
83-
splatScene.quaternion.copy(quaternion);
84-
splatScene.scale.copy(scale);
97+
// apply mesh position, orientation and scale
98+
const mesh = (i % 2 === 0) ? meshA : meshB;
99+
mesh.position.copy(rotationAxis).multiplyScalar(height).add(horizontalOffsetVector).add(orbitCenter);
100+
mesh.quaternion.copy(baseQuaternion).premultiply(rotationQuaternion);
101+
mesh.scale.copy(scale);
85102
}
86103
}
87104

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"type": "git",
55
"url": "https://github.com/mkkellogg/GaussianSplats3D"
66
},
7-
"version": "0.4.3",
7+
"version": "0.4.4",
88
"description": "Three.js-based 3D Gaussian splat viewer",
99
"module": "build/gaussian-splats-3d.module.js",
1010
"main": "build/gaussian-splats-3d.umd.cjs",

src/AbortablePromise.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ export class AbortablePromise {
6464
}, this.abortHandler);
6565
}
6666

67-
abort() {
68-
if (this.abortHandler) this.abortHandler();
67+
abort(reason) {
68+
if (this.abortHandler) this.abortHandler(reason);
6969
}
7070

7171
}

src/DropInViewer.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ export class DropInViewer extends THREE.Group {
108108
return this.viewer.removeSplatScenes(indexes, showLoadingUI);
109109
}
110110

111+
getSceneCount() {
112+
return this.viewer.getSceneCount();
113+
}
114+
111115
dispose() {
112116
return this.viewer.dispose();
113117
}

src/Util.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,12 @@ export const fetchWithProgress = function(path, onProgress, saveChunks = true) {
5959
const abortController = new AbortController();
6060
const signal = abortController.signal;
6161
let aborted = false;
62-
let rejectFunc = null;
6362
const abortHandler = (reason) => {
64-
abortController.abort(reason);
65-
rejectFunc(new AbortedPromiseError('Fetch aborted.'));
63+
abortController.abort(new AbortedPromiseError(reason));
6664
aborted = true;
6765
};
6866

6967
return new AbortablePromise((resolve, reject) => {
70-
rejectFunc = reject;
7168
fetch(path, { signal })
7269
.then(async (data) => {
7370
const reader = data.body.getReader();
@@ -109,6 +106,9 @@ export const fetchWithProgress = function(path, onProgress, saveChunks = true) {
109106
break;
110107
}
111108
}
109+
})
110+
.catch((error) => {
111+
reject(error);
112112
});
113113
}, abortHandler);
114114

0 commit comments

Comments
 (0)