decodeDraco.js 10 KB

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