Skip to content

Commit 369425e

Browse files
committed
Added mode to render only when needed
1 parent 6298b67 commit 369425e

File tree

6 files changed

+91
-12
lines changed

6 files changed

+91
-12
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,8 @@ const viewer = new GaussianSplats3D.Viewer({
259259
'sharedMemoryForWorkers': true,
260260
'integerBasedSort': true,
261261
'dynamicScene': false,
262-
'webXRMode': GaussianSplats3D.WebXRMode.None
262+
'webXRMode': GaussianSplats3D.WebXRMode.None,
263+
'renderMode': GaussianSplats3D.RenderMode.OnChange
263264
});
264265
viewer.addSplatScene('<path to .ply, .ksplat, or .splat file>')
265266
.then(() => {
@@ -289,6 +290,7 @@ Advanced `Viewer` parameters
289290
| `integerBasedSort` | Tells the sorting web worker to use the integer versions of relevant data to compute the distance of splats from the camera. Since integer arithmetic is faster than floating point, this reduces sort time. However it can result in integer overflows in larger scenes so it should only be used for small scenes. Defaults to `true`.
290291
| `dynamicScene` | Tells the viewer to not make any optimizations that depend on the scene being static. Additionally all splat data retrieved from the viewer's splat mesh will not have their respective scene transform applied to them by default.
291292
| `webXRMode` | Tells the viewer whether or not to enable built-in Web VR or Web AR. Valid values are defined in the `WebXRMode` enum: `None`, `VR`, and `AR`. Defaults to `None`.
293+
| `renderMode` | Controls when the viewer renders the scene. Valid values are defined in the `RenderMode` enum: `Always`, `OnChange`, and `Never`. Defaults to `Always`.
292294
<br>
293295

294296
### Creating KSPLAT files

src/RenderMode.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const RenderMode = {
2+
Always: 0,
3+
OnChange: 1,
4+
Never: 2
5+
};

src/SceneHelper.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ export class SceneHelper {
129129
this.meshCursor.visible = visible;
130130
}
131131

132+
getMeschCursorVisibility() {
133+
return this.meshCursor.visible;
134+
}
135+
132136
setMeshCursorPosition(position) {
133137
this.meshCursor.position.copy(position);
134138
}

src/SplatMesh.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export class SplatMesh extends THREE.Mesh {
7272
this.maxRadius = 0;
7373
this.visibleRegionRadius = 0;
7474
this.visibleRegionFadeStartRadius = 0;
75+
this.visibleRegionChanging = false;
7576

7677
this.disposed = false;
7778
}
@@ -921,6 +922,7 @@ export class SplatMesh extends THREE.Mesh {
921922
this.material.uniforms.currentTime.value = performance.now();
922923
this.material.uniforms.fadeInComplete.value = fadeInComplete;
923924
this.material.uniformsNeedUpdate = true;
925+
this.visibleRegionChanging = !fadeInComplete;
924926
}
925927

926928
/**

src/Viewer.js

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ import { VRButton } from './webxr/VRButton.js';
2020
import { ARButton } from './webxr/ARButton.js';
2121
import { delayedExecute } from './Util.js';
2222
import { LoaderStatus } from './loaders/LoaderStatus.js';
23-
23+
import { RenderMode } from './RenderMode.js';
2424

2525
const THREE_CAMERA_FOV = 50;
2626
const MINIMUM_DISTANCE_TO_NEW_FOCAL_POINT = .75;
2727
const MIN_SPLAT_COUNT_TO_SHOW_SPLAT_TREE_LOADING_SPINNER = 1500000;
2828
const FOCUS_MARKER_FADE_IN_SPEED = 10.0;
2929
const FOCUS_MARKER_FADE_OUT_SPEED = 2.5;
30+
const CONSECUTIVE_RENDERED_FRAMES_FOR_FPS_CALCULATION = 60;
3031

3132
/**
3233
* Viewer: Manages the rendering of splat scenes. Manages an instance of SplatMesh as well as a web worker
@@ -121,6 +122,8 @@ export class Viewer {
121122
this.gpuAcceleratedSort = false;
122123
}
123124

125+
this.renderMode = options.renderMode || RenderMode.Always;
126+
124127
this.controls = null;
125128

126129
this.showMeshCursor = false;
@@ -147,6 +150,7 @@ export class Viewer {
147150

148151
this.currentFPS = 0;
149152
this.lastSortTime = 0;
153+
this.consecutiveRenderFrames = 0;
150154

151155
this.previousCameraTarget = new THREE.Vector3();
152156
this.nextCameraTarget = new THREE.Vector3();
@@ -283,6 +287,10 @@ export class Viewer {
283287
}
284288
}
285289

290+
setRenderMode(renderMode) {
291+
this.renderMode = renderMode;
292+
}
293+
286294
onKeyDown = function() {
287295

288296
const forward = new THREE.Vector3();
@@ -941,6 +949,7 @@ export class Viewer {
941949
this.sortPromiseResolver();
942950
this.sortPromise = null;
943951
this.sortPromiseResolver = null;
952+
this.forceRenderNextFrame();
944953
if (sortCount === 0) {
945954
this.runAfterFirstSort.forEach((func) => {
946955
func();
@@ -1088,13 +1097,60 @@ export class Viewer {
10881097
this.requestFrameId = requestAnimationFrame(this.selfDrivenUpdateFunc);
10891098
}
10901099
this.update();
1091-
this.render();
1100+
if (this.shouldRender()) {
1101+
this.render();
1102+
this.consecutiveRenderFrames++;
1103+
} else {
1104+
this.consecutiveRenderFrames = 0;
1105+
}
1106+
this.renderNextFrame = false;
10921107
}
10931108

1109+
forceRenderNextFrame() {
1110+
this.renderNextFrame = true;
1111+
}
1112+
1113+
shouldRender = function() {
1114+
1115+
let renderCount = 0;
1116+
const lastCameraPosition = new THREE.Vector3();
1117+
const lastCameraOrientation = new THREE.Quaternion();
1118+
const changeEpsilon = 0.0001;
1119+
1120+
return function() {
1121+
let shouldRender = false;
1122+
let cameraChanged = false;
1123+
if (this.camera) {
1124+
const cp = this.camera.position;
1125+
const co = this.camera.quaternion;
1126+
cameraChanged = Math.abs(cp.x - lastCameraPosition.x) > changeEpsilon ||
1127+
Math.abs(cp.y - lastCameraPosition.y) > changeEpsilon ||
1128+
Math.abs(cp.z - lastCameraPosition.z) > changeEpsilon ||
1129+
Math.abs(co.x - lastCameraOrientation.x) > changeEpsilon ||
1130+
Math.abs(co.y - lastCameraOrientation.y) > changeEpsilon ||
1131+
Math.abs(co.z - lastCameraOrientation.z) > changeEpsilon ||
1132+
Math.abs(co.w - lastCameraOrientation.w) > changeEpsilon;
1133+
}
1134+
1135+
shouldRender = this.renderMode !== RenderMode.Never && (renderCount === 0 || this.splatMesh.visibleRegionChanging ||
1136+
cameraChanged || this.renderMode === RenderMode.Always || this.dynamicMode === true || this.renderNextFrame);
1137+
1138+
if (this.camera) {
1139+
lastCameraPosition.copy(this.camera.position);
1140+
lastCameraOrientation.copy(this.camera.quaternion);
1141+
}
1142+
1143+
renderCount++;
1144+
return shouldRender;
1145+
};
1146+
1147+
}();
1148+
10941149
render = function() {
10951150

10961151
return function() {
10971152
if (!this.initialized || !this.splatRenderingInitialized) return;
1153+
10981154
const hasRenderables = (threeScene) => {
10991155
for (let child of threeScene.children) {
11001156
if (child.visible) return true;
@@ -1141,14 +1197,18 @@ export class Viewer {
11411197
let frameCount = 0;
11421198

11431199
return function() {
1144-
const currentTime = getCurrentTime();
1145-
const calcDelta = currentTime - lastCalcTime;
1146-
if (calcDelta >= 1.0) {
1147-
this.currentFPS = frameCount;
1148-
frameCount = 0;
1149-
lastCalcTime = currentTime;
1200+
if (this.consecutiveRenderFrames > CONSECUTIVE_RENDERED_FRAMES_FOR_FPS_CALCULATION) {
1201+
const currentTime = getCurrentTime();
1202+
const calcDelta = currentTime - lastCalcTime;
1203+
if (calcDelta >= 1.0) {
1204+
this.currentFPS = frameCount;
1205+
frameCount = 0;
1206+
lastCalcTime = currentTime;
1207+
} else {
1208+
frameCount++;
1209+
}
11501210
} else {
1151-
frameCount++;
1211+
this.currentFPS = null;
11521212
}
11531213
};
11541214

@@ -1227,6 +1287,7 @@ export class Viewer {
12271287
this.sceneHelper.setFocusMarkerOpacity(newFocusMarkerOpacity);
12281288
this.sceneHelper.updateFocusMarker(this.nextCameraTarget, this.camera, renderDimensions);
12291289
wasTransitioning = true;
1290+
this.forceRenderNextFrame();
12301291
} else {
12311292
let currentFocusMarkerOpacity;
12321293
if (wasTransitioning) currentFocusMarkerOpacity = 1.0;
@@ -1237,6 +1298,7 @@ export class Viewer {
12371298
this.sceneHelper.setFocusMarkerOpacity(newFocusMarkerOpacity);
12381299
if (newFocusMarkerOpacity === 0.0) this.sceneHelper.setFocusMarkerVisibility(false);
12391300
}
1301+
if (currentFocusMarkerOpacity > 0.0) this.forceRenderNextFrame();
12401302
wasTransitioning = false;
12411303
}
12421304
};
@@ -1250,6 +1312,7 @@ export class Viewer {
12501312

12511313
return function() {
12521314
if (this.showMeshCursor) {
1315+
this.forceRenderNextFrame();
12531316
this.getRenderDimensions(renderDimensions);
12541317
outHits.length = 0;
12551318
this.raycaster.setFromCameraAndScreenPosition(this.camera, this.mousePosition, renderDimensions);
@@ -1261,6 +1324,7 @@ export class Viewer {
12611324
this.sceneHelper.setMeshCursorVisibility(false);
12621325
}
12631326
} else {
1327+
if (this.sceneHelper.getMeschCursorVisibility()) this.forceRenderNextFrame();
12641328
this.sceneHelper.setMeshCursorVisibility(false);
12651329
}
12661330
};
@@ -1279,7 +1343,7 @@ export class Viewer {
12791343
const meshCursorPosition = this.showMeshCursor ? this.sceneHelper.meshCursor.position : null;
12801344
const splatRenderCountPct = this.splatRenderCount / splatCount * 100;
12811345
this.infoPanel.update(renderDimensions, this.camera.position, cameraLookAtPosition,
1282-
this.camera.up, meshCursorPosition, this.currentFPS, splatCount,
1346+
this.camera.up, meshCursorPosition, this.currentFPS || 'N/A', splatCount,
12831347
this.splatRenderCount, splatRenderCountPct, this.lastSortTime);
12841348
};
12851349

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { OrbitControls } from './OrbitControls.js';
1313
import { AbortablePromise } from './AbortablePromise.js';
1414
import { SceneFormat } from './loaders/SceneFormat.js';
1515
import { WebXRMode } from './webxr/WebXRMode.js';
16+
import { RenderMode } from './RenderMode.js';
1617

1718
export {
1819
PlyParser,
@@ -29,5 +30,6 @@ export {
2930
OrbitControls,
3031
AbortablePromise,
3132
SceneFormat,
32-
WebXRMode
33+
WebXRMode,
34+
RenderMode
3335
};

0 commit comments

Comments
 (0)