DracoLoader.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import arraySlice from '../Core/arraySlice.js';
  2. import ComponentDatatype from '../Core/ComponentDatatype.js';
  3. import defined from '../Core/defined.js';
  4. import FeatureDetection from '../Core/FeatureDetection.js';
  5. import TaskProcessor from '../Core/TaskProcessor.js';
  6. import ForEach from '../ThirdParty/GltfPipeline/ForEach.js';
  7. import when from '../ThirdParty/when.js';
  8. /**
  9. * @private
  10. */
  11. function DracoLoader() {}
  12. // Maximum concurrency to use when decoding draco models
  13. DracoLoader._maxDecodingConcurrency = Math.max(FeatureDetection.hardwareConcurrency - 1, 1);
  14. // Exposed for testing purposes
  15. DracoLoader._decoderTaskProcessor = undefined;
  16. DracoLoader._taskProcessorReady = false;
  17. DracoLoader._getDecoderTaskProcessor = function () {
  18. if (!defined(DracoLoader._decoderTaskProcessor)) {
  19. var processor = new TaskProcessor('decodeDraco', DracoLoader._maxDecodingConcurrency);
  20. processor.initWebAssemblyModule({
  21. modulePath : 'ThirdParty/Workers/draco_wasm_wrapper.js',
  22. wasmBinaryFile : 'ThirdParty/draco_decoder.wasm',
  23. fallbackModulePath : 'ThirdParty/Workers/draco_decoder.js'
  24. }).then(function () {
  25. DracoLoader._taskProcessorReady = true;
  26. });
  27. DracoLoader._decoderTaskProcessor = processor;
  28. }
  29. return DracoLoader._decoderTaskProcessor;
  30. };
  31. /**
  32. * Returns true if the model uses or requires KHR_draco_mesh_compression.
  33. *
  34. * @private
  35. */
  36. DracoLoader.hasExtension = function(model) {
  37. return (defined(model.extensionsRequired.KHR_draco_mesh_compression)
  38. || defined(model.extensionsUsed.KHR_draco_mesh_compression));
  39. };
  40. function addBufferToLoadResources(loadResources, typedArray) {
  41. // Create a new id to differentiate from original glTF bufferViews
  42. var bufferViewId = 'runtime.' + Object.keys(loadResources.createdBufferViews).length;
  43. var loadResourceBuffers = loadResources.buffers;
  44. var id = Object.keys(loadResourceBuffers).length;
  45. loadResourceBuffers[id] = typedArray;
  46. loadResources.createdBufferViews[bufferViewId] = {
  47. buffer : id,
  48. byteOffset : 0,
  49. byteLength : typedArray.byteLength
  50. };
  51. return bufferViewId;
  52. }
  53. function addNewVertexBuffer(typedArray, model, context) {
  54. var loadResources = model._loadResources;
  55. var id = addBufferToLoadResources(loadResources, typedArray);
  56. loadResources.vertexBuffersToCreate.enqueue(id);
  57. return id;
  58. }
  59. function addNewIndexBuffer(indexArray, model, context) {
  60. var typedArray = indexArray.typedArray;
  61. var loadResources = model._loadResources;
  62. var id = addBufferToLoadResources(loadResources, typedArray);
  63. loadResources.indexBuffersToCreate.enqueue({
  64. id : id,
  65. componentType : ComponentDatatype.fromTypedArray(typedArray)
  66. });
  67. return {
  68. bufferViewId : id,
  69. numberOfIndices : indexArray.numberOfIndices
  70. };
  71. }
  72. function scheduleDecodingTask(decoderTaskProcessor, model, loadResources, context) {
  73. if (!DracoLoader._taskProcessorReady) {
  74. // The task processor is not ready to schedule tasks
  75. return;
  76. }
  77. var taskData = loadResources.primitivesToDecode.peek();
  78. if (!defined(taskData)) {
  79. // All primitives are processing
  80. return;
  81. }
  82. var promise = decoderTaskProcessor.scheduleTask(taskData, [taskData.array.buffer]);
  83. if (!defined(promise)) {
  84. // Cannot schedule another task this frame
  85. return;
  86. }
  87. loadResources.activeDecodingTasks++;
  88. loadResources.primitivesToDecode.dequeue();
  89. return promise.then(function (result) {
  90. loadResources.activeDecodingTasks--;
  91. var decodedIndexBuffer = addNewIndexBuffer(result.indexArray, model, context);
  92. var attributes = {};
  93. var decodedAttributeData = result.attributeData;
  94. for (var attributeName in decodedAttributeData) {
  95. if (decodedAttributeData.hasOwnProperty(attributeName)) {
  96. var attribute = decodedAttributeData[attributeName];
  97. var vertexArray = attribute.array;
  98. var vertexBufferView = addNewVertexBuffer(vertexArray, model, context);
  99. var data = attribute.data;
  100. data.bufferView = vertexBufferView;
  101. attributes[attributeName] = data;
  102. }
  103. }
  104. model._decodedData[taskData.mesh + '.primitive.' + taskData.primitive] = {
  105. bufferView : decodedIndexBuffer.bufferViewId,
  106. numberOfIndices : decodedIndexBuffer.numberOfIndices,
  107. attributes : attributes
  108. };
  109. });
  110. }
  111. DracoLoader._decodedModelResourceCache = undefined;
  112. /**
  113. * Parses draco extension on model primitives and
  114. * adds the decoding data to the model's load resources.
  115. *
  116. * @private
  117. */
  118. DracoLoader.parse = function(model, context) {
  119. if (!DracoLoader.hasExtension(model)) {
  120. return;
  121. }
  122. var loadResources = model._loadResources;
  123. var cacheKey = model.cacheKey;
  124. if (defined(cacheKey)) {
  125. if (!defined(DracoLoader._decodedModelResourceCache)) {
  126. if (!defined(context.cache.modelDecodingCache)) {
  127. context.cache.modelDecodingCache = {};
  128. }
  129. DracoLoader._decodedModelResourceCache = context.cache.modelDecodingCache;
  130. }
  131. // Decoded data for model will be loaded from cache
  132. var cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
  133. if (defined(cachedData)) {
  134. cachedData.count++;
  135. loadResources.pendingDecodingCache = true;
  136. return;
  137. }
  138. }
  139. var dequantizeInShader = model._dequantizeInShader;
  140. var gltf = model.gltf;
  141. ForEach.mesh(gltf, function(mesh, meshId) {
  142. ForEach.meshPrimitive(mesh, function(primitive, primitiveId) {
  143. if (!defined(primitive.extensions)) {
  144. return;
  145. }
  146. var compressionData = primitive.extensions.KHR_draco_mesh_compression;
  147. if (!defined(compressionData)) {
  148. return;
  149. }
  150. var bufferView = gltf.bufferViews[compressionData.bufferView];
  151. var typedArray = arraySlice(gltf.buffers[bufferView.buffer].extras._pipeline.source, bufferView.byteOffset, bufferView.byteOffset + bufferView.byteLength);
  152. loadResources.primitivesToDecode.enqueue({
  153. mesh : meshId,
  154. primitive : primitiveId,
  155. array : typedArray,
  156. bufferView : bufferView,
  157. compressedAttributes : compressionData.attributes,
  158. dequantizeInShader : dequantizeInShader
  159. });
  160. });
  161. });
  162. };
  163. /**
  164. * Schedules decoding tasks available this frame.
  165. * @private
  166. */
  167. DracoLoader.decodeModel = function(model, context) {
  168. if (!DracoLoader.hasExtension(model)) {
  169. return when.resolve();
  170. }
  171. var loadResources = model._loadResources;
  172. var cacheKey = model.cacheKey;
  173. if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) {
  174. var cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
  175. // Load decoded data for model when cache is ready
  176. if (defined(cachedData) && loadResources.pendingDecodingCache) {
  177. return when(cachedData.ready, function () {
  178. model._decodedData = cachedData.data;
  179. loadResources.pendingDecodingCache = false;
  180. });
  181. }
  182. // Decoded data for model should be cached when ready
  183. DracoLoader._decodedModelResourceCache[cacheKey] = {
  184. ready : false,
  185. count : 1,
  186. data : undefined
  187. };
  188. }
  189. if (loadResources.primitivesToDecode.length === 0) {
  190. // No more tasks to schedule
  191. return when.resolve();
  192. }
  193. var decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
  194. var decodingPromises = [];
  195. var promise = scheduleDecodingTask(decoderTaskProcessor, model, loadResources, context);
  196. while (defined(promise)) {
  197. decodingPromises.push(promise);
  198. promise = scheduleDecodingTask(decoderTaskProcessor, model, loadResources, context);
  199. }
  200. return when.all(decodingPromises);
  201. };
  202. /**
  203. * Decodes a compressed point cloud. Returns undefined if the task cannot be scheduled.
  204. * @private
  205. */
  206. DracoLoader.decodePointCloud = function(parameters) {
  207. var decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
  208. if (!DracoLoader._taskProcessorReady) {
  209. // The task processor is not ready to schedule tasks
  210. return;
  211. }
  212. return decoderTaskProcessor.scheduleTask(parameters, [parameters.buffer.buffer]);
  213. };
  214. /**
  215. * Caches a models decoded data so it doesn't need to decode more than once.
  216. * @private
  217. */
  218. DracoLoader.cacheDataForModel = function(model) {
  219. var cacheKey = model.cacheKey;
  220. if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) {
  221. var cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
  222. if (defined(cachedData)) {
  223. cachedData.ready = true;
  224. cachedData.data = model._decodedData;
  225. }
  226. }
  227. };
  228. /**
  229. * Destroys the cached data that this model references if it is no longer in use.
  230. * @private
  231. */
  232. DracoLoader.destroyCachedDataForModel = function(model) {
  233. var cacheKey = model.cacheKey;
  234. if (defined(cacheKey) && defined(DracoLoader._decodedModelResourceCache)) {
  235. var cachedData = DracoLoader._decodedModelResourceCache[cacheKey];
  236. if (defined(cachedData) && --cachedData.count === 0) {
  237. delete DracoLoader._decodedModelResourceCache[cacheKey];
  238. }
  239. }
  240. };
  241. export default DracoLoader;