BinaryDecoderWorker.js 13 KB

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