/** * Some types of possible point attribute data formats * * @class */ var PointAttributeTypes = { DATA_TYPE_DOUBLE: { ordinal: 0, name: "double", size: 8 }, DATA_TYPE_FLOAT: { ordinal: 1, name: "float", size: 4 }, DATA_TYPE_INT8: { ordinal: 2, name: "int8", size: 1 }, DATA_TYPE_UINT8: { ordinal: 3, name: "uint8", size: 1 }, DATA_TYPE_INT16: { ordinal: 4, name: "int16", size: 2 }, DATA_TYPE_UINT16: { ordinal: 5, name: "uint16", size: 2 }, DATA_TYPE_INT32: { ordinal: 6, name: "int32", size: 4 }, DATA_TYPE_UINT32: { ordinal: 7, name: "uint32", size: 4 }, DATA_TYPE_INT64: { ordinal: 8, name: "int64", size: 8 }, DATA_TYPE_UINT64: { ordinal: 9, name: "uint64", size: 8 } }; var i = 0; for (var obj in PointAttributeTypes) { PointAttributeTypes[i] = PointAttributeTypes[obj]; i++; } class PointAttribute { constructor(name, type, numElements) { this.name = name; this.type = type; this.numElements = numElements; this.byteSize = this.numElements * this.type.size; this.description = ""; this.range = [Infinity, -Infinity]; } } PointAttribute.POSITION_CARTESIAN = new PointAttribute("POSITION_CARTESIAN", PointAttributeTypes.DATA_TYPE_FLOAT, 3); PointAttribute.RGBA_PACKED = new PointAttribute("COLOR_PACKED", PointAttributeTypes.DATA_TYPE_INT8, 4); PointAttribute.COLOR_PACKED = PointAttribute.RGBA_PACKED; PointAttribute.RGB_PACKED = new PointAttribute("COLOR_PACKED", PointAttributeTypes.DATA_TYPE_INT8, 3); PointAttribute.NORMAL_FLOATS = new PointAttribute("NORMAL_FLOATS", PointAttributeTypes.DATA_TYPE_FLOAT, 3); PointAttribute.INTENSITY = new PointAttribute("INTENSITY", PointAttributeTypes.DATA_TYPE_UINT16, 1); PointAttribute.CLASSIFICATION = new PointAttribute("CLASSIFICATION", PointAttributeTypes.DATA_TYPE_UINT8, 1); PointAttribute.NORMAL_SPHEREMAPPED = new PointAttribute("NORMAL_SPHEREMAPPED", PointAttributeTypes.DATA_TYPE_UINT8, 2); PointAttribute.NORMAL_OCT16 = new PointAttribute("NORMAL_OCT16", PointAttributeTypes.DATA_TYPE_UINT8, 2); PointAttribute.NORMAL = new PointAttribute("NORMAL", PointAttributeTypes.DATA_TYPE_FLOAT, 3); PointAttribute.RETURN_NUMBER = new PointAttribute("RETURN_NUMBER", PointAttributeTypes.DATA_TYPE_UINT8, 1); PointAttribute.NUMBER_OF_RETURNS = new PointAttribute("NUMBER_OF_RETURNS", PointAttributeTypes.DATA_TYPE_UINT8, 1); PointAttribute.SOURCE_ID = new PointAttribute("SOURCE_ID", PointAttributeTypes.DATA_TYPE_UINT16, 1); PointAttribute.INDICES = new PointAttribute("INDICES", PointAttributeTypes.DATA_TYPE_UINT32, 1); PointAttribute.SPACING = new PointAttribute("SPACING", PointAttributeTypes.DATA_TYPE_FLOAT, 1); PointAttribute.GPS_TIME = new PointAttribute("GPS_TIME", PointAttributeTypes.DATA_TYPE_DOUBLE, 1); // import {Version} from "../../Version.js"; var typedArrayMapping = { "int8": Int8Array, "int16": Int16Array, "int32": Int32Array, "int64": Float64Array, "uint8": Uint8Array, "uint16": Uint16Array, "uint32": Uint32Array, "uint64": Float64Array, "float": Float32Array, "double": Float64Array }; Potree = {}; onmessage = function onmessage(event) { var { buffer, pointAttributes, scale, name, min, max, size, offset, numPoints } = event.data; var tStart = performance.now(); var view = new DataView(buffer); var attributeBuffers = {}; var attributeOffset = 0; var bytesPerPoint = 0; for (var pointAttribute of pointAttributes.attributes) { bytesPerPoint += pointAttribute.byteSize; } var gridSize = 32; var grid = new Uint32Array(gridSize ** 3); var toIndex = (x, y, z) => { // let dx = gridSize * (x - min.x) / size.x; // let dy = gridSize * (y - min.y) / size.y; // let dz = gridSize * (z - min.z) / size.z; // min is already subtracted var dx = gridSize * x / size.x; var dy = gridSize * y / size.y; var dz = gridSize * z / size.z; var ix = Math.min(parseInt(dx), gridSize - 1); var iy = Math.min(parseInt(dy), gridSize - 1); var iz = Math.min(parseInt(dz), gridSize - 1); var index = ix + iy * gridSize + iz * gridSize * gridSize; return index; }; var numOccupiedCells = 0; for (var _pointAttribute of pointAttributes.attributes) { if (["POSITION_CARTESIAN", "position"].includes(_pointAttribute.name)) { var buff = new ArrayBuffer(numPoints * 4 * 3); var positions = new Float32Array(buff); for (var j = 0; j < numPoints; j++) { var pointOffset = j * bytesPerPoint; var x = view.getInt32(pointOffset + attributeOffset + 0, true) * scale[0] + offset[0] - min.x; var y = view.getInt32(pointOffset + attributeOffset + 4, true) * scale[1] + offset[1] - min.y; var z = view.getInt32(pointOffset + attributeOffset + 8, true) * scale[2] + offset[2] - min.z; var index = toIndex(x, y, z); var 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)) { var _buff = new ArrayBuffer(numPoints * 4); var colors = new Uint8Array(_buff); for (var _j = 0; _j < numPoints; _j++) { var _pointOffset = _j * bytesPerPoint; var r = view.getUint16(_pointOffset + attributeOffset + 0, true); var g = view.getUint16(_pointOffset + attributeOffset + 2, true); var 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 { var _buff2 = new ArrayBuffer(numPoints * 4); var f32 = new Float32Array(_buff2); var TypedArray = typedArrayMapping[_pointAttribute.type.name]; preciseBuffer = new TypedArray(numPoints); var [_offset, _scale] = [0, 1]; var 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 }; var getter = getterMap[_pointAttribute.type.name].bind(view); // compute offset and scale to pack larger types into 32 bit floats if (_pointAttribute.type.size > 4) { var [amin, amax] = _pointAttribute.range; _offset = amin; _scale = 1 / (amax - amin); } for (var _j2 = 0; _j2 < numPoints; _j2++) { var _pointOffset2 = _j2 * bytesPerPoint; var value = getter(_pointOffset2 + attributeOffset, true); f32[_j2] = (value - _offset) * _scale; preciseBuffer[_j2] = value; } attributeBuffers[_pointAttribute.name] = { buffer: _buff2, preciseBuffer: preciseBuffer, attribute: _pointAttribute, offset: _offset, scale: _scale }; } attributeOffset += _pointAttribute.byteSize; } var occupancy = parseInt(numPoints / numOccupiedCells); // console.log(`${name}: #points: ${numPoints}: #occupiedCells: ${numOccupiedCells}, occupancy: ${occupancy} points/cell`); { // add indices var _buff3 = new ArrayBuffer(numPoints * 4); var indices = new Uint32Array(_buff3); for (var i = 0; i < numPoints; i++) { indices[i] = i; } attributeBuffers["INDICES"] = { buffer: _buff3, attribute: PointAttribute.INDICES }; } { // handle attribute vectors var vectors = pointAttributes.vectors; for (var vector of vectors) { var { name: _name, attributes } = vector; var numVectorElements = attributes.length; var _buffer = new ArrayBuffer(numVectorElements * numPoints * 4); var _f = new Float32Array(_buffer); var iElement = 0; for (var sourceName of attributes) { var sourceBuffer = attributeBuffers[sourceName]; var { offset: _offset2, scale: _scale2 } = sourceBuffer; var _view = new DataView(sourceBuffer.buffer); var _getter = _view.getFloat32.bind(_view); for (var _j3 = 0; _j3 < numPoints; _j3++) { var _value = _getter(_j3 * 4, true); _f[_j3 * numVectorElements + iElement] = _value / _scale2 + _offset2; } iElement++; } var 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)}`); var message = { buffer: buffer, attributeBuffers: attributeBuffers, density: occupancy }; var transferables = []; for (var property in message.attributeBuffers) { transferables.push(message.attributeBuffers[property].buffer); } transferables.push(buffer); postMessage(message, transferables); };