@sauskylark/potree
Version:
WebGL point cloud viewer
431 lines (315 loc) • 14.5 kB
JavaScript
// import {Version} from "../../Version.js";
import {PointAttributes, PointAttribute, PointAttributeTypes} from "../../../loader/PointAttributes.js";
import {BrotliDecode} from "../../../../libs/brotli/decode.js";
const typedArrayMapping = {
"int8": Int8Array,
"int16": Int16Array,
"int32": Int32Array,
"int64": Float64Array,
"uint8": Uint8Array,
"uint16": Uint16Array,
"uint32": Uint32Array,
"uint64": Float64Array,
"float": Float32Array,
"double": Float64Array,
};
Potree = {};
function dealign24b(mortoncode){
// see https://stackoverflow.com/questions/45694690/how-i-can-remove-all-odds-bits-in-c
// input alignment of desired bits
// ..a..b..c..d..e..f..g..h..i..j..k..l..m..n..o..p
let x = mortoncode;
// ..a..b..c..d..e..f..g..h..i..j..k..l..m..n..o..p ..a..b..c..d..e..f..g..h..i..j..k..l..m..n..o..p
// ..a.....c.....e.....g.....i.....k.....m.....o... .....b.....d.....f.....h.....j.....l.....n.....p
// ....a.....c.....e.....g.....i.....k.....m.....o. .....b.....d.....f.....h.....j.....l.....n.....p
x = ((x & 0b001000001000001000001000) >> 2) | ((x & 0b000001000001000001000001) >> 0);
// ....ab....cd....ef....gh....ij....kl....mn....op ....ab....cd....ef....gh....ij....kl....mn....op
// ....ab..........ef..........ij..........mn...... ..........cd..........gh..........kl..........op
// ........ab..........ef..........ij..........mn.. ..........cd..........gh..........kl..........op
x = ((x & 0b000011000000000011000000) >> 4) | ((x & 0b000000000011000000000011) >> 0);
// ........abcd........efgh........ijkl........mnop ........abcd........efgh........ijkl........mnop
// ........abcd....................ijkl............ ....................efgh....................mnop
// ................abcd....................ijkl.... ....................efgh....................mnop
x = ((x & 0b000000001111000000000000) >> 8) | ((x & 0b000000000000000000001111) >> 0);
// ................abcdefgh................ijklmnop ................abcdefgh................ijklmnop
// ................abcdefgh........................ ........................................ijklmnop
// ................................abcdefgh........ ........................................ijklmnop
x = ((x & 0b000000000000000000000000) >> 16) | ((x & 0b000000000000000011111111) >> 0);
// sucessfully realigned!
//................................abcdefghijklmnop
return x;
}
let mask_b0 = new Uint8Array([0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3]);
onmessage = function (event) {
let {pointAttributes, scale, name, min, max, size, offset, numPoints} = event.data;
let tStart = performance.now();
let buffer;
if(numPoints === 0){
buffer = {buffer: new ArrayBuffer(0)};
}else{
try{
buffer = BrotliDecode(new Int8Array(event.data.buffer));
}catch(e){
buffer = {buffer: new ArrayBuffer(numPoints * (pointAttributes.byteSize + 12))};
console.error(`problem with node ${name}: `, e);
}
}
let view = new DataView(buffer.buffer);
let attributeBuffers = {};
let attributeOffset = 0;
let bytesPerPoint = 0;
for (let pointAttribute of pointAttributes.attributes) {
bytesPerPoint += pointAttribute.byteSize;
}
let gridSize = 32;
let grid = new Uint32Array(gridSize ** 3);
let toIndex = (x, y, z) => {
// min is already subtracted
let dx = gridSize * x / size.x;
let dy = gridSize * y / size.y;
let dz = gridSize * z / size.z;
let ix = Math.min(parseInt(dx), gridSize - 1);
let iy = Math.min(parseInt(dy), gridSize - 1);
let iz = Math.min(parseInt(dz), gridSize - 1);
let index = ix + iy * gridSize + iz * gridSize * gridSize;
return index;
};
let numOccupiedCells = 0;
let byteOffset = 0;
for (let pointAttribute of pointAttributes.attributes) {
if(["POSITION_CARTESIAN", "position"].includes(pointAttribute.name)){
// let tStart = performance.now();
let buff = new ArrayBuffer(numPoints * 4 * 3);
let positions = new Float32Array(buff);
for (let j = 0; j < numPoints; j++) {
let mc_0 = view.getUint32(byteOffset + 4, true);
let mc_1 = view.getUint32(byteOffset + 0, true);
let mc_2 = view.getUint32(byteOffset + 12, true);
let mc_3 = view.getUint32(byteOffset + 8, true);
byteOffset += 16;
let X = dealign24b((mc_3 & 0x00FFFFFF) >>> 0)
| (dealign24b(((mc_3 >>> 24) | (mc_2 << 8)) >>> 0) << 8);
let Y = dealign24b((mc_3 & 0x00FFFFFF) >>> 1)
| (dealign24b(((mc_3 >>> 24) | (mc_2 << 8)) >>> 1) << 8)
let Z = dealign24b((mc_3 & 0x00FFFFFF) >>> 2)
| (dealign24b(((mc_3 >>> 24) | (mc_2 << 8)) >>> 2) << 8)
if(mc_1 != 0 || mc_2 != 0){
X = X | (dealign24b((mc_1 & 0x00FFFFFF) >>> 0) << 16)
| (dealign24b(((mc_1 >>> 24) | (mc_0 << 8)) >>> 0) << 24);
Y = Y | (dealign24b((mc_1 & 0x00FFFFFF) >>> 1) << 16)
| (dealign24b(((mc_1 >>> 24) | (mc_0 << 8)) >>> 1) << 24);
Z = Z | (dealign24b((mc_1 & 0x00FFFFFF) >>> 2) << 16)
| (dealign24b(((mc_1 >>> 24) | (mc_0 << 8)) >>> 2) << 24);
}
// =======================
// VERIFY AGAINST LOOP VERSION
// =======================
// let reference;
// { // correct reference
// let mc_upper = view.getBigUint64(byteOffset + 0, true);
// let mc_lower = view.getBigUint64(byteOffset + 8, true);
// let X = 0n;
// let Y = 0n;
// let Z = 0n;
// for(let k = 0n; k < 16n; k++){
// let mask_lower = (mc_lower >> (3n * k)) & 0b111n;
// let mask_upper = (mc_upper >> (3n * k)) & 0b111n;
// X = X | (((mask_lower >> 0n) & 0b001n) << k);
// X = X | ((((mask_upper >> 0n) & 0b001n) << k) << 16n);
// Y = Y | (((mask_lower >> 1n) & 0b001n) << k);
// Y = Y | ((((mask_upper >> 1n) & 0b001n) << k) << 16n);
// Z = Z | (((mask_lower >> 2n) & 0b001n) << k);
// Z = Z | ((((mask_upper >> 2n) & 0b001n) << k) << 16n);
// }
// reference = [X, Y, Z];
// }
// //dbgad += parseInt(reference[2]);
// let [rX, rY, rZ] = reference;
// if(X !== parseInt(rX)){
// debugger;
// }
// if(Y !== parseInt(rY)){
// debugger;
// }
// if(Z !== parseInt(rZ)){
// debugger;
// }
// let mc_upper = view.getBigUint64(byteOffset + 0, true);
// let mc_lower = view.getBigUint64(byteOffset + 8, true);
// byteOffset += 16;
// =======================
// MAGIC NUMBERS 32BIT
// =======================
// let mc0 = parseInt((mc_lower >> 0n) & 0x00FFFFFFn);
// let mc1 = parseInt((mc_lower >> 24n) & 0x00FFFFFFn);
// let mc2 = parseInt((mc_lower >> 48n) & 0x00FFFFFFn);
// let X = dealign24b(mc0 >> 0) | (dealign24b(mc1 >> 0) << 8) | (dealign24b(mc2 >> 0) << 16);
// let Y = dealign24b(mc0 >> 1) | (dealign24b(mc1 >> 1) << 8) | (dealign24b(mc2 >> 1) << 16);
// let Z = dealign24b(mc0 >> 2) | (dealign24b(mc1 >> 2) << 8) | (dealign24b(mc2 >> 2) << 16);
// =======================
// MAGIC NUMBERS BIGINT
// =======================
// let X = dealign(mc_lower >> 0n);// | (dealign(mc_upper >> 0n) << 16n);
// let Y = dealign(mc_lower >> 1n);// | (dealign(mc_upper >> 1n) << 16n);
// let Z = dealign(mc_lower >> 2n);// | (dealign(mc_upper >> 2n) << 16n);
// =======================
// LOOP
// =======================
// let X = 0n;
// let Y = 0n;
// let Z = 0n;
// for(let k = 0n; k < 16n; k++){
// let mask_lower = (mc_lower >> (3n * k)) & 0b111n;
// let mask_upper = (mc_upper >> (3n * k)) & 0b111n;
// X = X | (((mask_lower >> 0n) & 0b001n) << k);
// X = X | ((((mask_upper >> 0n) & 0b001n) << k) << 16n);
// Y = Y | (((mask_lower >> 1n) & 0b001n) << k);
// Y = Y | ((((mask_upper >> 1n) & 0b001n) << k) << 16n);
// Z = Z | (((mask_lower >> 2n) & 0b001n) << k);
// Z = Z | ((((mask_upper >> 2n) & 0b001n) << k) << 16n);
// }
let x = parseInt(X) * scale[0] + offset[0] - min.x;
let y = parseInt(Y) * scale[1] + offset[1] - min.y;
let z = parseInt(Z) * scale[2] + offset[2] - min.z;
let index = toIndex(x, y, z);
let count = grid[index]++;
if(count === 0){
numOccupiedCells++;
}
positions[3 * j + 0] = x;
positions[3 * j + 1] = y;
positions[3 * j + 2] = z;
}
// let duration = performance.now() - tStart;
// console.log(`xyz: ${duration.toFixed(1)}ms`);
attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute };
}else if(["RGBA", "rgba"].includes(pointAttribute.name)){
let buff = new ArrayBuffer(numPoints * 4);
let colors = new Uint8Array(buff);
// for (let j = 0; j < numPoints; j++) {
// let r = view.getUint16(byteOffset + 0, true);
// let g = view.getUint16(byteOffset + 2, true);
// let b = view.getUint16(byteOffset + 4, true);
// byteOffset += 6;
// colors[4 * j + 0] = r > 255 ? r / 256 : r;
// colors[4 * j + 1] = g > 255 ? g / 256 : g;
// colors[4 * j + 2] = b > 255 ? b / 256 : b;
// }
// let tStart = performance.now();
for (let j = 0; j < numPoints; j++) {
let mc_0 = view.getUint32(byteOffset + 4, true);
let mc_1 = view.getUint32(byteOffset + 0, true);
byteOffset += 8;
let r = dealign24b((mc_1 & 0x00FFFFFF) >>> 0)
| (dealign24b(((mc_1 >>> 24) | (mc_0 << 8)) >>> 0) << 8);
let g = dealign24b((mc_1 & 0x00FFFFFF) >>> 1)
| (dealign24b(((mc_1 >>> 24) | (mc_0 << 8)) >>> 1) << 8);
let b = dealign24b((mc_1 & 0x00FFFFFF) >>> 2)
| (dealign24b(((mc_1 >>> 24) | (mc_0 << 8)) >>> 2) << 8);
// let bits = mask_b0[mc_1 >>> 24];
// if(((r >> 8) & 0b11) !== bits){
// debugger;
// }
// let r = dealign24b(mc0 >> 0) | (dealign24b(mc1 >> 0) << 8);
// let g = dealign24b(mc0 >> 1) | (dealign24b(mc1 >> 1) << 8);
// let b = dealign24b(mc0 >> 2) | (dealign24b(mc1 >> 2) << 8);
colors[4 * j + 0] = r > 255 ? r / 256 : r;
colors[4 * j + 1] = g > 255 ? g / 256 : g;
colors[4 * j + 2] = b > 255 ? b / 256 : b;
}
// let duration = performance.now() - tStart;
// console.log(`rgb: ${duration.toFixed(1)}ms`);
attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute };
}else{
let buff = new ArrayBuffer(numPoints * 4);
let f32 = new Float32Array(buff);
let TypedArray = typedArrayMapping[pointAttribute.type.name];
preciseBuffer = new TypedArray(numPoints);
let [offset, scale] = [0, 1];
const getterMap = {
"int8": view.getInt8,
"int16": view.getInt16,
"int32": view.getInt32,
// "int64": view.getInt64,
"uint8": view.getUint8,
"uint16": view.getUint16,
"uint32": view.getUint32,
// "uint64": view.getUint64,
"float": view.getFloat32,
"double": view.getFloat64,
};
const getter = getterMap[pointAttribute.type.name].bind(view);
// compute offset and scale to pack larger types into 32 bit floats
if(pointAttribute.type.size > 4){
let [amin, amax] = pointAttribute.range;
offset = amin;
scale = 1 / (amax - amin);
}
for(let j = 0; j < numPoints; j++){
// let pointOffset = j * bytesPerPoint;
let value = getter(byteOffset, true);
byteOffset += pointAttribute.byteSize;
f32[j] = (value - offset) * scale;
preciseBuffer[j] = value;
}
attributeBuffers[pointAttribute.name] = {
buffer: buff,
preciseBuffer: preciseBuffer,
attribute: pointAttribute,
offset: offset,
scale: scale,
};
}
// attributeOffset += pointAttribute.byteSize;
}
let occupancy = parseInt(numPoints / numOccupiedCells);
// console.log(`${name}: #points: ${numPoints}: #occupiedCells: ${numOccupiedCells}, occupancy: ${occupancy} points/cell`);
{ // add indices
let buff = new ArrayBuffer(numPoints * 4);
let indices = new Uint32Array(buff);
for (let i = 0; i < numPoints; i++) {
indices[i] = i;
}
attributeBuffers["INDICES"] = { buffer: buff, attribute: PointAttribute.INDICES };
}
{ // handle attribute vectors
let vectors = pointAttributes.vectors;
for(let vector of vectors){
let {name, attributes} = vector;
let numVectorElements = attributes.length;
let buffer = new ArrayBuffer(numVectorElements * numPoints * 4);
let f32 = new Float32Array(buffer);
let iElement = 0;
for(let sourceName of attributes){
let sourceBuffer = attributeBuffers[sourceName];
let {offset, scale} = sourceBuffer;
let view = new DataView(sourceBuffer.buffer);
const getter = view.getFloat32.bind(view);
for(let j = 0; j < numPoints; j++){
let value = getter(j * 4, true);
f32[j * numVectorElements + iElement] = (value / scale) + offset;
}
iElement++;
}
let vecAttribute = new PointAttribute(name, PointAttributeTypes.DATA_TYPE_FLOAT, 3);
attributeBuffers[name] = {
buffer: buffer,
attribute: vecAttribute,
};
}
}
let duration = performance.now() - tStart;
let pointsPerMs = numPoints / duration;
// console.log(`duration: ${duration.toFixed(1)}ms, #points: ${numPoints}, points/ms: ${pointsPerMs.toFixed(1)}`);
let message = {
buffer: buffer,
attributeBuffers: attributeBuffers,
density: occupancy,
};
let transferables = [];
for (let property in message.attributeBuffers) {
transferables.push(message.attributeBuffers[property].buffer);
}
// transferables.push(buffer);
postMessage(message, transferables);
};