@pnext/three-loader
Version:
Potree loader for ThreeJS, converted and adapted to Typescript.
218 lines (170 loc) • 6.6 kB
JavaScript
import { PointAttribute, PointAttributeTypes } from './point-attributes.ts';
const typedArrayMapping = {
int8: Int8Array,
int16: Int16Array,
int32: Int32Array,
int64: Float64Array,
uint8: Uint8Array,
uint16: Uint16Array,
uint32: Uint32Array,
uint64: Float64Array,
float: Float32Array,
double: Float64Array,
};
onmessage = function (event) {
let { buffer, pointAttributes, scale, name, min, max, size, offset, numPoints } = event.data;
let view = new DataView(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 tightBoxMin = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY];
let tightBoxMax = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY];
for (let pointAttribute of pointAttributes.attributes) {
if (['POSITION_CARTESIAN', 'position'].includes(pointAttribute.name)) {
let buff = new ArrayBuffer(numPoints * 4 * 3);
let positions = new Float32Array(buff);
for (let j = 0; j < numPoints; j++) {
let pointOffset = j * bytesPerPoint;
let x =
view.getInt32(pointOffset + attributeOffset + 0, true) * scale[0] + offset[0] - min.x;
let y =
view.getInt32(pointOffset + attributeOffset + 4, true) * scale[1] + offset[1] - min.y;
let z =
view.getInt32(pointOffset + attributeOffset + 8, true) * scale[2] + offset[2] - min.z;
tightBoxMin[0] = Math.min(tightBoxMin[0], x);
tightBoxMin[1] = Math.min(tightBoxMin[1], y);
tightBoxMin[2] = Math.min(tightBoxMin[2], z);
tightBoxMax[0] = Math.max(tightBoxMax[0], x);
tightBoxMax[1] = Math.max(tightBoxMax[1], y);
tightBoxMax[2] = Math.max(tightBoxMax[2], 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;
}
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 pointOffset = j * bytesPerPoint;
let r = view.getUint16(pointOffset + attributeOffset + 0, true);
let g = view.getUint16(pointOffset + attributeOffset + 2, true);
let b = view.getUint16(pointOffset + attributeOffset + 4, true);
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;
}
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];
let 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(pointOffset + attributeOffset, true);
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);
{
// 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 message = {
buffer: buffer,
attributeBuffers: attributeBuffers,
density: occupancy,
tightBoundingBox: { min: tightBoxMin, max: tightBoxMax },
};
let transferables = [];
for (let property in message.attributeBuffers) {
transferables.push(message.attributeBuffers[property].buffer);
}
transferables.push(buffer);
postMessage(message, transferables);
};