playcanvas
Version:
PlayCanvas WebGL game engine
337 lines (334 loc) • 10.2 kB
JavaScript
import { EventHandler } from '../../core/event-handler.js';
import { TEXTURELOCK_READ } from '../../platform/graphics/constants.js';
function SortWorker() {
var order;
var centers;
var chunks;
var mapping;
var cameraPosition;
var cameraDirection;
var forceUpdate = false;
var lastCameraPosition = {
x: 0,
y: 0,
z: 0
};
var lastCameraDirection = {
x: 0,
y: 0,
z: 0
};
var boundMin = {
x: 0,
y: 0,
z: 0
};
var boundMax = {
x: 0,
y: 0,
z: 0
};
var distances;
var countBuffer;
var numBins = 32;
var binCount = new Array(numBins).fill(0);
var binBase = new Array(numBins).fill(0);
var binDivider = new Array(numBins).fill(0);
var binarySearch = (m, n, compare_fn)=>{
while(m <= n){
var k = n + m >> 1;
var cmp = compare_fn(k);
if (cmp > 0) {
m = k + 1;
} else if (cmp < 0) {
n = k - 1;
} else {
return k;
}
}
return ~m;
};
var update = ()=>{
if (!order || !centers || centers.length === 0 || !cameraPosition || !cameraDirection) return;
var px = cameraPosition.x;
var py = cameraPosition.y;
var pz = cameraPosition.z;
var dx = cameraDirection.x;
var dy = cameraDirection.y;
var dz = cameraDirection.z;
var epsilon = 0.001;
if (!forceUpdate && 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;
}
forceUpdate = false;
lastCameraPosition.x = px;
lastCameraPosition.y = py;
lastCameraPosition.z = pz;
lastCameraDirection.x = dx;
lastCameraDirection.y = dy;
lastCameraDirection.z = dz;
var minDist;
var maxDist;
for(var i = 0; i < 8; ++i){
var x = i & 1 ? boundMin.x : boundMax.x;
var y = i & 2 ? boundMin.y : boundMax.y;
var z = i & 4 ? boundMin.z : boundMax.z;
var d = x * dx + y * dy + z * dz;
if (i === 0) {
minDist = maxDist = d;
} else {
minDist = Math.min(minDist, d);
maxDist = Math.max(maxDist, d);
}
}
var numVertices = centers.length / 3;
var compareBits = Math.max(10, Math.min(20, Math.round(Math.log2(numVertices / 4))));
var bucketCount = 2 ** compareBits + 1;
if ((distances == null ? void 0 : distances.length) !== numVertices) {
distances = new Uint32Array(numVertices);
}
if (!countBuffer || countBuffer.length !== bucketCount) {
countBuffer = new Uint32Array(bucketCount);
} else {
countBuffer.fill(0);
}
var range = maxDist - minDist;
if (range < 1e-6) {
for(var i1 = 0; i1 < numVertices; ++i1){
distances[i1] = 0;
countBuffer[0]++;
}
} else if (chunks) {
var numChunks = chunks.length / 6;
binCount.fill(0);
for(var i2 = 0; i2 < numChunks; ++i2){
var x1 = chunks[i2 * 6 + 0];
var y1 = chunks[i2 * 6 + 1];
var z1 = chunks[i2 * 6 + 2];
var r = chunks[i2 * 6 + 3];
var d1 = x1 * dx + y1 * dy + z1 * dz - minDist;
var binMin = Math.max(0, Math.floor((d1 - r) * numBins / range));
var binMax = Math.min(numBins, Math.ceil((d1 + r) * numBins / range));
for(var j = binMin; j < binMax; ++j){
binCount[j]++;
}
}
var binTotal = binCount.reduce((a, b)=>a + b, 0);
for(var i3 = 0; i3 < numBins; ++i3){
binDivider[i3] = binCount[i3] / binTotal * bucketCount >>> 0;
}
for(var i4 = 0; i4 < numBins; ++i4){
binBase[i4] = i4 === 0 ? 0 : binBase[i4 - 1] + binDivider[i4 - 1];
}
var binRange = range / numBins;
var ii = 0;
for(var i5 = 0; i5 < numVertices; ++i5){
var x2 = centers[ii++];
var y2 = centers[ii++];
var z2 = centers[ii++];
var d2 = (x2 * dx + y2 * dy + z2 * dz - minDist) / binRange;
var bin = d2 >>> 0;
var sortKey = binBase[bin] + binDivider[bin] * (d2 - bin) >>> 0;
distances[i5] = sortKey;
countBuffer[sortKey]++;
}
} else {
var divider = 2 ** compareBits / range;
var ii1 = 0;
for(var i6 = 0; i6 < numVertices; ++i6){
var x3 = centers[ii1++];
var y3 = centers[ii1++];
var z3 = centers[ii1++];
var d3 = (x3 * dx + y3 * dy + z3 * dz - minDist) * divider;
var sortKey1 = d3 >>> 0;
distances[i6] = sortKey1;
countBuffer[sortKey1]++;
}
}
for(var i7 = 1; i7 < bucketCount; i7++){
countBuffer[i7] += countBuffer[i7 - 1];
}
for(var i8 = 0; i8 < numVertices; i8++){
var distance = distances[i8];
var destIndex = --countBuffer[distance];
order[destIndex] = i8;
}
var cameraDist = px * dx + py * dy + pz * dz;
var dist = (i)=>{
var o = order[i] * 3;
return centers[o++] * dx + centers[o++] * dy + centers[o] * dz - cameraDist;
};
var findZero = ()=>{
var result = binarySearch(0, numVertices - 1, (i)=>-dist(i));
return Math.min(numVertices, Math.abs(result));
};
var count = dist(numVertices - 1) >= 0 ? findZero() : numVertices;
if (mapping) {
for(var i9 = 0; i9 < numVertices; ++i9){
order[i9] = mapping[order[i9]];
}
}
self.postMessage({
order: order.buffer,
count
}, [
order.buffer
]);
order = null;
};
self.onmessage = (message)=>{
if (message.data.order) {
order = new Uint32Array(message.data.order);
}
if (message.data.centers) {
centers = new Float32Array(message.data.centers);
forceUpdate = true;
var initialized = false;
var numVertices = centers.length / 3;
for(var i = 0; i < numVertices; ++i){
var x = centers[i * 3 + 0];
var y = centers[i * 3 + 1];
var z = centers[i * 3 + 2];
if (isNaN(x)) {
x = centers[i * 3 + 0] = 0;
}
if (isNaN(y)) {
y = centers[i * 3 + 1] = 0;
}
if (isNaN(z)) {
z = centers[i * 3 + 2] = 0;
}
if (!initialized) {
initialized = true;
boundMin.x = boundMax.x = x;
boundMin.y = boundMax.y = y;
boundMin.z = boundMax.z = z;
} else {
boundMin.x = Math.min(boundMin.x, x);
boundMax.x = Math.max(boundMax.x, x);
boundMin.y = Math.min(boundMin.y, y);
boundMax.y = Math.max(boundMax.y, y);
boundMin.z = Math.min(boundMin.z, z);
boundMax.z = Math.max(boundMax.z, z);
}
}
if (!initialized) {
boundMin.x = boundMax.x = boundMin.y = boundMax.y = boundMin.z = boundMax.z = 0;
}
}
if (message.data.chunks) {
chunks = new Float32Array(message.data.chunks);
forceUpdate = true;
for(var i1 = 0; i1 < chunks.length / 6; ++i1){
var mx = chunks[i1 * 6 + 0];
var my = chunks[i1 * 6 + 1];
var mz = chunks[i1 * 6 + 2];
var Mx = chunks[i1 * 6 + 3];
var My = chunks[i1 * 6 + 4];
var Mz = chunks[i1 * 6 + 5];
chunks[i1 * 6 + 0] = (mx + Mx) * 0.5;
chunks[i1 * 6 + 1] = (my + My) * 0.5;
chunks[i1 * 6 + 2] = (mz + Mz) * 0.5;
chunks[i1 * 6 + 3] = Math.sqrt((Mx - mx) ** 2 + (My - my) ** 2 + (Mz - mz) ** 2) * 0.5;
}
}
if (message.data.hasOwnProperty('mapping')) {
mapping = message.data.mapping ? new Uint32Array(message.data.mapping) : null;
forceUpdate = true;
}
if (message.data.cameraPosition) cameraPosition = message.data.cameraPosition;
if (message.data.cameraDirection) cameraDirection = message.data.cameraDirection;
update();
};
}
class GSplatSorter extends EventHandler {
destroy() {
this.worker.terminate();
this.worker = null;
}
init(orderTexture, centers, chunks) {
this.orderTexture = orderTexture;
this.centers = centers.slice();
var orderBuffer = this.orderTexture.lock({
mode: TEXTURELOCK_READ
}).slice();
this.orderTexture.unlock();
for(var i = 0; i < orderBuffer.length; ++i){
orderBuffer[i] = i;
}
var obj = {
order: orderBuffer.buffer,
centers: centers.buffer,
chunks: chunks == null ? void 0 : chunks.buffer
};
var transfer = [
orderBuffer.buffer,
centers.buffer
].concat(chunks ? [
chunks.buffer
] : []);
this.worker.postMessage(obj, transfer);
}
setMapping(mapping) {
if (mapping) {
var centers = new Float32Array(mapping.length * 3);
for(var i = 0; i < mapping.length; ++i){
var src = mapping[i] * 3;
var dst = i * 3;
centers[dst + 0] = this.centers[src + 0];
centers[dst + 1] = this.centers[src + 1];
centers[dst + 2] = this.centers[src + 2];
}
this.worker.postMessage({
centers: centers.buffer,
mapping: mapping.buffer
}, [
centers.buffer,
mapping.buffer
]);
} else {
var centers1 = this.centers.slice();
this.worker.postMessage({
centers: centers1.buffer,
mapping: null
}, [
centers1.buffer
]);
}
}
setCamera(pos, dir) {
this.worker.postMessage({
cameraPosition: {
x: pos.x,
y: pos.y,
z: pos.z
},
cameraDirection: {
x: dir.x,
y: dir.y,
z: dir.z
}
});
}
constructor(){
super();
this.worker = new Worker(URL.createObjectURL(new Blob([
"(" + SortWorker.toString() + ")()"
], {
type: "application/javascript"
})));
this.worker.onmessage = (message)=>{
var newOrder = message.data.order;
var oldOrder = this.orderTexture._levels[0].buffer;
this.worker.postMessage({
order: oldOrder
}, [
oldOrder
]);
this.orderTexture._levels[0] = new Uint32Array(newOrder);
this.orderTexture.upload();
this.fire('updated', message.data.count);
};
}
}
export { GSplatSorter };