123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- 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];
- }
- }//add
- const replacements = {
- "COLOR_PACKED": "rgba",
- "RGBA": "rgba",
- "INTENSITY": "intensity",
- "CLASSIFICATION": "classification",
- "GPS_TIME": "gps-time",
- };
- const replaceOldNames = (old) => {
- if(replacements[old]){
- return replacements[old];
- }else {
- return old;
- }
- };
- PointAttribute.POSITION_CARTESIAN = new PointAttribute(
- "POSITION_CARTESIAN", PointAttributeTypes.DATA_TYPE_FLOAT, 3);
- PointAttribute.RGBA_PACKED = new PointAttribute(
- replaceOldNames("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(
- replaceOldNames("INTENSITY"), PointAttributeTypes.DATA_TYPE_UINT16, 1);
- PointAttribute.CLASSIFICATION = new PointAttribute(
- replaceOldNames("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(
- replaceOldNames("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);
- };
|