Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Splat updates #5808

Merged
merged 6 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions extras/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ export { GltfExporter } from './exporters/gltf-exporter.js';

// splat
export { registerPlyParser } from './splat/ply-parser.js';
export { Splat } from './splat/splat.js';
export { SplatData } from './splat/splat-data.js';
export { SplatInstance } from './splat/splat-instance.js';
1 change: 1 addition & 0 deletions extras/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"type": "module",
"author": "",
"license": "MIT",
"dependencies": {
Expand Down
11 changes: 5 additions & 6 deletions extras/splat/splat-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,17 @@ class SplatInstance {
cameraMat.getTranslation(cameraPosition);
cameraMat.getZ(cameraDirection);

const modelMat = this.meshInstance.node.getWorldTransform();
const invModelMat = mat.invert(modelMat);
invModelMat.transformPoint(cameraPosition, cameraPosition);
invModelMat.transformVector(cameraDirection, cameraDirection);

// sort if the camera has changed
if (!cameraPosition.equalsApprox(this.lastCameraPosition) || !cameraDirection.equalsApprox(this.lastCameraDirection)) {

this.lastCameraPosition.copy(cameraPosition);
this.lastCameraDirection.copy(cameraDirection);
sorted = true;

const modelMat = this.meshInstance.node.getWorldTransform();
const invModelMat = mat.invert(modelMat);
invModelMat.transformPoint(cameraPosition, cameraPosition);
invModelMat.transformVector(cameraDirection, cameraDirection);

this.sorter.setCamera(cameraPosition, cameraDirection);
}

Expand Down
8 changes: 8 additions & 0 deletions extras/splat/splat-material.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,14 @@ const splatVS = `
vec2 v1 = min(sqrt(2.0 * lambda1), 1024.0) * diagonalVector;
vec2 v2 = min(sqrt(2.0 * lambda2), 1024.0) * vec2(diagonalVector.y, -diagonalVector.x);

// early out tiny splats
// TODO: figure out length units and expose as uniform parameter
// TODO: perhaps make this a shader compile-time option
if (dot(v1, v1) < 1.0 || dot(v2, v2) < 1.0) {
gl_Position = vec4(0.0, 0.0, 2.0, 1.0);
return;
}

gl_Position = splat_proj +
vec4((vertex_position.x * v1 + vertex_position.y * v2) / viewport * 2.0,
0.0, 0.0) * splat_proj.w;
Expand Down
32 changes: 30 additions & 2 deletions extras/splat/splat-sorter.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { EventHandler } from 'playcanvas';

// sort blind set of data
function SortWorker() {

Expand All @@ -15,6 +17,9 @@ function SortWorker() {
let cameraDirection;
let intIndices;

const lastCameraPosition = { x: 0, y: 0, z: 0 };
const lastCameraDirection = { x: 0, y: 0, z: 0 };

const boundMin = { x: 0, y: 0, z: 0 };
const boundMax = { x: 0, y: 0, z: 0 };

Expand All @@ -33,6 +38,24 @@ function SortWorker() {
const dy = cameraDirection.y;
const dz = cameraDirection.z;

const epsilon = 0.001;

if (Math.abs(px - lastCameraPosition.x) < epsilon &&
Math.abs(py - lastCameraPosition.y) < epsilon &&
Math.abs(pz - lastCameraPosition.z) < epsilon &&
Math.abs(dx - lastCameraDirection.x) < epsilon &&
Math.abs(dy - lastCameraDirection.y) < epsilon &&
Math.abs(dz - lastCameraDirection.z) < epsilon) {
return;
}

lastCameraPosition.x = px;
lastCameraPosition.y = py;
lastCameraPosition.z = pz;
lastCameraDirection.x = dx;
lastCameraDirection.y = dy;
lastCameraDirection.z = dz;

// create distance buffer
const numVertices = centers.length / 3;
if (distances?.length !== numVertices) {
Expand Down Expand Up @@ -144,12 +167,14 @@ function SortWorker() {
};
}

class SplatSorter {
class SplatSorter extends EventHandler {
worker;

vertexBuffer;

constructor() {
super();

this.worker = new Worker(URL.createObjectURL(new Blob([`(${SortWorker.toString()})()`], {
type: 'application/javascript'
})));
Expand All @@ -163,7 +188,10 @@ class SplatSorter {
data: oldData
}, [oldData]);

this.vertexBuffer.setData(newData);
setTimeout(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering why the setTimeout is required...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without the setTimeout, the postMessage above will only be enqueued after setData has finished executing (setData can be slooow). With the setTimeout, postMessage is sent immediately and the worker can start sorting for the next round while setData completes.

I've confirmed this using Chrome profiler, but it's possible we can do better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worthy of a comment in the code?

this.vertexBuffer.setData(newData);
this.fire('updated');
});
};
}

Expand Down