|
@@ -1,43 +1,36 @@
|
|
-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);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+class Version {
|
|
|
|
+ constructor(version) {
|
|
|
|
+ this.version = version;
|
|
|
|
+ var 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) {
|
|
|
|
+ var 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) {
|
|
|
|
+ var 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);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -45,400 +38,368 @@ class Version{
|
|
*
|
|
*
|
|
* @class
|
|
* @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}
|
|
|
|
|
|
+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
|
|
|
|
+ }
|
|
};
|
|
};
|
|
-
|
|
|
|
-let i = 0;
|
|
|
|
-for (let obj in PointAttributeTypes) {
|
|
|
|
- PointAttributeTypes[i] = PointAttributeTypes[obj];
|
|
|
|
- i++;
|
|
|
|
|
|
+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];
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+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.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.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,
|
|
|
|
|
|
+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);
|
|
|
|
+
|
|
|
|
+var typedArrayMapping = {
|
|
|
|
+ "int8": Int8Array,
|
|
|
|
+ "int16": Int16Array,
|
|
|
|
+ "int32": Int32Array,
|
|
|
|
+ "int64": Float64Array,
|
|
|
|
+ "uint8": Uint8Array,
|
|
|
|
+ "uint16": Uint16Array,
|
|
|
|
+ "uint32": Uint32Array,
|
|
|
|
+ "uint64": Float64Array,
|
|
|
|
+ "float": Float32Array,
|
|
|
|
+ "double": Float64Array
|
|
};
|
|
};
|
|
-
|
|
|
|
Potree = {};
|
|
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);
|
|
|
|
|
|
+onmessage = function onmessage(event) {
|
|
|
|
+ performance.mark("binary-decoder-start");
|
|
|
|
+ var buffer = event.data.buffer;
|
|
|
|
+ var pointAttributes = event.data.pointAttributes;
|
|
|
|
+ var numPoints = buffer.byteLength / pointAttributes.byteSize;
|
|
|
|
+ var view = new DataView(buffer);
|
|
|
|
+ var version = new Version(event.data.version);
|
|
|
|
+ var nodeOffset = event.data.offset;
|
|
|
|
+ var scale = event.data.scale;
|
|
|
|
+ var spacing = event.data.spacing;
|
|
|
|
+ var hasChildren = event.data.hasChildren;
|
|
|
|
+ var name = event.data.name;
|
|
|
|
+ var tightBoxMin = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY];
|
|
|
|
+ var tightBoxMax = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY];
|
|
|
|
+ var mean = [0, 0, 0];
|
|
|
|
+ var attributeBuffers = {};
|
|
|
|
+ var inOffset = 0;
|
|
|
|
+ for (var pointAttribute of pointAttributes.attributes) {
|
|
|
|
+ if (pointAttribute.name === "POSITION_CARTESIAN") {
|
|
|
|
+ var buff = new ArrayBuffer(numPoints * 4 * 3);
|
|
|
|
+ var positions = new Float32Array(buff);
|
|
|
|
+ for (var j = 0; j < numPoints; j++) {
|
|
|
|
+ var x = void 0,
|
|
|
|
+ y = void 0,
|
|
|
|
+ z = void 0;
|
|
|
|
+ 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") {
|
|
|
|
+ var _buff = new ArrayBuffer(numPoints * 4);
|
|
|
|
+ var colors = new Uint8Array(_buff);
|
|
|
|
+ for (var _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") {
|
|
|
|
+ var _buff2 = new ArrayBuffer(numPoints * 4 * 3);
|
|
|
|
+ var normals = new Float32Array(_buff2);
|
|
|
|
+ for (var _j2 = 0; _j2 < numPoints; _j2++) {
|
|
|
|
+ var bx = view.getUint8(inOffset + _j2 * pointAttributes.byteSize + 0);
|
|
|
|
+ var by = view.getUint8(inOffset + _j2 * pointAttributes.byteSize + 1);
|
|
|
|
+ var ex = bx / 255;
|
|
|
|
+ var ey = by / 255;
|
|
|
|
+ var nx = ex * 2 - 1;
|
|
|
|
+ var ny = ey * 2 - 1;
|
|
|
|
+ var nz = 1;
|
|
|
|
+ var nw = -1;
|
|
|
|
+ var 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 * _j2 + 0] = nx;
|
|
|
|
+ normals[3 * _j2 + 1] = ny;
|
|
|
|
+ normals[3 * _j2 + 2] = nz;
|
|
|
|
+ }
|
|
|
|
+ attributeBuffers[pointAttribute.name] = {
|
|
|
|
+ buffer: _buff2,
|
|
|
|
+ attribute: pointAttribute
|
|
|
|
+ };
|
|
|
|
+ } else if (pointAttribute.name === "NORMAL_OCT16") {
|
|
|
|
+ var _buff3 = new ArrayBuffer(numPoints * 4 * 3);
|
|
|
|
+ var _normals = new Float32Array(_buff3);
|
|
|
|
+ for (var _j3 = 0; _j3 < numPoints; _j3++) {
|
|
|
|
+ var _bx = view.getUint8(inOffset + _j3 * pointAttributes.byteSize + 0);
|
|
|
|
+ var _by = view.getUint8(inOffset + _j3 * pointAttributes.byteSize + 1);
|
|
|
|
+ var u = _bx / 255 * 2 - 1;
|
|
|
|
+ var v = _by / 255 * 2 - 1;
|
|
|
|
+ var _z = 1 - Math.abs(u) - Math.abs(v);
|
|
|
|
+ var _x = 0;
|
|
|
|
+ var _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);
|
|
|
|
+ }
|
|
|
|
+ var length = Math.sqrt(_x * _x + _y * _y + _z * _z);
|
|
|
|
+ _x = _x / length;
|
|
|
|
+ _y = _y / length;
|
|
|
|
+ _z = _z / length;
|
|
|
|
+ _normals[3 * _j3 + 0] = _x;
|
|
|
|
+ _normals[3 * _j3 + 1] = _y;
|
|
|
|
+ _normals[3 * _j3 + 2] = _z;
|
|
|
|
+ }
|
|
|
|
+ attributeBuffers[pointAttribute.name] = {
|
|
|
|
+ buffer: _buff3,
|
|
|
|
+ attribute: pointAttribute
|
|
|
|
+ };
|
|
|
|
+ } else if (pointAttribute.name === "NORMAL") {
|
|
|
|
+ var _buff4 = new ArrayBuffer(numPoints * 4 * 3);
|
|
|
|
+ var _normals2 = new Float32Array(_buff4);
|
|
|
|
+ for (var _j4 = 0; _j4 < numPoints; _j4++) {
|
|
|
|
+ var _x2 = view.getFloat32(inOffset + _j4 * pointAttributes.byteSize + 0, true);
|
|
|
|
+ var _y2 = view.getFloat32(inOffset + _j4 * pointAttributes.byteSize + 4, true);
|
|
|
|
+ var _z2 = view.getFloat32(inOffset + _j4 * pointAttributes.byteSize + 8, true);
|
|
|
|
+ _normals2[3 * _j4 + 0] = _x2;
|
|
|
|
+ _normals2[3 * _j4 + 1] = _y2;
|
|
|
|
+ _normals2[3 * _j4 + 2] = _z2;
|
|
|
|
+ }
|
|
|
|
+ attributeBuffers[pointAttribute.name] = {
|
|
|
|
+ buffer: _buff4,
|
|
|
|
+ attribute: pointAttribute
|
|
|
|
+ };
|
|
|
|
+ } else {
|
|
|
|
+ var _buff5 = new ArrayBuffer(numPoints * 4);
|
|
|
|
+ var f32 = new Float32Array(_buff5);
|
|
|
|
+ var TypedArray = typedArrayMapping[pointAttribute.type.name];
|
|
|
|
+ preciseBuffer = new TypedArray(numPoints);
|
|
|
|
+ var [min, max] = [Infinity, -Infinity];
|
|
|
|
+ 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) {
|
|
|
|
+ for (var _j5 = 0; _j5 < numPoints; _j5++) {
|
|
|
|
+ var value = getter(inOffset + _j5 * 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 (var _j6 = 0; _j6 < numPoints; _j6++) {
|
|
|
|
+ var _value = getter(inOffset + _j6 * pointAttributes.byteSize, true);
|
|
|
|
+ if (!Number.isNaN(_value)) {
|
|
|
|
+ min = Math.min(min, _value);
|
|
|
|
+ max = Math.max(max, _value);
|
|
|
|
+ }
|
|
|
|
+ f32[_j6] = (_value - offset) * _scale;
|
|
|
|
+ preciseBuffer[_j6] = _value;
|
|
|
|
+ }
|
|
|
|
+ pointAttribute.range = [min, max];
|
|
|
|
+ attributeBuffers[pointAttribute.name] = {
|
|
|
|
+ buffer: _buff5,
|
|
|
|
+ preciseBuffer: preciseBuffer,
|
|
|
|
+ attribute: pointAttribute,
|
|
|
|
+ offset: offset,
|
|
|
|
+ scale: _scale
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ inOffset += pointAttribute.byteSize;
|
|
|
|
+ }
|
|
|
|
+ {
|
|
|
|
+ // add indices
|
|
|
|
+ var _buff6 = new ArrayBuffer(numPoints * 4);
|
|
|
|
+ var indices = new Uint32Array(_buff6);
|
|
|
|
+ for (var i = 0; i < numPoints; i++) {
|
|
|
|
+ indices[i] = i;
|
|
|
|
+ }
|
|
|
|
+ attributeBuffers["INDICES"] = {
|
|
|
|
+ buffer: _buff6,
|
|
|
|
+ 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: _offset,
|
|
|
|
+ scale: _scale2
|
|
|
|
+ } = sourceBuffer;
|
|
|
|
+ var _view = new DataView(sourceBuffer.buffer);
|
|
|
|
+ var _getter = _view.getFloat32.bind(_view);
|
|
|
|
+ for (var _j7 = 0; _j7 < numPoints; _j7++) {
|
|
|
|
+ var _value2 = _getter(_j7 * 4, true);
|
|
|
|
+ _f[_j7 * numVectorElements + iElement] = _value2 / _scale2 + _offset;
|
|
|
|
+ }
|
|
|
|
+ iElement++;
|
|
|
|
+ }
|
|
|
|
+ var 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();
|
|
|
|
+ var message = {
|
|
|
|
+ buffer: buffer,
|
|
|
|
+ mean: mean,
|
|
|
|
+ attributeBuffers: attributeBuffers,
|
|
|
|
+ tightBoundingBox: {
|
|
|
|
+ min: tightBoxMin,
|
|
|
|
+ max: tightBoxMax
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ var transferables = [];
|
|
|
|
+ for (var property in message.attributeBuffers) {
|
|
|
|
+ transferables.push(message.attributeBuffers[property].buffer);
|
|
|
|
+ }
|
|
|
|
+ transferables.push(buffer);
|
|
|
|
+ postMessage(message, transferables);
|
|
};
|
|
};
|