update
This commit is contained in:
303
libs/potree/workers/2.0/DecoderWorker.js
Normal file
303
libs/potree/workers/2.0/DecoderWorker.js
Normal file
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* Some types of possible point attribute data formats
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
const 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}
|
||||
};
|
||||
|
||||
let i = 0;
|
||||
for (let 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);
|
||||
|
||||
const typedArrayMapping = {
|
||||
"int8": Int8Array,
|
||||
"int16": Int16Array,
|
||||
"int32": Int32Array,
|
||||
"int64": Float64Array,
|
||||
"uint8": Uint8Array,
|
||||
"uint16": Uint16Array,
|
||||
"uint32": Uint32Array,
|
||||
"uint64": Float64Array,
|
||||
"float": Float32Array,
|
||||
"double": Float64Array,
|
||||
};
|
||||
|
||||
Potree = {};
|
||||
|
||||
onmessage = function (event) {
|
||||
|
||||
let {buffer, pointAttributes, scale, name, min, max, size, offset, numPoints} = event.data;
|
||||
|
||||
let tStart = performance.now();
|
||||
|
||||
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) => {
|
||||
// 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
|
||||
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;
|
||||
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;
|
||||
|
||||
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];
|
||||
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);
|
||||
// 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);
|
||||
};
|
||||
2496
libs/potree/workers/2.0/DecoderWorker_brotli.js
Normal file
2496
libs/potree/workers/2.0/DecoderWorker_brotli.js
Normal file
File diff suppressed because one or more lines are too long
444
libs/potree/workers/BinaryDecoderWorker.js
Normal file
444
libs/potree/workers/BinaryDecoderWorker.js
Normal file
@@ -0,0 +1,444 @@
|
||||
class Version{
|
||||
|
||||
constructor(version){
|
||||
this.version = version;
|
||||
let vmLength = (version.indexOf('.') === -1) ? version.length : version.indexOf('.');
|
||||
this.versionMajor = parseInt(version.substr(0, vmLength));
|
||||
this.versionMinor = parseInt(version.substr(vmLength + 1));
|
||||
if (this.versionMinor.length === 0) {
|
||||
this.versionMinor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
newerThan(version){
|
||||
let v = new Version(version);
|
||||
|
||||
if (this.versionMajor > v.versionMajor) {
|
||||
return true;
|
||||
} else if (this.versionMajor === v.versionMajor && this.versionMinor > v.versionMinor) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
equalOrHigher(version){
|
||||
let v = new Version(version);
|
||||
|
||||
if (this.versionMajor > v.versionMajor) {
|
||||
return true;
|
||||
} else if (this.versionMajor === v.versionMajor && this.versionMinor >= v.versionMinor) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
upTo(version){
|
||||
return !this.newerThan(version);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Some types of possible point attribute data formats
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
const 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}
|
||||
};
|
||||
|
||||
let i = 0;
|
||||
for (let 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);
|
||||
|
||||
const typedArrayMapping = {
|
||||
"int8": Int8Array,
|
||||
"int16": Int16Array,
|
||||
"int32": Int32Array,
|
||||
"int64": Float64Array,
|
||||
"uint8": Uint8Array,
|
||||
"uint16": Uint16Array,
|
||||
"uint32": Uint32Array,
|
||||
"uint64": Float64Array,
|
||||
"float": Float32Array,
|
||||
"double": Float64Array,
|
||||
};
|
||||
|
||||
Potree = {};
|
||||
|
||||
onmessage = function (event) {
|
||||
|
||||
performance.mark("binary-decoder-start");
|
||||
|
||||
let buffer = event.data.buffer;
|
||||
let pointAttributes = event.data.pointAttributes;
|
||||
let numPoints = buffer.byteLength / pointAttributes.byteSize;
|
||||
let view = new DataView(buffer);
|
||||
let version = new Version(event.data.version);
|
||||
let nodeOffset = event.data.offset;
|
||||
let scale = event.data.scale;
|
||||
let spacing = event.data.spacing;
|
||||
let hasChildren = event.data.hasChildren;
|
||||
let name = event.data.name;
|
||||
|
||||
let tightBoxMin = [ Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY ];
|
||||
let tightBoxMax = [ Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY ];
|
||||
let mean = [0, 0, 0];
|
||||
|
||||
|
||||
let attributeBuffers = {};
|
||||
let inOffset = 0;
|
||||
for (let pointAttribute of pointAttributes.attributes) {
|
||||
|
||||
if (pointAttribute.name === "POSITION_CARTESIAN") {
|
||||
let buff = new ArrayBuffer(numPoints * 4 * 3);
|
||||
let positions = new Float32Array(buff);
|
||||
|
||||
for (let j = 0; j < numPoints; j++) {
|
||||
let x, y, z;
|
||||
|
||||
if (version.newerThan('1.3')) {
|
||||
x = (view.getUint32(inOffset + j * pointAttributes.byteSize + 0, true) * scale);
|
||||
y = (view.getUint32(inOffset + j * pointAttributes.byteSize + 4, true) * scale);
|
||||
z = (view.getUint32(inOffset + j * pointAttributes.byteSize + 8, true) * scale);
|
||||
} else {
|
||||
x = view.getFloat32(j * pointAttributes.byteSize + 0, true) + nodeOffset[0];
|
||||
y = view.getFloat32(j * pointAttributes.byteSize + 4, true) + nodeOffset[1];
|
||||
z = view.getFloat32(j * pointAttributes.byteSize + 8, true) + nodeOffset[2];
|
||||
}
|
||||
|
||||
positions[3 * j + 0] = x;
|
||||
positions[3 * j + 1] = y;
|
||||
positions[3 * j + 2] = z;
|
||||
|
||||
mean[0] += x / numPoints;
|
||||
mean[1] += y / numPoints;
|
||||
mean[2] += z / numPoints;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute };
|
||||
} else if (pointAttribute.name === "rgba") {
|
||||
let buff = new ArrayBuffer(numPoints * 4);
|
||||
let colors = new Uint8Array(buff);
|
||||
|
||||
for (let j = 0; j < numPoints; j++) {
|
||||
colors[4 * j + 0] = view.getUint8(inOffset + j * pointAttributes.byteSize + 0);
|
||||
colors[4 * j + 1] = view.getUint8(inOffset + j * pointAttributes.byteSize + 1);
|
||||
colors[4 * j + 2] = view.getUint8(inOffset + j * pointAttributes.byteSize + 2);
|
||||
}
|
||||
|
||||
attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute };
|
||||
} else if (pointAttribute.name === "NORMAL_SPHEREMAPPED") {
|
||||
let buff = new ArrayBuffer(numPoints * 4 * 3);
|
||||
let normals = new Float32Array(buff);
|
||||
|
||||
for (let j = 0; j < numPoints; j++) {
|
||||
let bx = view.getUint8(inOffset + j * pointAttributes.byteSize + 0);
|
||||
let by = view.getUint8(inOffset + j * pointAttributes.byteSize + 1);
|
||||
|
||||
let ex = bx / 255;
|
||||
let ey = by / 255;
|
||||
|
||||
let nx = ex * 2 - 1;
|
||||
let ny = ey * 2 - 1;
|
||||
let nz = 1;
|
||||
let nw = -1;
|
||||
|
||||
let l = (nx * (-nx)) + (ny * (-ny)) + (nz * (-nw));
|
||||
nz = l;
|
||||
nx = nx * Math.sqrt(l);
|
||||
ny = ny * Math.sqrt(l);
|
||||
|
||||
nx = nx * 2;
|
||||
ny = ny * 2;
|
||||
nz = nz * 2 - 1;
|
||||
|
||||
normals[3 * j + 0] = nx;
|
||||
normals[3 * j + 1] = ny;
|
||||
normals[3 * j + 2] = nz;
|
||||
}
|
||||
|
||||
attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute };
|
||||
} else if (pointAttribute.name === "NORMAL_OCT16") {
|
||||
let buff = new ArrayBuffer(numPoints * 4 * 3);
|
||||
let normals = new Float32Array(buff);
|
||||
|
||||
for (let j = 0; j < numPoints; j++) {
|
||||
let bx = view.getUint8(inOffset + j * pointAttributes.byteSize + 0);
|
||||
let by = view.getUint8(inOffset + j * pointAttributes.byteSize + 1);
|
||||
|
||||
let u = (bx / 255) * 2 - 1;
|
||||
let v = (by / 255) * 2 - 1;
|
||||
|
||||
let z = 1 - Math.abs(u) - Math.abs(v);
|
||||
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
if (z >= 0) {
|
||||
x = u;
|
||||
y = v;
|
||||
} else {
|
||||
x = -(v / Math.sign(v) - 1) / Math.sign(u);
|
||||
y = -(u / Math.sign(u) - 1) / Math.sign(v);
|
||||
}
|
||||
|
||||
let length = Math.sqrt(x * x + y * y + z * z);
|
||||
x = x / length;
|
||||
y = y / length;
|
||||
z = z / length;
|
||||
|
||||
normals[3 * j + 0] = x;
|
||||
normals[3 * j + 1] = y;
|
||||
normals[3 * j + 2] = z;
|
||||
}
|
||||
|
||||
attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute };
|
||||
} else if (pointAttribute.name === "NORMAL") {
|
||||
let buff = new ArrayBuffer(numPoints * 4 * 3);
|
||||
let normals = new Float32Array(buff);
|
||||
|
||||
for (let j = 0; j < numPoints; j++) {
|
||||
let x = view.getFloat32(inOffset + j * pointAttributes.byteSize + 0, true);
|
||||
let y = view.getFloat32(inOffset + j * pointAttributes.byteSize + 4, true);
|
||||
let z = view.getFloat32(inOffset + j * pointAttributes.byteSize + 8, true);
|
||||
|
||||
normals[3 * j + 0] = x;
|
||||
normals[3 * j + 1] = y;
|
||||
normals[3 * j + 2] = z;
|
||||
}
|
||||
|
||||
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 [min, max] = [Infinity, -Infinity];
|
||||
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){
|
||||
for(let j = 0; j < numPoints; j++){
|
||||
let value = getter(inOffset + j * pointAttributes.byteSize, true);
|
||||
|
||||
if(!Number.isNaN(value)){
|
||||
min = Math.min(min, value);
|
||||
max = Math.max(max, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(pointAttribute.initialRange != null){
|
||||
offset = pointAttribute.initialRange[0];
|
||||
scale = 1 / (pointAttribute.initialRange[1] - pointAttribute.initialRange[0]);
|
||||
}else {
|
||||
offset = min;
|
||||
scale = 1 / (max - min);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for(let j = 0; j < numPoints; j++){
|
||||
let value = getter(inOffset + j * pointAttributes.byteSize, true);
|
||||
|
||||
if(!Number.isNaN(value)){
|
||||
min = Math.min(min, value);
|
||||
max = Math.max(max, value);
|
||||
}
|
||||
|
||||
f32[j] = (value - offset) * scale;
|
||||
preciseBuffer[j] = value;
|
||||
}
|
||||
|
||||
pointAttribute.range = [min, max];
|
||||
|
||||
attributeBuffers[pointAttribute.name] = {
|
||||
buffer: buff,
|
||||
preciseBuffer: preciseBuffer,
|
||||
attribute: pointAttribute,
|
||||
offset: offset,
|
||||
scale: scale,
|
||||
};
|
||||
}
|
||||
|
||||
inOffset += pointAttribute.byteSize;
|
||||
}
|
||||
|
||||
{ // 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,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
performance.mark("binary-decoder-end");
|
||||
|
||||
// { // print timings
|
||||
// //performance.measure("spacing", "spacing-start", "spacing-end");
|
||||
// performance.measure("binary-decoder", "binary-decoder-start", "binary-decoder-end");
|
||||
// let measure = performance.getEntriesByType("measure")[0];
|
||||
// let dpp = 1000 * measure.duration / numPoints;
|
||||
// let pps = parseInt(numPoints / (measure.duration / 1000));
|
||||
// let debugMessage = `${measure.duration.toFixed(3)} ms, ${numPoints} points, ${pps.toLocaleString()} points/sec`;
|
||||
// console.log(debugMessage);
|
||||
// }
|
||||
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
let message = {
|
||||
buffer: buffer,
|
||||
mean: mean,
|
||||
attributeBuffers: attributeBuffers,
|
||||
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);
|
||||
};
|
||||
213
libs/potree/workers/EptBinaryDecoderWorker.js
Normal file
213
libs/potree/workers/EptBinaryDecoderWorker.js
Normal file
@@ -0,0 +1,213 @@
|
||||
function parseEpt(event) {
|
||||
let buffer = event.data.buffer;
|
||||
let view = new DataView(buffer);
|
||||
let schema = event.data.schema;
|
||||
let scale = event.data.scale;
|
||||
let offset = event.data.offset;
|
||||
let mins = event.data.mins;
|
||||
|
||||
let dimensions = schema.reduce((p, c) => {
|
||||
p[c.name] = c;
|
||||
return p;
|
||||
}, { });
|
||||
|
||||
let dimOffset = (name) => {
|
||||
let offset = 0;
|
||||
for (var i = 0; i < schema.length; ++i) {
|
||||
if (schema[i].name == name) return offset;
|
||||
offset += schema[i].size;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
let getExtractor = (name) => {
|
||||
let offset = dimOffset(name);
|
||||
let type = dimensions[name].type;
|
||||
let size = dimensions[name].size;
|
||||
|
||||
if (type == 'signed') switch (size) {
|
||||
case 1: return (p) => view.getInt8(p + offset);
|
||||
case 2: return (p) => view.getInt16(p + offset, true);
|
||||
case 4: return (p) => view.getInt32(p + offset, true);
|
||||
case 8: return (p) => view.getInt64(p + offset, true);
|
||||
}
|
||||
if (type == 'unsigned') switch (size) {
|
||||
case 1: return (p) => view.getUint8(p + offset);
|
||||
case 2: return (p) => view.getUint16(p + offset, true);
|
||||
case 4: return (p) => view.getUint32(p + offset, true);
|
||||
case 8: return (p) => view.getUint64(p + offset, true);
|
||||
}
|
||||
if (type == 'float') switch (size) {
|
||||
case 4: return (p) => view.getFloat32(p + offset, true);
|
||||
case 8: return (p) => view.getFloat64(p + offset, true);
|
||||
}
|
||||
|
||||
let str = JSON.stringify(dimensions[name]);
|
||||
throw new Error(`Invalid dimension specification for ${name}: ${str}`);
|
||||
};
|
||||
|
||||
let pointSize = schema.reduce((p, c) => p + c.size, 0);
|
||||
let numPoints = buffer.byteLength / pointSize;
|
||||
|
||||
let xyzBuffer, rgbBuffer, intensityBuffer, classificationBuffer,
|
||||
returnNumberBuffer, numberOfReturnsBuffer, pointSourceIdBuffer;
|
||||
let xyz, rgb, intensity, classification, returnNumber, numberOfReturns,
|
||||
pointSourceId;
|
||||
let xyzExtractor, rgbExtractor, intensityExtractor, classificationExtractor,
|
||||
returnNumberExtractor, numberOfReturnsExtractor, pointSourceIdExtractor;
|
||||
let twoByteColor = false;
|
||||
|
||||
if (dimensions['X'] && dimensions['Y'] && dimensions['Z']) {
|
||||
xyzBuffer = new ArrayBuffer(numPoints * 4 * 3);
|
||||
xyz = new Float32Array(xyzBuffer);
|
||||
xyzExtractor = [
|
||||
getExtractor('X'),
|
||||
getExtractor('Y'),
|
||||
getExtractor('Z')
|
||||
];
|
||||
}
|
||||
|
||||
if (dimensions['Red'] && dimensions['Green'] && dimensions['Blue']) {
|
||||
rgbBuffer = new ArrayBuffer(numPoints * 4);
|
||||
rgb = new Uint8Array(rgbBuffer);
|
||||
rgbExtractor = [
|
||||
getExtractor('Red'),
|
||||
getExtractor('Green'),
|
||||
getExtractor('Blue')
|
||||
];
|
||||
|
||||
let r, g, b, pos;
|
||||
for (let i = 0; i < numPoints && !twoByteColor; ++i) {
|
||||
pos = i * pointSize;
|
||||
r = rgbExtractor[0](pos);
|
||||
g = rgbExtractor[1](pos);
|
||||
b = rgbExtractor[2](pos);
|
||||
if (r > 255 || g > 255 || b > 255) twoByteColor = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (dimensions['Intensity']) {
|
||||
intensityBuffer = new ArrayBuffer(numPoints * 4);
|
||||
intensity = new Float32Array(intensityBuffer);
|
||||
intensityExtractor = getExtractor('Intensity');
|
||||
}
|
||||
|
||||
if (dimensions['Classification']) {
|
||||
classificationBuffer = new ArrayBuffer(numPoints);
|
||||
classification = new Uint8Array(classificationBuffer);
|
||||
classificationExtractor = getExtractor('Classification');
|
||||
}
|
||||
|
||||
if (dimensions['ReturnNumber']) {
|
||||
returnNumberBuffer = new ArrayBuffer(numPoints);
|
||||
returnNumber = new Uint8Array(returnNumberBuffer);
|
||||
returnNumberExtractor = getExtractor('ReturnNumber');
|
||||
}
|
||||
|
||||
if (dimensions['NumberOfReturns']) {
|
||||
numberOfReturnsBuffer = new ArrayBuffer(numPoints);
|
||||
numberOfReturns = new Uint8Array(numberOfReturnsBuffer);
|
||||
numberOfReturnsExtractor = getExtractor('NumberOfReturns');
|
||||
}
|
||||
|
||||
if (dimensions['PointSourceId']) {
|
||||
pointSourceIdBuffer = new ArrayBuffer(numPoints * 2);
|
||||
pointSourceId = new Uint16Array(pointSourceIdBuffer);
|
||||
pointSourceIdExtractor = getExtractor('PointSourceId');
|
||||
}
|
||||
|
||||
let mean = [0, 0, 0];
|
||||
let bounds = {
|
||||
min: [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE],
|
||||
max: [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE],
|
||||
};
|
||||
|
||||
let x, y, z, r, g, b;
|
||||
for (let i = 0; i < numPoints; ++i) {
|
||||
let pos = i * pointSize;
|
||||
if (xyz) {
|
||||
x = xyzExtractor[0](pos) * scale.x + offset.x - mins[0];
|
||||
y = xyzExtractor[1](pos) * scale.y + offset.y - mins[1];
|
||||
z = xyzExtractor[2](pos) * scale.z + offset.z - mins[2];
|
||||
|
||||
mean[0] += x / numPoints;
|
||||
mean[1] += y / numPoints;
|
||||
mean[2] += z / numPoints;
|
||||
|
||||
bounds.min[0] = Math.min(bounds.min[0], x);
|
||||
bounds.min[1] = Math.min(bounds.min[1], y);
|
||||
bounds.min[2] = Math.min(bounds.min[2], z);
|
||||
|
||||
bounds.max[0] = Math.max(bounds.max[0], x);
|
||||
bounds.max[1] = Math.max(bounds.max[1], y);
|
||||
bounds.max[2] = Math.max(bounds.max[2], z);
|
||||
|
||||
xyz[3 * i + 0] = x;
|
||||
xyz[3 * i + 1] = y;
|
||||
xyz[3 * i + 2] = z;
|
||||
}
|
||||
|
||||
if (rgb) {
|
||||
r = rgbExtractor[0](pos);
|
||||
g = rgbExtractor[1](pos);
|
||||
b = rgbExtractor[2](pos);
|
||||
|
||||
if (twoByteColor) {
|
||||
r /= 256;
|
||||
g /= 256;
|
||||
b /= 256;
|
||||
}
|
||||
|
||||
rgb[4 * i + 0] = r;
|
||||
rgb[4 * i + 1] = g;
|
||||
rgb[4 * i + 2] = b;
|
||||
}
|
||||
|
||||
if (intensity) intensity[i] = intensityExtractor(pos);
|
||||
if (classification) classification[i] = classificationExtractor(pos);
|
||||
if (returnNumber) returnNumber[i] = returnNumberExtractor(pos);
|
||||
if (numberOfReturns) numberOfReturns[i] = numberOfReturnsExtractor(pos);
|
||||
if (pointSourceId) pointSourceId[i] = pointSourceIdExtractor(pos);
|
||||
}
|
||||
|
||||
let indicesBuffer = new ArrayBuffer(numPoints * 4);
|
||||
let indices = new Uint32Array(indicesBuffer);
|
||||
for (let i = 0; i < numPoints; ++i) {
|
||||
indices[i] = i;
|
||||
}
|
||||
|
||||
let message = {
|
||||
numPoints: numPoints,
|
||||
tightBoundingBox: bounds,
|
||||
mean: mean,
|
||||
|
||||
position: xyzBuffer,
|
||||
color: rgbBuffer,
|
||||
intensity: intensityBuffer,
|
||||
classification: classificationBuffer,
|
||||
returnNumber: returnNumberBuffer,
|
||||
numberOfReturns: numberOfReturnsBuffer,
|
||||
pointSourceId: pointSourceIdBuffer,
|
||||
indices: indicesBuffer
|
||||
};
|
||||
|
||||
let transferables = [
|
||||
message.position,
|
||||
message.color,
|
||||
message.intensity,
|
||||
message.classification,
|
||||
message.returnNumber,
|
||||
message.numberOfReturns,
|
||||
message.pointSourceId,
|
||||
message.indices
|
||||
].filter((v) => v);
|
||||
|
||||
postMessage(message, transferables);
|
||||
}
|
||||
|
||||
|
||||
// importScripts('/libs/ept/ParseBuffer.js');
|
||||
onmessage = function(event) {
|
||||
parseEpt(event);
|
||||
}
|
||||
|
||||
200
libs/potree/workers/EptLaszipDecoderWorker.js
Normal file
200
libs/potree/workers/EptLaszipDecoderWorker.js
Normal file
@@ -0,0 +1,200 @@
|
||||
function readUsingDataView(event) {
|
||||
performance.mark("laslaz-start");
|
||||
|
||||
let buffer = event.data.buffer;
|
||||
let numPoints = event.data.numPoints;
|
||||
let pointSize = event.data.pointSize;
|
||||
let pointFormat = event.data.pointFormatID;
|
||||
|
||||
// gps time byte offsets from LAS specification
|
||||
let gpsOffsets = [null, 20, null, 20, 20, 20, 22, 22, 22, 22, 22]
|
||||
let gpsOffset = gpsOffsets[pointFormat];
|
||||
|
||||
let scale = event.data.scale;
|
||||
let offset = event.data.offset;
|
||||
|
||||
let sourceUint8 = new Uint8Array(buffer);
|
||||
let sourceView = new DataView(buffer);
|
||||
|
||||
let tightBoundingBox = {
|
||||
min: [
|
||||
Number.POSITIVE_INFINITY,
|
||||
Number.POSITIVE_INFINITY,
|
||||
Number.POSITIVE_INFINITY
|
||||
],
|
||||
max: [
|
||||
Number.NEGATIVE_INFINITY,
|
||||
Number.NEGATIVE_INFINITY,
|
||||
Number.NEGATIVE_INFINITY
|
||||
]
|
||||
};
|
||||
|
||||
let mean = [0, 0, 0];
|
||||
|
||||
let pBuff = new ArrayBuffer(numPoints * 3 * 4);
|
||||
let cBuff = new ArrayBuffer(numPoints * 4);
|
||||
let iBuff = new ArrayBuffer(numPoints * 4);
|
||||
let clBuff = new ArrayBuffer(numPoints);
|
||||
let rnBuff = new ArrayBuffer(numPoints);
|
||||
let nrBuff = new ArrayBuffer(numPoints);
|
||||
let psBuff = new ArrayBuffer(numPoints * 2);
|
||||
let gpsBuff64 = new ArrayBuffer(numPoints * 8);
|
||||
let gpsBuff32 = new ArrayBuffer(numPoints * 4);
|
||||
|
||||
let positions = new Float32Array(pBuff);
|
||||
let colors = new Uint8Array(cBuff);
|
||||
let intensities = new Float32Array(iBuff);
|
||||
let classifications = new Uint8Array(clBuff);
|
||||
let returnNumbers = new Uint8Array(rnBuff);
|
||||
let numberOfReturns = new Uint8Array(nrBuff);
|
||||
let pointSourceIDs = new Uint16Array(psBuff);
|
||||
let gpsTime64 = new Float64Array(gpsBuff64)
|
||||
let gpsTime32 = new Float32Array(gpsBuff32)
|
||||
|
||||
// Point format 3 contains an 8-byte GpsTime before RGB values, so make
|
||||
// sure we have the correct color offset.
|
||||
let hasColor = pointFormat == 2 || pointFormat == 3;
|
||||
let co = pointFormat == 2 ? 20 : 28;
|
||||
|
||||
// TODO This should be cached per-resource since this is an expensive check.
|
||||
let twoByteColor = false;
|
||||
if (hasColor) {
|
||||
let r, g, b, pos;
|
||||
for (let i = 0; i < numPoints && !twoByteColor; ++i) {
|
||||
pos = i * pointSize;
|
||||
r = sourceView.getUint16(pos + co, true)
|
||||
g = sourceView.getUint16(pos + co + 2, true)
|
||||
b = sourceView.getUint16(pos + co + 4, true)
|
||||
if (r > 255 || g > 255 || b > 255) twoByteColor = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
// POSITION
|
||||
let ux = sourceView.getInt32(i * pointSize + 0, true);
|
||||
let uy = sourceView.getInt32(i * pointSize + 4, true);
|
||||
let uz = sourceView.getInt32(i * pointSize + 8, true);
|
||||
|
||||
x = ux * scale[0] + offset[0] - event.data.mins[0];
|
||||
y = uy * scale[1] + offset[1] - event.data.mins[1];
|
||||
z = uz * scale[2] + offset[2] - event.data.mins[2];
|
||||
|
||||
positions[3 * i + 0] = x;
|
||||
positions[3 * i + 1] = y;
|
||||
positions[3 * i + 2] = z;
|
||||
|
||||
mean[0] += x / numPoints;
|
||||
mean[1] += y / numPoints;
|
||||
mean[2] += z / numPoints;
|
||||
|
||||
tightBoundingBox.min[0] = Math.min(tightBoundingBox.min[0], x);
|
||||
tightBoundingBox.min[1] = Math.min(tightBoundingBox.min[1], y);
|
||||
tightBoundingBox.min[2] = Math.min(tightBoundingBox.min[2], z);
|
||||
|
||||
tightBoundingBox.max[0] = Math.max(tightBoundingBox.max[0], x);
|
||||
tightBoundingBox.max[1] = Math.max(tightBoundingBox.max[1], y);
|
||||
tightBoundingBox.max[2] = Math.max(tightBoundingBox.max[2], z);
|
||||
|
||||
// INTENSITY
|
||||
let intensity = sourceView.getUint16(i * pointSize + 12, true);
|
||||
intensities[i] = intensity;
|
||||
|
||||
// RETURN NUMBER, stored in the first 3 bits - 00000111
|
||||
// number of returns stored in next 3 bits - 00111000
|
||||
let returnNumberAndNumberOfReturns = sourceView.getUint8(i * pointSize + 14, true);
|
||||
let returnNumber = returnNumberAndNumberOfReturns & 0b0111;
|
||||
let numberOfReturn = (returnNumberAndNumberOfReturns & 0b00111000) >> 3;
|
||||
returnNumbers[i] = returnNumber;
|
||||
numberOfReturns[i] = numberOfReturn;
|
||||
|
||||
// CLASSIFICATION
|
||||
let classification = sourceView.getUint8(i * pointSize + 15, true);
|
||||
classifications[i] = classification;
|
||||
|
||||
// POINT SOURCE ID
|
||||
let pointSourceID = sourceView.getUint16(i * pointSize + 18, true);
|
||||
pointSourceIDs[i] = pointSourceID;
|
||||
|
||||
// COLOR, if available
|
||||
if (hasColor) {
|
||||
let r = sourceView.getUint16(i * pointSize + co, true)
|
||||
let g = sourceView.getUint16(i * pointSize + co + 2, true)
|
||||
let b = sourceView.getUint16(i * pointSize + co + 4, true)
|
||||
|
||||
if (twoByteColor) {
|
||||
r /= 256;
|
||||
g /= 256;
|
||||
b /= 256;
|
||||
}
|
||||
|
||||
colors[4 * i + 0] = r;
|
||||
colors[4 * i + 1] = g;
|
||||
colors[4 * i + 2] = b;
|
||||
colors[4 * i + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
let min = Infinity
|
||||
let max = -Infinity
|
||||
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
min = Math.min(min, gpsTime64[i])
|
||||
max = Math.max(max, gpsTime64[i])
|
||||
}
|
||||
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
gpsTime32[i] = gpsTime64[i] = min
|
||||
}
|
||||
|
||||
let indices = new ArrayBuffer(numPoints * 4);
|
||||
let iIndices = new Uint32Array(indices);
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
iIndices[i] = i;
|
||||
}
|
||||
|
||||
performance.mark("laslaz-end");
|
||||
|
||||
//{ // print timings
|
||||
// performance.measure("laslaz", "laslaz-start", "laslaz-end");
|
||||
// let measure = performance.getEntriesByType("measure")[0];
|
||||
// let dpp = 1000 * measure.duration / numPoints;
|
||||
// let debugMessage = `${measure.duration.toFixed(3)} ms, ${numPoints} points, ${dpp.toFixed(3)} µs / point`;
|
||||
// console.log(debugMessage);
|
||||
//}
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
let message = {
|
||||
mean: mean,
|
||||
position: pBuff,
|
||||
color: cBuff,
|
||||
intensity: iBuff,
|
||||
classification: clBuff,
|
||||
returnNumber: rnBuff,
|
||||
numberOfReturns: nrBuff,
|
||||
pointSourceID: psBuff,
|
||||
tightBoundingBox: tightBoundingBox,
|
||||
indices: indices,
|
||||
gpsTime: gpsBuff32,
|
||||
gpsMeta: { offset: min, range: max-min }
|
||||
};
|
||||
|
||||
let transferables = [
|
||||
message.position,
|
||||
message.color,
|
||||
message.intensity,
|
||||
message.classification,
|
||||
message.returnNumber,
|
||||
message.numberOfReturns,
|
||||
message.pointSourceID,
|
||||
message.indices,
|
||||
message.gpsTime
|
||||
];
|
||||
|
||||
postMessage(message, transferables);
|
||||
};
|
||||
|
||||
|
||||
|
||||
onmessage = readUsingDataView;
|
||||
|
||||
22376
libs/potree/workers/EptZstandardDecoderWorker.js
Normal file
22376
libs/potree/workers/EptZstandardDecoderWorker.js
Normal file
File diff suppressed because one or more lines are too long
344
libs/potree/workers/LASDecoderWorker.js
Normal file
344
libs/potree/workers/LASDecoderWorker.js
Normal file
@@ -0,0 +1,344 @@
|
||||
|
||||
function readUsingTempArrays(event) {
|
||||
|
||||
performance.mark("laslaz-start");
|
||||
|
||||
let buffer = event.data.buffer;
|
||||
let numPoints = event.data.numPoints;
|
||||
let sourcePointSize = event.data.pointSize;
|
||||
let pointFormatID = event.data.pointFormatID;
|
||||
let scale = event.data.scale;
|
||||
let offset = event.data.offset;
|
||||
|
||||
let temp = new ArrayBuffer(4);
|
||||
let tempUint8 = new Uint8Array(temp);
|
||||
let tempUint16 = new Uint16Array(temp);
|
||||
let tempInt32 = new Int32Array(temp);
|
||||
let sourceUint8 = new Uint8Array(buffer);
|
||||
|
||||
let tightBoundingBox = {
|
||||
min: [ Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY ],
|
||||
max: [ Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY ]
|
||||
};
|
||||
|
||||
let mean = [0, 0, 0];
|
||||
|
||||
let pBuff = new ArrayBuffer(numPoints * 3 * 4);
|
||||
let cBuff = new ArrayBuffer(numPoints * 4);
|
||||
let iBuff = new ArrayBuffer(numPoints * 4);
|
||||
let clBuff = new ArrayBuffer(numPoints);
|
||||
let rnBuff = new ArrayBuffer(numPoints);
|
||||
let nrBuff = new ArrayBuffer(numPoints);
|
||||
let psBuff = new ArrayBuffer(numPoints * 2);
|
||||
|
||||
let positions = new Float32Array(pBuff);
|
||||
let colors = new Uint8Array(cBuff);
|
||||
let intensities = new Float32Array(iBuff);
|
||||
let classifications = new Uint8Array(clBuff);
|
||||
let returnNumbers = new Uint8Array(rnBuff);
|
||||
let numberOfReturns = new Uint8Array(nrBuff);
|
||||
let pointSourceIDs = new Uint16Array(psBuff);
|
||||
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
// POSITION
|
||||
tempUint8[0] = sourceUint8[i * sourcePointSize + 0];
|
||||
tempUint8[1] = sourceUint8[i * sourcePointSize + 1];
|
||||
tempUint8[2] = sourceUint8[i * sourcePointSize + 2];
|
||||
tempUint8[3] = sourceUint8[i * sourcePointSize + 3];
|
||||
let x = tempInt32[0];
|
||||
|
||||
tempUint8[0] = sourceUint8[i * sourcePointSize + 4];
|
||||
tempUint8[1] = sourceUint8[i * sourcePointSize + 5];
|
||||
tempUint8[2] = sourceUint8[i * sourcePointSize + 6];
|
||||
tempUint8[3] = sourceUint8[i * sourcePointSize + 7];
|
||||
let y = tempInt32[0];
|
||||
|
||||
tempUint8[0] = sourceUint8[i * sourcePointSize + 8];
|
||||
tempUint8[1] = sourceUint8[i * sourcePointSize + 9];
|
||||
tempUint8[2] = sourceUint8[i * sourcePointSize + 10];
|
||||
tempUint8[3] = sourceUint8[i * sourcePointSize + 11];
|
||||
let z = tempInt32[0];
|
||||
|
||||
x = x * scale[0] + offset[0] - event.data.mins[0];
|
||||
y = y * scale[1] + offset[1] - event.data.mins[1];
|
||||
z = z * scale[2] + offset[2] - event.data.mins[2];
|
||||
|
||||
positions[3 * i + 0] = x;
|
||||
positions[3 * i + 1] = y;
|
||||
positions[3 * i + 2] = z;
|
||||
|
||||
mean[0] += x / numPoints;
|
||||
mean[1] += y / numPoints;
|
||||
mean[2] += z / numPoints;
|
||||
|
||||
tightBoundingBox.min[0] = Math.min(tightBoundingBox.min[0], x);
|
||||
tightBoundingBox.min[1] = Math.min(tightBoundingBox.min[1], y);
|
||||
tightBoundingBox.min[2] = Math.min(tightBoundingBox.min[2], z);
|
||||
|
||||
tightBoundingBox.max[0] = Math.max(tightBoundingBox.max[0], x);
|
||||
tightBoundingBox.max[1] = Math.max(tightBoundingBox.max[1], y);
|
||||
tightBoundingBox.max[2] = Math.max(tightBoundingBox.max[2], z);
|
||||
|
||||
// INTENSITY
|
||||
tempUint8[0] = sourceUint8[i * sourcePointSize + 12];
|
||||
tempUint8[1] = sourceUint8[i * sourcePointSize + 13];
|
||||
let intensity = tempUint16[0];
|
||||
intensities[i] = intensity;
|
||||
|
||||
// RETURN NUMBER, stored in the first 3 bits - 00000111
|
||||
let returnNumber = sourceUint8[i * sourcePointSize + 14] & 0b111;
|
||||
returnNumbers[i] = returnNumber;
|
||||
|
||||
// NUMBER OF RETURNS, stored in 00111000
|
||||
numberOfReturns[i] = (sourceUint8[i * pointSize + 14] & 0b111000) >> 3;
|
||||
|
||||
debugger;
|
||||
|
||||
// CLASSIFICATION
|
||||
let classification = sourceUint8[i * sourcePointSize + 15];
|
||||
classifications[i] = classification;
|
||||
|
||||
// POINT SOURCE ID
|
||||
tempUint8[0] = sourceUint8[i * sourcePointSize + 18];
|
||||
tempUint8[1] = sourceUint8[i * sourcePointSize + 19];
|
||||
let pointSourceID = tempUint16[0];
|
||||
pointSourceIDs[i] = pointSourceID;
|
||||
|
||||
// COLOR, if available
|
||||
if (pointFormatID === 2) {
|
||||
tempUint8[0] = sourceUint8[i * sourcePointSize + 20];
|
||||
tempUint8[1] = sourceUint8[i * sourcePointSize + 21];
|
||||
let r = tempUint16[0];
|
||||
|
||||
tempUint8[0] = sourceUint8[i * sourcePointSize + 22];
|
||||
tempUint8[1] = sourceUint8[i * sourcePointSize + 23];
|
||||
let g = tempUint16[0];
|
||||
|
||||
tempUint8[0] = sourceUint8[i * sourcePointSize + 24];
|
||||
tempUint8[1] = sourceUint8[i * sourcePointSize + 25];
|
||||
let b = tempUint16[0];
|
||||
|
||||
r = r / 256;
|
||||
g = g / 256;
|
||||
b = b / 256;
|
||||
colors[4 * i + 0] = r;
|
||||
colors[4 * i + 1] = g;
|
||||
colors[4 * i + 2] = b;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
let indices = new ArrayBuffer(numPoints * 4);
|
||||
let iIndices = new Uint32Array(indices);
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
iIndices[i] = i;
|
||||
}
|
||||
|
||||
performance.mark("laslaz-end");
|
||||
performance.measure("laslaz", "laslaz-start", "laslaz-end");
|
||||
|
||||
let measure = performance.getEntriesByType("measure")[0];
|
||||
let dpp = 1000 * measure.duration / numPoints;
|
||||
let debugMessage = `${measure.duration.toFixed(3)} ms, ${numPoints} points, ${dpp.toFixed(3)} micros / point`;
|
||||
console.log(debugMessage);
|
||||
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
let message = {
|
||||
mean: mean,
|
||||
position: pBuff,
|
||||
color: cBuff,
|
||||
intensity: iBuff,
|
||||
classification: clBuff,
|
||||
returnNumber: rnBuff,
|
||||
numberOfReturns: nrBuff,
|
||||
pointSourceID: psBuff,
|
||||
tightBoundingBox: tightBoundingBox,
|
||||
indices: indices
|
||||
};
|
||||
|
||||
let transferables = [
|
||||
message.position,
|
||||
message.color,
|
||||
message.intensity,
|
||||
message.classification,
|
||||
message.returnNumber,
|
||||
message.numberOfReturns,
|
||||
message.pointSourceID,
|
||||
message.indices];
|
||||
|
||||
debugger;
|
||||
|
||||
postMessage(message, transferables);
|
||||
};
|
||||
|
||||
|
||||
function readUsingDataView(event) {
|
||||
|
||||
performance.mark("laslaz-start");
|
||||
|
||||
let buffer = event.data.buffer;
|
||||
let numPoints = event.data.numPoints;
|
||||
let sourcePointSize = event.data.pointSize;
|
||||
let pointFormatID = event.data.pointFormatID;
|
||||
let scale = event.data.scale;
|
||||
let offset = event.data.offset;
|
||||
|
||||
let sourceView = new DataView(buffer);
|
||||
|
||||
let tightBoundingBox = {
|
||||
min: [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE],
|
||||
max: [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE]
|
||||
};
|
||||
|
||||
let mean = [0, 0, 0];
|
||||
|
||||
let pBuff = new ArrayBuffer(numPoints * 3 * 4);
|
||||
let cBuff = new ArrayBuffer(numPoints * 4);
|
||||
let iBuff = new ArrayBuffer(numPoints * 4);
|
||||
let clBuff = new ArrayBuffer(numPoints);
|
||||
let rnBuff = new ArrayBuffer(numPoints);
|
||||
let nrBuff = new ArrayBuffer(numPoints);
|
||||
let psBuff = new ArrayBuffer(numPoints * 2);
|
||||
|
||||
let positions = new Float32Array(pBuff);
|
||||
let colors = new Uint8Array(cBuff);
|
||||
let intensities = new Float32Array(iBuff);
|
||||
let classifications = new Uint8Array(clBuff);
|
||||
let returnNumbers = new Uint8Array(rnBuff);
|
||||
let numberOfReturns = new Uint8Array(nrBuff);
|
||||
let pointSourceIDs = new Uint16Array(psBuff);
|
||||
|
||||
const rangeIntensity = [Infinity, -Infinity];
|
||||
const rangeClassification = [Infinity, -Infinity];
|
||||
const rangeReturnNumber = [Infinity, -Infinity];
|
||||
const rangeNumberOfReturns = [Infinity, -Infinity];
|
||||
const rangeSourceID = [Infinity, -Infinity];
|
||||
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
// POSITION
|
||||
let ux = sourceView.getInt32(i * sourcePointSize + 0, true);
|
||||
let uy = sourceView.getInt32(i * sourcePointSize + 4, true);
|
||||
let uz = sourceView.getInt32(i * sourcePointSize + 8, true);
|
||||
|
||||
x = ux * scale[0] + offset[0] - event.data.mins[0];
|
||||
y = uy * scale[1] + offset[1] - event.data.mins[1];
|
||||
z = uz * scale[2] + offset[2] - event.data.mins[2];
|
||||
|
||||
positions[3 * i + 0] = x;
|
||||
positions[3 * i + 1] = y;
|
||||
positions[3 * i + 2] = z;
|
||||
|
||||
mean[0] += x / numPoints;
|
||||
mean[1] += y / numPoints;
|
||||
mean[2] += z / numPoints;
|
||||
|
||||
tightBoundingBox.min[0] = Math.min(tightBoundingBox.min[0], x);
|
||||
tightBoundingBox.min[1] = Math.min(tightBoundingBox.min[1], y);
|
||||
tightBoundingBox.min[2] = Math.min(tightBoundingBox.min[2], z);
|
||||
|
||||
tightBoundingBox.max[0] = Math.max(tightBoundingBox.max[0], x);
|
||||
tightBoundingBox.max[1] = Math.max(tightBoundingBox.max[1], y);
|
||||
tightBoundingBox.max[2] = Math.max(tightBoundingBox.max[2], z);
|
||||
|
||||
// INTENSITY
|
||||
let intensity = sourceView.getUint16(i * sourcePointSize + 12, true);
|
||||
intensities[i] = intensity;
|
||||
rangeIntensity[0] = Math.min(rangeIntensity[0], intensity);
|
||||
rangeIntensity[1] = Math.max(rangeIntensity[1], intensity);
|
||||
|
||||
// RETURN NUMBER, stored in the first 3 bits - 00000111
|
||||
// number of returns stored in next 3 bits - 00111000
|
||||
let returnNumberAndNumberOfReturns = sourceView.getUint8(i * sourcePointSize + 14, true);
|
||||
let returnNumber = returnNumberAndNumberOfReturns & 0b0111;
|
||||
let numberOfReturn = (returnNumberAndNumberOfReturns & 0b00111000) >> 3;
|
||||
returnNumbers[i] = returnNumber;
|
||||
numberOfReturns[i] = numberOfReturn;
|
||||
rangeReturnNumber[0] = Math.min(rangeReturnNumber[0], returnNumber);
|
||||
rangeReturnNumber[1] = Math.max(rangeReturnNumber[1], returnNumber);
|
||||
rangeNumberOfReturns[0] = Math.min(rangeNumberOfReturns[0], numberOfReturn);
|
||||
rangeNumberOfReturns[1] = Math.max(rangeNumberOfReturns[1], numberOfReturn);
|
||||
|
||||
// CLASSIFICATION
|
||||
let classification = sourceView.getUint8(i * sourcePointSize + 15, true);
|
||||
classifications[i] = classification;
|
||||
rangeClassification[0] = Math.min(rangeClassification[0], classification);
|
||||
rangeClassification[1] = Math.max(rangeClassification[1], classification);
|
||||
|
||||
// POINT SOURCE ID
|
||||
let pointSourceID = sourceView.getUint16(i * sourcePointSize + 18, true);
|
||||
pointSourceIDs[i] = pointSourceID;
|
||||
rangeSourceID[0] = Math.min(rangeSourceID[0], pointSourceID);
|
||||
rangeSourceID[1] = Math.max(rangeSourceID[1], pointSourceID);
|
||||
|
||||
// COLOR, if available
|
||||
if (pointFormatID === 2) {
|
||||
let r = sourceView.getUint16(i * sourcePointSize + 20, true) / 256;
|
||||
let g = sourceView.getUint16(i * sourcePointSize + 22, true) / 256;
|
||||
let b = sourceView.getUint16(i * sourcePointSize + 24, true) / 256;
|
||||
|
||||
colors[4 * i + 0] = r;
|
||||
colors[4 * i + 1] = g;
|
||||
colors[4 * i + 2] = b;
|
||||
colors[4 * i + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
let indices = new ArrayBuffer(numPoints * 4);
|
||||
let iIndices = new Uint32Array(indices);
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
iIndices[i] = i;
|
||||
}
|
||||
|
||||
performance.mark("laslaz-end");
|
||||
|
||||
//{ // print timings
|
||||
// performance.measure("laslaz", "laslaz-start", "laslaz-end");
|
||||
// let measure = performance.getEntriesByType("measure")[0];
|
||||
// let dpp = 1000 * measure.duration / numPoints;
|
||||
// let debugMessage = `${measure.duration.toFixed(3)} ms, ${numPoints} points, ${dpp.toFixed(3)} µs / point`;
|
||||
// console.log(debugMessage);
|
||||
//}
|
||||
performance.clearMarks();
|
||||
performance.clearMeasures();
|
||||
|
||||
const ranges = {
|
||||
"intensity": rangeIntensity,
|
||||
"classification": rangeClassification,
|
||||
"return number": rangeReturnNumber,
|
||||
"number of returns": rangeNumberOfReturns,
|
||||
"source id": rangeSourceID,
|
||||
};
|
||||
|
||||
let message = {
|
||||
mean: mean,
|
||||
position: pBuff,
|
||||
color: cBuff,
|
||||
intensity: iBuff,
|
||||
classification: clBuff,
|
||||
returnNumber: rnBuff,
|
||||
numberOfReturns: nrBuff,
|
||||
pointSourceID: psBuff,
|
||||
tightBoundingBox: tightBoundingBox,
|
||||
indices: indices,
|
||||
ranges: ranges,
|
||||
};
|
||||
|
||||
let transferables = [
|
||||
message.position,
|
||||
message.color,
|
||||
message.intensity,
|
||||
message.classification,
|
||||
message.returnNumber,
|
||||
message.numberOfReturns,
|
||||
message.pointSourceID,
|
||||
message.indices];
|
||||
|
||||
postMessage(message, transferables);
|
||||
};
|
||||
|
||||
|
||||
|
||||
onmessage = readUsingDataView;
|
||||
//onmessage = readUsingTempArrays;
|
||||
49756
libs/potree/workers/LASLAZWorker.js
Normal file
49756
libs/potree/workers/LASLAZWorker.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user