UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

337 lines (334 loc) 10.2 kB
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 };