decodeDraco.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /* global require */
  2. import ComponentDatatype from "../Core/ComponentDatatype.js";
  3. import defined from "../Core/defined.js";
  4. import IndexDatatype from "../Core/IndexDatatype.js";
  5. import RuntimeError from "../Core/RuntimeError.js";
  6. import createTaskProcessorWorker from "./createTaskProcessorWorker.js";
  7. let draco;
  8. function decodeIndexArray(dracoGeometry, dracoDecoder) {
  9. const numPoints = dracoGeometry.num_points();
  10. const numFaces = dracoGeometry.num_faces();
  11. const faceIndices = new draco.DracoInt32Array();
  12. const numIndices = numFaces * 3;
  13. const indexArray = IndexDatatype.createTypedArray(numPoints, numIndices);
  14. let offset = 0;
  15. for (let i = 0; i < numFaces; ++i) {
  16. dracoDecoder.GetFaceFromMesh(dracoGeometry, i, faceIndices);
  17. indexArray[offset + 0] = faceIndices.GetValue(0);
  18. indexArray[offset + 1] = faceIndices.GetValue(1);
  19. indexArray[offset + 2] = faceIndices.GetValue(2);
  20. offset += 3;
  21. }
  22. draco.destroy(faceIndices);
  23. return {
  24. typedArray: indexArray,
  25. numberOfIndices: numIndices,
  26. };
  27. }
  28. function decodeQuantizedDracoTypedArray(
  29. dracoGeometry,
  30. dracoDecoder,
  31. dracoAttribute,
  32. quantization,
  33. vertexArrayLength
  34. ) {
  35. let vertexArray;
  36. let attributeData;
  37. if (quantization.quantizationBits <= 8) {
  38. attributeData = new draco.DracoUInt8Array();
  39. vertexArray = new Uint8Array(vertexArrayLength);
  40. dracoDecoder.GetAttributeUInt8ForAllPoints(
  41. dracoGeometry,
  42. dracoAttribute,
  43. attributeData
  44. );
  45. } else if (quantization.quantizationBits <= 16) {
  46. attributeData = new draco.DracoUInt16Array();
  47. vertexArray = new Uint16Array(vertexArrayLength);
  48. dracoDecoder.GetAttributeUInt16ForAllPoints(
  49. dracoGeometry,
  50. dracoAttribute,
  51. attributeData
  52. );
  53. } else {
  54. attributeData = new draco.DracoFloat32Array();
  55. vertexArray = new Float32Array(vertexArrayLength);
  56. dracoDecoder.GetAttributeFloatForAllPoints(
  57. dracoGeometry,
  58. dracoAttribute,
  59. attributeData
  60. );
  61. }
  62. for (let i = 0; i < vertexArrayLength; ++i) {
  63. vertexArray[i] = attributeData.GetValue(i);
  64. }
  65. draco.destroy(attributeData);
  66. return vertexArray;
  67. }
  68. function decodeDracoTypedArray(
  69. dracoGeometry,
  70. dracoDecoder,
  71. dracoAttribute,
  72. vertexArrayLength
  73. ) {
  74. let vertexArray;
  75. let attributeData;
  76. // Some attribute types are casted down to 32 bit since Draco only returns 32 bit values
  77. switch (dracoAttribute.data_type()) {
  78. case 1:
  79. case 11: // DT_INT8 or DT_BOOL
  80. attributeData = new draco.DracoInt8Array();
  81. vertexArray = new Int8Array(vertexArrayLength);
  82. dracoDecoder.GetAttributeInt8ForAllPoints(
  83. dracoGeometry,
  84. dracoAttribute,
  85. attributeData
  86. );
  87. break;
  88. case 2: // DT_UINT8
  89. attributeData = new draco.DracoUInt8Array();
  90. vertexArray = new Uint8Array(vertexArrayLength);
  91. dracoDecoder.GetAttributeUInt8ForAllPoints(
  92. dracoGeometry,
  93. dracoAttribute,
  94. attributeData
  95. );
  96. break;
  97. case 3: // DT_INT16
  98. attributeData = new draco.DracoInt16Array();
  99. vertexArray = new Int16Array(vertexArrayLength);
  100. dracoDecoder.GetAttributeInt16ForAllPoints(
  101. dracoGeometry,
  102. dracoAttribute,
  103. attributeData
  104. );
  105. break;
  106. case 4: // DT_UINT16
  107. attributeData = new draco.DracoUInt16Array();
  108. vertexArray = new Uint16Array(vertexArrayLength);
  109. dracoDecoder.GetAttributeUInt16ForAllPoints(
  110. dracoGeometry,
  111. dracoAttribute,
  112. attributeData
  113. );
  114. break;
  115. case 5:
  116. case 7: // DT_INT32 or DT_INT64
  117. attributeData = new draco.DracoInt32Array();
  118. vertexArray = new Int32Array(vertexArrayLength);
  119. dracoDecoder.GetAttributeInt32ForAllPoints(
  120. dracoGeometry,
  121. dracoAttribute,
  122. attributeData
  123. );
  124. break;
  125. case 6:
  126. case 8: // DT_UINT32 or DT_UINT64
  127. attributeData = new draco.DracoUInt32Array();
  128. vertexArray = new Uint32Array(vertexArrayLength);
  129. dracoDecoder.GetAttributeUInt32ForAllPoints(
  130. dracoGeometry,
  131. dracoAttribute,
  132. attributeData
  133. );
  134. break;
  135. case 9:
  136. case 10: // DT_FLOAT32 or DT_FLOAT64
  137. attributeData = new draco.DracoFloat32Array();
  138. vertexArray = new Float32Array(vertexArrayLength);
  139. dracoDecoder.GetAttributeFloatForAllPoints(
  140. dracoGeometry,
  141. dracoAttribute,
  142. attributeData
  143. );
  144. break;
  145. }
  146. for (let i = 0; i < vertexArrayLength; ++i) {
  147. vertexArray[i] = attributeData.GetValue(i);
  148. }
  149. draco.destroy(attributeData);
  150. return vertexArray;
  151. }
  152. function decodeAttribute(dracoGeometry, dracoDecoder, dracoAttribute) {
  153. const numPoints = dracoGeometry.num_points();
  154. const numComponents = dracoAttribute.num_components();
  155. let quantization;
  156. let transform = new draco.AttributeQuantizationTransform();
  157. if (transform.InitFromAttribute(dracoAttribute)) {
  158. const minValues = new Array(numComponents);
  159. for (let i = 0; i < numComponents; ++i) {
  160. minValues[i] = transform.min_value(i);
  161. }
  162. quantization = {
  163. quantizationBits: transform.quantization_bits(),
  164. minValues: minValues,
  165. range: transform.range(),
  166. octEncoded: false,
  167. };
  168. }
  169. draco.destroy(transform);
  170. transform = new draco.AttributeOctahedronTransform();
  171. if (transform.InitFromAttribute(dracoAttribute)) {
  172. quantization = {
  173. quantizationBits: transform.quantization_bits(),
  174. octEncoded: true,
  175. };
  176. }
  177. draco.destroy(transform);
  178. const vertexArrayLength = numPoints * numComponents;
  179. let vertexArray;
  180. if (defined(quantization)) {
  181. vertexArray = decodeQuantizedDracoTypedArray(
  182. dracoGeometry,
  183. dracoDecoder,
  184. dracoAttribute,
  185. quantization,
  186. vertexArrayLength
  187. );
  188. } else {
  189. vertexArray = decodeDracoTypedArray(
  190. dracoGeometry,
  191. dracoDecoder,
  192. dracoAttribute,
  193. vertexArrayLength
  194. );
  195. }
  196. const componentDatatype = ComponentDatatype.fromTypedArray(vertexArray);
  197. return {
  198. array: vertexArray,
  199. data: {
  200. componentsPerAttribute: numComponents,
  201. componentDatatype: componentDatatype,
  202. byteOffset: dracoAttribute.byte_offset(),
  203. byteStride:
  204. ComponentDatatype.getSizeInBytes(componentDatatype) * numComponents,
  205. normalized: dracoAttribute.normalized(),
  206. quantization: quantization,
  207. },
  208. };
  209. }
  210. function decodePointCloud(parameters) {
  211. const dracoDecoder = new draco.Decoder();
  212. if (parameters.dequantizeInShader) {
  213. dracoDecoder.SkipAttributeTransform(draco.POSITION);
  214. dracoDecoder.SkipAttributeTransform(draco.NORMAL);
  215. }
  216. const buffer = new draco.DecoderBuffer();
  217. buffer.Init(parameters.buffer, parameters.buffer.length);
  218. const geometryType = dracoDecoder.GetEncodedGeometryType(buffer);
  219. if (geometryType !== draco.POINT_CLOUD) {
  220. throw new RuntimeError("Draco geometry type must be POINT_CLOUD.");
  221. }
  222. const dracoPointCloud = new draco.PointCloud();
  223. const decodingStatus = dracoDecoder.DecodeBufferToPointCloud(
  224. buffer,
  225. dracoPointCloud
  226. );
  227. if (!decodingStatus.ok() || dracoPointCloud.ptr === 0) {
  228. throw new RuntimeError(
  229. `Error decoding draco point cloud: ${decodingStatus.error_msg()}`
  230. );
  231. }
  232. draco.destroy(buffer);
  233. const result = {};
  234. const properties = parameters.properties;
  235. for (const propertyName in properties) {
  236. if (properties.hasOwnProperty(propertyName)) {
  237. let dracoAttribute;
  238. if (propertyName === "POSITION" || propertyName === "NORMAL") {
  239. const dracoAttributeId = dracoDecoder.GetAttributeId(
  240. dracoPointCloud,
  241. draco[propertyName]
  242. );
  243. dracoAttribute = dracoDecoder.GetAttribute(
  244. dracoPointCloud,
  245. dracoAttributeId
  246. );
  247. } else {
  248. const attributeId = properties[propertyName];
  249. dracoAttribute = dracoDecoder.GetAttributeByUniqueId(
  250. dracoPointCloud,
  251. attributeId
  252. );
  253. }
  254. result[propertyName] = decodeAttribute(
  255. dracoPointCloud,
  256. dracoDecoder,
  257. dracoAttribute
  258. );
  259. }
  260. }
  261. draco.destroy(dracoPointCloud);
  262. draco.destroy(dracoDecoder);
  263. return result;
  264. }
  265. function decodePrimitive(parameters) {
  266. const dracoDecoder = new draco.Decoder();
  267. // Skip all parameter types except generic
  268. const attributesToSkip = ["POSITION", "NORMAL", "COLOR", "TEX_COORD"];
  269. if (parameters.dequantizeInShader) {
  270. for (let i = 0; i < attributesToSkip.length; ++i) {
  271. dracoDecoder.SkipAttributeTransform(draco[attributesToSkip[i]]);
  272. }
  273. }
  274. const bufferView = parameters.bufferView;
  275. const buffer = new draco.DecoderBuffer();
  276. buffer.Init(parameters.array, bufferView.byteLength);
  277. const geometryType = dracoDecoder.GetEncodedGeometryType(buffer);
  278. if (geometryType !== draco.TRIANGULAR_MESH) {
  279. throw new RuntimeError("Unsupported draco mesh geometry type.");
  280. }
  281. const dracoGeometry = new draco.Mesh();
  282. const decodingStatus = dracoDecoder.DecodeBufferToMesh(buffer, dracoGeometry);
  283. if (!decodingStatus.ok() || dracoGeometry.ptr === 0) {
  284. throw new RuntimeError(
  285. `Error decoding draco mesh geometry: ${decodingStatus.error_msg()}`
  286. );
  287. }
  288. draco.destroy(buffer);
  289. const attributeData = {};
  290. const compressedAttributes = parameters.compressedAttributes;
  291. for (const attributeName in compressedAttributes) {
  292. if (compressedAttributes.hasOwnProperty(attributeName)) {
  293. const compressedAttribute = compressedAttributes[attributeName];
  294. const dracoAttribute = dracoDecoder.GetAttributeByUniqueId(
  295. dracoGeometry,
  296. compressedAttribute
  297. );
  298. attributeData[attributeName] = decodeAttribute(
  299. dracoGeometry,
  300. dracoDecoder,
  301. dracoAttribute
  302. );
  303. }
  304. }
  305. const result = {
  306. indexArray: decodeIndexArray(dracoGeometry, dracoDecoder),
  307. attributeData: attributeData,
  308. };
  309. draco.destroy(dracoGeometry);
  310. draco.destroy(dracoDecoder);
  311. return result;
  312. }
  313. function decode(parameters) {
  314. if (defined(parameters.bufferView)) {
  315. return decodePrimitive(parameters);
  316. }
  317. return decodePointCloud(parameters);
  318. }
  319. function initWorker(dracoModule) {
  320. draco = dracoModule;
  321. self.onmessage = createTaskProcessorWorker(decode);
  322. self.postMessage(true);
  323. }
  324. function decodeDraco(event) {
  325. const data = event.data;
  326. // Expect the first message to be to load a web assembly module
  327. const wasmConfig = data.webAssemblyConfig;
  328. if (defined(wasmConfig)) {
  329. // Require and compile WebAssembly module, or use fallback if not supported
  330. return require([wasmConfig.modulePath], function (dracoModule) {
  331. if (defined(wasmConfig.wasmBinaryFile)) {
  332. if (!defined(dracoModule)) {
  333. dracoModule = self.DracoDecoderModule;
  334. }
  335. dracoModule(wasmConfig).then(function (compiledModule) {
  336. initWorker(compiledModule);
  337. });
  338. } else {
  339. initWorker(dracoModule());
  340. }
  341. });
  342. }
  343. }
  344. export default decodeDraco;