Skip to content

Commit

Permalink
Added mode to render only when needed
Browse files Browse the repository at this point in the history
  • Loading branch information
mkkellogg committed Mar 11, 2024
1 parent 6298b67 commit 369425e
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 12 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ const viewer = new GaussianSplats3D.Viewer({
'sharedMemoryForWorkers': true,
'integerBasedSort': true,
'dynamicScene': false,
'webXRMode': GaussianSplats3D.WebXRMode.None
'webXRMode': GaussianSplats3D.WebXRMode.None,
'renderMode': GaussianSplats3D.RenderMode.OnChange
});
viewer.addSplatScene('<path to .ply, .ksplat, or .splat file>')
.then(() => {
Expand Down Expand Up @@ -289,6 +290,7 @@ Advanced `Viewer` parameters
| `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`.
| `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.
| `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`.
| `renderMode` | Controls when the viewer renders the scene. Valid values are defined in the `RenderMode` enum: `Always`, `OnChange`, and `Never`. Defaults to `Always`.
<br>

### Creating KSPLAT files
Expand Down
5 changes: 5 additions & 0 deletions src/RenderMode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const RenderMode = {
Always: 0,
OnChange: 1,
Never: 2
};
4 changes: 4 additions & 0 deletions src/SceneHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ export class SceneHelper {
this.meshCursor.visible = visible;
}

getMeschCursorVisibility() {
return this.meshCursor.visible;
}

setMeshCursorPosition(position) {
this.meshCursor.position.copy(position);
}
Expand Down
2 changes: 2 additions & 0 deletions src/SplatMesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export class SplatMesh extends THREE.Mesh {
this.maxRadius = 0;
this.visibleRegionRadius = 0;
this.visibleRegionFadeStartRadius = 0;
this.visibleRegionChanging = false;

this.disposed = false;
}
Expand Down Expand Up @@ -921,6 +922,7 @@ export class SplatMesh extends THREE.Mesh {
this.material.uniforms.currentTime.value = performance.now();
this.material.uniforms.fadeInComplete.value = fadeInComplete;
this.material.uniformsNeedUpdate = true;
this.visibleRegionChanging = !fadeInComplete;
}

/**
Expand Down
84 changes: 74 additions & 10 deletions src/Viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import { VRButton } from './webxr/VRButton.js';
import { ARButton } from './webxr/ARButton.js';
import { delayedExecute } from './Util.js';
import { LoaderStatus } from './loaders/LoaderStatus.js';

import { RenderMode } from './RenderMode.js';

const THREE_CAMERA_FOV = 50;
const MINIMUM_DISTANCE_TO_NEW_FOCAL_POINT = .75;
const MIN_SPLAT_COUNT_TO_SHOW_SPLAT_TREE_LOADING_SPINNER = 1500000;
const FOCUS_MARKER_FADE_IN_SPEED = 10.0;
const FOCUS_MARKER_FADE_OUT_SPEED = 2.5;
const CONSECUTIVE_RENDERED_FRAMES_FOR_FPS_CALCULATION = 60;

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

this.renderMode = options.renderMode || RenderMode.Always;

this.controls = null;

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

this.currentFPS = 0;
this.lastSortTime = 0;
this.consecutiveRenderFrames = 0;

this.previousCameraTarget = new THREE.Vector3();
this.nextCameraTarget = new THREE.Vector3();
Expand Down Expand Up @@ -283,6 +287,10 @@ export class Viewer {
}
}

setRenderMode(renderMode) {
this.renderMode = renderMode;
}

onKeyDown = function() {

const forward = new THREE.Vector3();
Expand Down Expand Up @@ -941,6 +949,7 @@ export class Viewer {
this.sortPromiseResolver();
this.sortPromise = null;
this.sortPromiseResolver = null;
this.forceRenderNextFrame();
if (sortCount === 0) {
this.runAfterFirstSort.forEach((func) => {
func();
Expand Down Expand Up @@ -1088,13 +1097,60 @@ export class Viewer {
this.requestFrameId = requestAnimationFrame(this.selfDrivenUpdateFunc);
}
this.update();
this.render();
if (this.shouldRender()) {
this.render();
this.consecutiveRenderFrames++;
} else {
this.consecutiveRenderFrames = 0;
}
this.renderNextFrame = false;
}

forceRenderNextFrame() {
this.renderNextFrame = true;
}

shouldRender = function() {

let renderCount = 0;
const lastCameraPosition = new THREE.Vector3();
const lastCameraOrientation = new THREE.Quaternion();
const changeEpsilon = 0.0001;

return function() {
let shouldRender = false;
let cameraChanged = false;
if (this.camera) {
const cp = this.camera.position;
const co = this.camera.quaternion;
cameraChanged = Math.abs(cp.x - lastCameraPosition.x) > changeEpsilon ||
Math.abs(cp.y - lastCameraPosition.y) > changeEpsilon ||
Math.abs(cp.z - lastCameraPosition.z) > changeEpsilon ||
Math.abs(co.x - lastCameraOrientation.x) > changeEpsilon ||
Math.abs(co.y - lastCameraOrientation.y) > changeEpsilon ||
Math.abs(co.z - lastCameraOrientation.z) > changeEpsilon ||
Math.abs(co.w - lastCameraOrientation.w) > changeEpsilon;
}

shouldRender = this.renderMode !== RenderMode.Never && (renderCount === 0 || this.splatMesh.visibleRegionChanging ||
cameraChanged || this.renderMode === RenderMode.Always || this.dynamicMode === true || this.renderNextFrame);

if (this.camera) {
lastCameraPosition.copy(this.camera.position);
lastCameraOrientation.copy(this.camera.quaternion);
}

renderCount++;
return shouldRender;
};

}();

render = function() {

return function() {
if (!this.initialized || !this.splatRenderingInitialized) return;

const hasRenderables = (threeScene) => {
for (let child of threeScene.children) {
if (child.visible) return true;
Expand Down Expand Up @@ -1141,14 +1197,18 @@ export class Viewer {
let frameCount = 0;

return function() {
const currentTime = getCurrentTime();
const calcDelta = currentTime - lastCalcTime;
if (calcDelta >= 1.0) {
this.currentFPS = frameCount;
frameCount = 0;
lastCalcTime = currentTime;
if (this.consecutiveRenderFrames > CONSECUTIVE_RENDERED_FRAMES_FOR_FPS_CALCULATION) {
const currentTime = getCurrentTime();
const calcDelta = currentTime - lastCalcTime;
if (calcDelta >= 1.0) {
this.currentFPS = frameCount;
frameCount = 0;
lastCalcTime = currentTime;
} else {
frameCount++;
}
} else {
frameCount++;
this.currentFPS = null;
}
};

Expand Down Expand Up @@ -1227,6 +1287,7 @@ export class Viewer {
this.sceneHelper.setFocusMarkerOpacity(newFocusMarkerOpacity);
this.sceneHelper.updateFocusMarker(this.nextCameraTarget, this.camera, renderDimensions);
wasTransitioning = true;
this.forceRenderNextFrame();
} else {
let currentFocusMarkerOpacity;
if (wasTransitioning) currentFocusMarkerOpacity = 1.0;
Expand All @@ -1237,6 +1298,7 @@ export class Viewer {
this.sceneHelper.setFocusMarkerOpacity(newFocusMarkerOpacity);
if (newFocusMarkerOpacity === 0.0) this.sceneHelper.setFocusMarkerVisibility(false);
}
if (currentFocusMarkerOpacity > 0.0) this.forceRenderNextFrame();
wasTransitioning = false;
}
};
Expand All @@ -1250,6 +1312,7 @@ export class Viewer {

return function() {
if (this.showMeshCursor) {
this.forceRenderNextFrame();
this.getRenderDimensions(renderDimensions);
outHits.length = 0;
this.raycaster.setFromCameraAndScreenPosition(this.camera, this.mousePosition, renderDimensions);
Expand All @@ -1261,6 +1324,7 @@ export class Viewer {
this.sceneHelper.setMeshCursorVisibility(false);
}
} else {
if (this.sceneHelper.getMeschCursorVisibility()) this.forceRenderNextFrame();
this.sceneHelper.setMeshCursorVisibility(false);
}
};
Expand All @@ -1279,7 +1343,7 @@ export class Viewer {
const meshCursorPosition = this.showMeshCursor ? this.sceneHelper.meshCursor.position : null;
const splatRenderCountPct = this.splatRenderCount / splatCount * 100;
this.infoPanel.update(renderDimensions, this.camera.position, cameraLookAtPosition,
this.camera.up, meshCursorPosition, this.currentFPS, splatCount,
this.camera.up, meshCursorPosition, this.currentFPS || 'N/A', splatCount,
this.splatRenderCount, splatRenderCountPct, this.lastSortTime);
};

Expand Down
4 changes: 3 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { OrbitControls } from './OrbitControls.js';
import { AbortablePromise } from './AbortablePromise.js';
import { SceneFormat } from './loaders/SceneFormat.js';
import { WebXRMode } from './webxr/WebXRMode.js';
import { RenderMode } from './RenderMode.js';

export {
PlyParser,
Expand All @@ -29,5 +30,6 @@ export {
OrbitControls,
AbortablePromise,
SceneFormat,
WebXRMode
WebXRMode,
RenderMode
};

0 comments on commit 369425e

Please sign in to comment.