BinaryDecoderWorker.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. class Version{
  2. constructor(version){
  3. this.version = version;
  4. let vmLength = (version.indexOf('.') === -1) ? version.length : version.indexOf('.');
  5. this.versionMajor = parseInt(version.substr(0, vmLength));
  6. this.versionMinor = parseInt(version.substr(vmLength + 1));
  7. if (this.versionMinor.length === 0) {
  8. this.versionMinor = 0;
  9. }
  10. }
  11. newerThan(version){
  12. let v = new Version(version);
  13. if (this.versionMajor > v.versionMajor) {
  14. return true;
  15. } else if (this.versionMajor === v.versionMajor && this.versionMinor > v.versionMinor) {
  16. return true;
  17. } else {
  18. return false;
  19. }
  20. }
  21. equalOrHigher(version){
  22. let v = new Version(version);
  23. if (this.versionMajor > v.versionMajor) {
  24. return true;
  25. } else if (this.versionMajor === v.versionMajor && this.versionMinor >= v.versionMinor) {
  26. return true;
  27. } else {
  28. return false;
  29. }
  30. }
  31. upTo(version){
  32. return !this.newerThan(version);
  33. }
  34. }
  35. /**
  36. * Some types of possible point attribute data formats
  37. *
  38. * @class
  39. */
  40. const PointAttributeTypes = {
  41. DATA_TYPE_DOUBLE: {ordinal: 0, name: "double", size: 8},
  42. DATA_TYPE_FLOAT: {ordinal: 1, name: "float", size: 4},
  43. DATA_TYPE_INT8: {ordinal: 2, name: "int8", size: 1},
  44. DATA_TYPE_UINT8: {ordinal: 3, name: "uint8", size: 1},
  45. DATA_TYPE_INT16: {ordinal: 4, name: "int16", size: 2},
  46. DATA_TYPE_UINT16: {ordinal: 5, name: "uint16", size: 2},
  47. DATA_TYPE_INT32: {ordinal: 6, name: "int32", size: 4},
  48. DATA_TYPE_UINT32: {ordinal: 7, name: "uint32", size: 4},
  49. DATA_TYPE_INT64: {ordinal: 8, name: "int64", size: 8},
  50. DATA_TYPE_UINT64: {ordinal: 9, name: "uint64", size: 8}
  51. };
  52. let i = 0;
  53. for (let obj in PointAttributeTypes) {
  54. PointAttributeTypes[i] = PointAttributeTypes[obj];
  55. i++;
  56. }
  57. class PointAttribute{
  58. constructor(name, type, numElements){
  59. this.name = name;
  60. this.type = type;
  61. this.numElements = numElements;
  62. this.byteSize = this.numElements * this.type.size;
  63. this.description = "";
  64. this.range = [Infinity, -Infinity];
  65. }
  66. }//add
  67. const replacements = {
  68. "COLOR_PACKED": "rgba",
  69. "RGBA": "rgba",
  70. "INTENSITY": "intensity",
  71. "CLASSIFICATION": "classification",
  72. "GPS_TIME": "gps-time",
  73. };
  74. const replaceOldNames = (old) => {
  75. if(replacements[old]){
  76. return replacements[old];
  77. }else {
  78. return old;
  79. }
  80. };
  81. PointAttribute.POSITION_CARTESIAN = new PointAttribute(
  82. "POSITION_CARTESIAN", PointAttributeTypes.DATA_TYPE_FLOAT, 3);
  83. PointAttribute.RGBA_PACKED = new PointAttribute(
  84. replaceOldNames("COLOR_PACKED"), PointAttributeTypes.DATA_TYPE_INT8, 4);
  85. PointAttribute.COLOR_PACKED = PointAttribute.RGBA_PACKED;
  86. PointAttribute.RGB_PACKED = new PointAttribute(
  87. "COLOR_PACKED", PointAttributeTypes.DATA_TYPE_INT8, 3);
  88. PointAttribute.NORMAL_FLOATS = new PointAttribute(
  89. "NORMAL_FLOATS", PointAttributeTypes.DATA_TYPE_FLOAT, 3);
  90. PointAttribute.INTENSITY = new PointAttribute(
  91. replaceOldNames("INTENSITY"), PointAttributeTypes.DATA_TYPE_UINT16, 1);
  92. PointAttribute.CLASSIFICATION = new PointAttribute(
  93. replaceOldNames("CLASSIFICATION"), PointAttributeTypes.DATA_TYPE_UINT8, 1);
  94. PointAttribute.NORMAL_SPHEREMAPPED = new PointAttribute(
  95. "NORMAL_SPHEREMAPPED", PointAttributeTypes.DATA_TYPE_UINT8, 2);
  96. PointAttribute.NORMAL_OCT16 = new PointAttribute(
  97. "NORMAL_OCT16", PointAttributeTypes.DATA_TYPE_UINT8, 2);
  98. PointAttribute.NORMAL = new PointAttribute(
  99. "NORMAL", PointAttributeTypes.DATA_TYPE_FLOAT, 3);
  100. PointAttribute.RETURN_NUMBER = new PointAttribute(
  101. "RETURN_NUMBER", PointAttributeTypes.DATA_TYPE_UINT8, 1);
  102. PointAttribute.NUMBER_OF_RETURNS = new PointAttribute(
  103. "NUMBER_OF_RETURNS", PointAttributeTypes.DATA_TYPE_UINT8, 1);
  104. PointAttribute.SOURCE_ID = new PointAttribute(
  105. "SOURCE_ID", PointAttributeTypes.DATA_TYPE_UINT16, 1);
  106. PointAttribute.INDICES = new PointAttribute(
  107. "INDICES", PointAttributeTypes.DATA_TYPE_UINT32, 1);
  108. PointAttribute.SPACING = new PointAttribute(
  109. "SPACING", PointAttributeTypes.DATA_TYPE_FLOAT, 1);
  110. PointAttribute.GPS_TIME = new PointAttribute(
  111. replaceOldNames("GPS_TIME"), PointAttributeTypes.DATA_TYPE_DOUBLE, 1);
  112. const typedArrayMapping = {
  113. "int8": Int8Array,
  114. "int16": Int16Array,
  115. "int32": Int32Array,
  116. "int64": Float64Array,
  117. "uint8": Uint8Array,
  118. "uint16": Uint16Array,
  119. "uint32": Uint32Array,
  120. "uint64": Float64Array,
  121. "float": Float32Array,
  122. "double": Float64Array,
  123. };
  124. Potree = {};
  125. onmessage = function (event) {
  126. performance.mark("binary-decoder-start");
  127. let buffer = event.data.buffer;
  128. let pointAttributes = event.data.pointAttributes;
  129. let numPoints = buffer.byteLength / pointAttributes.byteSize;
  130. let view = new DataView(buffer);
  131. let version = new Version(event.data.version);
  132. let nodeOffset = event.data.offset;
  133. let scale = event.data.scale;
  134. let spacing = event.data.spacing;
  135. let hasChildren = event.data.hasChildren;
  136. let name = event.data.name;
  137. let tightBoxMin = [ Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY ];
  138. let tightBoxMax = [ Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY ];
  139. let mean = [0, 0, 0];
  140. let attributeBuffers = {};
  141. let inOffset = 0;
  142. for (let pointAttribute of pointAttributes.attributes) {
  143. if (pointAttribute.name === "POSITION_CARTESIAN") {
  144. let buff = new ArrayBuffer(numPoints * 4 * 3);
  145. let positions = new Float32Array(buff);
  146. for (let j = 0; j < numPoints; j++) {
  147. let x, y, z;
  148. if (version.newerThan('1.3')) {
  149. x = (view.getUint32(inOffset + j * pointAttributes.byteSize + 0, true) * scale);
  150. y = (view.getUint32(inOffset + j * pointAttributes.byteSize + 4, true) * scale);
  151. z = (view.getUint32(inOffset + j * pointAttributes.byteSize + 8, true) * scale);
  152. } else {
  153. x = view.getFloat32(j * pointAttributes.byteSize + 0, true) + nodeOffset[0];
  154. y = view.getFloat32(j * pointAttributes.byteSize + 4, true) + nodeOffset[1];
  155. z = view.getFloat32(j * pointAttributes.byteSize + 8, true) + nodeOffset[2];
  156. }
  157. positions[3 * j + 0] = x;
  158. positions[3 * j + 1] = y;
  159. positions[3 * j + 2] = z;
  160. mean[0] += x / numPoints;
  161. mean[1] += y / numPoints;
  162. mean[2] += z / numPoints;
  163. tightBoxMin[0] = Math.min(tightBoxMin[0], x);
  164. tightBoxMin[1] = Math.min(tightBoxMin[1], y);
  165. tightBoxMin[2] = Math.min(tightBoxMin[2], z);
  166. tightBoxMax[0] = Math.max(tightBoxMax[0], x);
  167. tightBoxMax[1] = Math.max(tightBoxMax[1], y);
  168. tightBoxMax[2] = Math.max(tightBoxMax[2], z);
  169. }
  170. attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute };
  171. } else if (pointAttribute.name === "rgba") {
  172. let buff = new ArrayBuffer(numPoints * 4);
  173. let colors = new Uint8Array(buff);
  174. for (let j = 0; j < numPoints; j++) {
  175. colors[4 * j + 0] = view.getUint8(inOffset + j * pointAttributes.byteSize + 0);
  176. colors[4 * j + 1] = view.getUint8(inOffset + j * pointAttributes.byteSize + 1);
  177. colors[4 * j + 2] = view.getUint8(inOffset + j * pointAttributes.byteSize + 2);
  178. }
  179. attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute };
  180. } else if (pointAttribute.name === "NORMAL_SPHEREMAPPED") {
  181. let buff = new ArrayBuffer(numPoints * 4 * 3);
  182. let normals = new Float32Array(buff);
  183. for (let j = 0; j < numPoints; j++) {
  184. let bx = view.getUint8(inOffset + j * pointAttributes.byteSize + 0);
  185. let by = view.getUint8(inOffset + j * pointAttributes.byteSize + 1);
  186. let ex = bx / 255;
  187. let ey = by / 255;
  188. let nx = ex * 2 - 1;
  189. let ny = ey * 2 - 1;
  190. let nz = 1;
  191. let nw = -1;
  192. let l = (nx * (-nx)) + (ny * (-ny)) + (nz * (-nw));
  193. nz = l;
  194. nx = nx * Math.sqrt(l);
  195. ny = ny * Math.sqrt(l);
  196. nx = nx * 2;
  197. ny = ny * 2;
  198. nz = nz * 2 - 1;
  199. normals[3 * j + 0] = nx;
  200. normals[3 * j + 1] = ny;
  201. normals[3 * j + 2] = nz;
  202. }
  203. attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute };
  204. } else if (pointAttribute.name === "NORMAL_OCT16") {
  205. let buff = new ArrayBuffer(numPoints * 4 * 3);
  206. let normals = new Float32Array(buff);
  207. for (let j = 0; j < numPoints; j++) {
  208. let bx = view.getUint8(inOffset + j * pointAttributes.byteSize + 0);
  209. let by = view.getUint8(inOffset + j * pointAttributes.byteSize + 1);
  210. let u = (bx / 255) * 2 - 1;
  211. let v = (by / 255) * 2 - 1;
  212. let z = 1 - Math.abs(u) - Math.abs(v);
  213. let x = 0;
  214. let y = 0;
  215. if (z >= 0) {
  216. x = u;
  217. y = v;
  218. } else {
  219. x = -(v / Math.sign(v) - 1) / Math.sign(u);
  220. y = -(u / Math.sign(u) - 1) / Math.sign(v);
  221. }
  222. let length = Math.sqrt(x * x + y * y + z * z);
  223. x = x / length;
  224. y = y / length;
  225. z = z / length;
  226. normals[3 * j + 0] = x;
  227. normals[3 * j + 1] = y;
  228. normals[3 * j + 2] = z;
  229. }
  230. attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute };
  231. } else if (pointAttribute.name === "NORMAL") {
  232. let buff = new ArrayBuffer(numPoints * 4 * 3);
  233. let normals = new Float32Array(buff);
  234. for (let j = 0; j < numPoints; j++) {
  235. let x = view.getFloat32(inOffset + j * pointAttributes.byteSize + 0, true);
  236. let y = view.getFloat32(inOffset + j * pointAttributes.byteSize + 4, true);
  237. let z = view.getFloat32(inOffset + j * pointAttributes.byteSize + 8, true);
  238. normals[3 * j + 0] = x;
  239. normals[3 * j + 1] = y;
  240. normals[3 * j + 2] = z;
  241. }
  242. attributeBuffers[pointAttribute.name] = { buffer: buff, attribute: pointAttribute };
  243. } else {
  244. let buff = new ArrayBuffer(numPoints * 4);
  245. let f32 = new Float32Array(buff);
  246. let TypedArray = typedArrayMapping[pointAttribute.type.name];
  247. preciseBuffer = new TypedArray(numPoints);
  248. let [min, max] = [Infinity, -Infinity];
  249. let [offset, scale] = [0, 1];
  250. const getterMap = {
  251. "int8": view.getInt8,
  252. "int16": view.getInt16,
  253. "int32": view.getInt32,
  254. "int64": view.getInt64,
  255. "uint8": view.getUint8,
  256. "uint16": view.getUint16,
  257. "uint32": view.getUint32,
  258. "uint64": view.getUint64,
  259. "float": view.getFloat32,
  260. "double": view.getFloat64,
  261. };
  262. const getter = getterMap[pointAttribute.type.name].bind(view);
  263. // compute offset and scale to pack larger types into 32 bit floats
  264. if(pointAttribute.type.size > 4){
  265. for(let j = 0; j < numPoints; j++){
  266. let value = getter(inOffset + j * pointAttributes.byteSize, true);
  267. if(!Number.isNaN(value)){
  268. min = Math.min(min, value);
  269. max = Math.max(max, value);
  270. }
  271. }
  272. if(pointAttribute.initialRange != null){
  273. offset = pointAttribute.initialRange[0];
  274. scale = 1 / (pointAttribute.initialRange[1] - pointAttribute.initialRange[0]);
  275. }else {
  276. offset = min;
  277. scale = 1 / (max - min);
  278. }
  279. }
  280. for(let j = 0; j < numPoints; j++){
  281. let value = getter(inOffset + j * pointAttributes.byteSize, true);
  282. if(!Number.isNaN(value)){
  283. min = Math.min(min, value);
  284. max = Math.max(max, value);
  285. }
  286. f32[j] = (value - offset) * scale;
  287. preciseBuffer[j] = value;
  288. }
  289. pointAttribute.range = [min, max];
  290. attributeBuffers[pointAttribute.name] = {
  291. buffer: buff,
  292. preciseBuffer: preciseBuffer,
  293. attribute: pointAttribute,
  294. offset: offset,
  295. scale: scale,
  296. };
  297. }
  298. inOffset += pointAttribute.byteSize;
  299. }
  300. { // add indices
  301. let buff = new ArrayBuffer(numPoints * 4);
  302. let indices = new Uint32Array(buff);
  303. for (let i = 0; i < numPoints; i++) {
  304. indices[i] = i;
  305. }
  306. attributeBuffers["INDICES"] = { buffer: buff, attribute: PointAttribute.INDICES };
  307. }
  308. { // handle attribute vectors
  309. let vectors = pointAttributes.vectors;
  310. for(let vector of vectors){
  311. let {name, attributes} = vector;
  312. let numVectorElements = attributes.length;
  313. let buffer = new ArrayBuffer(numVectorElements * numPoints * 4);
  314. let f32 = new Float32Array(buffer);
  315. let iElement = 0;
  316. for(let sourceName of attributes){
  317. let sourceBuffer = attributeBuffers[sourceName];
  318. let {offset, scale} = sourceBuffer;
  319. let view = new DataView(sourceBuffer.buffer);
  320. const getter = view.getFloat32.bind(view);
  321. for(let j = 0; j < numPoints; j++){
  322. let value = getter(j * 4, true);
  323. f32[j * numVectorElements + iElement] = (value / scale) + offset;
  324. }
  325. iElement++;
  326. }
  327. let vecAttribute = new PointAttribute(name, PointAttributeTypes.DATA_TYPE_FLOAT, 3);
  328. attributeBuffers[name] = {
  329. buffer: buffer,
  330. attribute: vecAttribute,
  331. };
  332. }
  333. }
  334. performance.mark("binary-decoder-end");
  335. // { // print timings
  336. // //performance.measure("spacing", "spacing-start", "spacing-end");
  337. // performance.measure("binary-decoder", "binary-decoder-start", "binary-decoder-end");
  338. // let measure = performance.getEntriesByType("measure")[0];
  339. // let dpp = 1000 * measure.duration / numPoints;
  340. // let pps = parseInt(numPoints / (measure.duration / 1000));
  341. // let debugMessage = `${measure.duration.toFixed(3)} ms, ${numPoints} points, ${pps.toLocaleString()} points/sec`;
  342. // console.log(debugMessage);
  343. // }
  344. performance.clearMarks();
  345. performance.clearMeasures();
  346. let message = {
  347. buffer: buffer,
  348. mean: mean,
  349. attributeBuffers: attributeBuffers,
  350. tightBoundingBox: { min: tightBoxMin, max: tightBoxMax },
  351. };
  352. let transferables = [];
  353. for (let property in message.attributeBuffers) {
  354. transferables.push(message.attributeBuffers[property].buffer);
  355. }
  356. transferables.push(buffer);
  357. postMessage(message, transferables);
  358. };