GltfDracoLoader.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import Check from "../Core/Check.js";
  2. import defaultValue from "../Core/defaultValue.js";
  3. import defer from "../Core/defer.js";
  4. import defined from "../Core/defined.js";
  5. import DracoLoader from "./DracoLoader.js";
  6. import ResourceLoader from "./ResourceLoader.js";
  7. import ResourceLoaderState from "./ResourceLoaderState.js";
  8. /**
  9. * Load a draco buffer from a glTF.
  10. * <p>
  11. * Implements the {@link ResourceLoader} interface.
  12. * </p>
  13. *
  14. * @alias GltfDracoLoader
  15. * @constructor
  16. * @augments ResourceLoader
  17. *
  18. * @param {Object} options Object with the following properties:
  19. * @param {ResourceCache} options.resourceCache The {@link ResourceCache} (to avoid circular dependencies).
  20. * @param {Object} options.gltf The glTF JSON.
  21. * @param {Object} options.draco The Draco extension object.
  22. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  23. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  24. * @param {String} [options.cacheKey] The cache key of the resource.
  25. *
  26. * @private
  27. */
  28. export default function GltfDracoLoader(options) {
  29. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  30. const resourceCache = options.resourceCache;
  31. const gltf = options.gltf;
  32. const draco = options.draco;
  33. const gltfResource = options.gltfResource;
  34. const baseResource = options.baseResource;
  35. const cacheKey = options.cacheKey;
  36. //>>includeStart('debug', pragmas.debug);
  37. Check.typeOf.func("options.resourceCache", resourceCache);
  38. Check.typeOf.object("options.gltf", gltf);
  39. Check.typeOf.object("options.draco", draco);
  40. Check.typeOf.object("options.gltfResource", gltfResource);
  41. Check.typeOf.object("options.baseResource", baseResource);
  42. //>>includeEnd('debug');
  43. this._resourceCache = resourceCache;
  44. this._gltfResource = gltfResource;
  45. this._baseResource = baseResource;
  46. this._gltf = gltf;
  47. this._draco = draco;
  48. this._cacheKey = cacheKey;
  49. this._bufferViewLoader = undefined;
  50. this._bufferViewTypedArray = undefined;
  51. this._decodePromise = undefined;
  52. this._decodedData = undefined;
  53. this._state = ResourceLoaderState.UNLOADED;
  54. this._promise = defer();
  55. }
  56. if (defined(Object.create)) {
  57. GltfDracoLoader.prototype = Object.create(ResourceLoader.prototype);
  58. GltfDracoLoader.prototype.constructor = GltfDracoLoader;
  59. }
  60. Object.defineProperties(GltfDracoLoader.prototype, {
  61. /**
  62. * A promise that resolves to the resource when the resource is ready.
  63. *
  64. * @memberof GltfDracoLoader.prototype
  65. *
  66. * @type {Promise.<GltfDracoLoader>}
  67. * @readonly
  68. * @private
  69. */
  70. promise: {
  71. get: function () {
  72. return this._promise.promise;
  73. },
  74. },
  75. /**
  76. * The cache key of the resource.
  77. *
  78. * @memberof GltfDracoLoader.prototype
  79. *
  80. * @type {String}
  81. * @readonly
  82. * @private
  83. */
  84. cacheKey: {
  85. get: function () {
  86. return this._cacheKey;
  87. },
  88. },
  89. /**
  90. * The decoded data.
  91. *
  92. * @memberof GltfDracoLoader.prototype
  93. *
  94. * @type {Object}
  95. * @readonly
  96. * @private
  97. */
  98. decodedData: {
  99. get: function () {
  100. return this._decodedData;
  101. },
  102. },
  103. });
  104. /**
  105. * Loads the resource.
  106. * @private
  107. */
  108. GltfDracoLoader.prototype.load = function () {
  109. const resourceCache = this._resourceCache;
  110. const bufferViewLoader = resourceCache.loadBufferView({
  111. gltf: this._gltf,
  112. bufferViewId: this._draco.bufferView,
  113. gltfResource: this._gltfResource,
  114. baseResource: this._baseResource,
  115. });
  116. this._bufferViewLoader = bufferViewLoader;
  117. this._state = ResourceLoaderState.LOADING;
  118. const that = this;
  119. bufferViewLoader.promise
  120. .then(function () {
  121. if (that.isDestroyed()) {
  122. return;
  123. }
  124. // Now wait for process() to run to finish loading
  125. that._bufferViewTypedArray = bufferViewLoader.typedArray;
  126. that._state = ResourceLoaderState.PROCESSING;
  127. })
  128. .catch(function (error) {
  129. if (that.isDestroyed()) {
  130. return;
  131. }
  132. handleError(that, error);
  133. });
  134. };
  135. function handleError(dracoLoader, error) {
  136. dracoLoader.unload();
  137. dracoLoader._state = ResourceLoaderState.FAILED;
  138. const errorMessage = "Failed to load Draco";
  139. dracoLoader._promise.reject(dracoLoader.getError(errorMessage, error));
  140. }
  141. /**
  142. * Processes the resource until it becomes ready.
  143. *
  144. * @param {FrameState} frameState The frame state.
  145. * @private
  146. */
  147. GltfDracoLoader.prototype.process = function (frameState) {
  148. //>>includeStart('debug', pragmas.debug);
  149. Check.typeOf.object("frameState", frameState);
  150. //>>includeEnd('debug');
  151. if (!defined(this._bufferViewTypedArray)) {
  152. // Not ready to decode the Draco buffer
  153. return;
  154. }
  155. if (defined(this._decodePromise)) {
  156. // Currently decoding
  157. return;
  158. }
  159. const draco = this._draco;
  160. const gltf = this._gltf;
  161. const bufferViews = gltf.bufferViews;
  162. const bufferViewId = draco.bufferView;
  163. const bufferView = bufferViews[bufferViewId];
  164. const compressedAttributes = draco.attributes;
  165. const decodeOptions = {
  166. // Need to make a copy of the typed array otherwise the underlying
  167. // ArrayBuffer may be accessed on both the worker and the main thread. This
  168. // leads to errors such as "ArrayBuffer at index 0 is already detached".
  169. // PERFORMANCE_IDEA: Look into SharedArrayBuffer to get around this.
  170. array: new Uint8Array(this._bufferViewTypedArray),
  171. bufferView: bufferView,
  172. compressedAttributes: compressedAttributes,
  173. dequantizeInShader: true,
  174. };
  175. const decodePromise = DracoLoader.decodeBufferView(decodeOptions);
  176. if (!defined(decodePromise)) {
  177. // Cannot schedule task this frame
  178. return;
  179. }
  180. const that = this;
  181. this._decodePromise = decodePromise
  182. .then(function (results) {
  183. if (that.isDestroyed()) {
  184. return;
  185. }
  186. // Unload everything except the decoded data
  187. that.unload();
  188. that._decodedData = {
  189. indices: results.indexArray,
  190. vertexAttributes: results.attributeData,
  191. };
  192. that._state = ResourceLoaderState.READY;
  193. that._promise.resolve(that);
  194. })
  195. .catch(function (error) {
  196. if (that.isDestroyed()) {
  197. return;
  198. }
  199. handleError(that, error);
  200. });
  201. };
  202. /**
  203. * Unloads the resource.
  204. * @private
  205. */
  206. GltfDracoLoader.prototype.unload = function () {
  207. if (defined(this._bufferViewLoader)) {
  208. this._resourceCache.unload(this._bufferViewLoader);
  209. }
  210. this._bufferViewLoader = undefined;
  211. this._bufferViewTypedArray = undefined;
  212. this._decodedData = undefined;
  213. this._gltf = undefined;
  214. };