GltfBufferViewLoader.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import Check from "../Core/Check.js";
  2. import defaultValue from "../Core/defaultValue.js";
  3. import defined from "../Core/defined.js";
  4. import hasExtension from "./hasExtension.js";
  5. import { MeshoptDecoder } from "meshoptimizer";
  6. import ResourceLoader from "./ResourceLoader.js";
  7. import ResourceLoaderState from "./ResourceLoaderState.js";
  8. /**
  9. * Loads a glTF buffer view.
  10. * <p>
  11. * Implements the {@link ResourceLoader} interface.
  12. * </p>
  13. *
  14. * @alias GltfBufferViewLoader
  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 {number} options.bufferViewId The buffer view ID.
  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. function GltfBufferViewLoader(options) {
  29. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  30. const resourceCache = options.resourceCache;
  31. const gltf = options.gltf;
  32. const bufferViewId = options.bufferViewId;
  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.number("options.bufferViewId", bufferViewId);
  40. Check.typeOf.object("options.gltfResource", gltfResource);
  41. Check.typeOf.object("options.baseResource", baseResource);
  42. //>>includeEnd('debug');
  43. const bufferView = gltf.bufferViews[bufferViewId];
  44. let bufferId = bufferView.buffer;
  45. let byteOffset = bufferView.byteOffset;
  46. let byteLength = bufferView.byteLength;
  47. let hasMeshopt = false;
  48. let meshoptByteStride;
  49. let meshoptCount;
  50. let meshoptMode;
  51. let meshoptFilter;
  52. if (hasExtension(bufferView, "EXT_meshopt_compression")) {
  53. const meshopt = bufferView.extensions.EXT_meshopt_compression;
  54. bufferId = meshopt.buffer;
  55. byteOffset = defaultValue(meshopt.byteOffset, 0);
  56. byteLength = meshopt.byteLength;
  57. hasMeshopt = true;
  58. meshoptByteStride = meshopt.byteStride;
  59. meshoptCount = meshopt.count;
  60. meshoptMode = meshopt.mode;
  61. meshoptFilter = defaultValue(meshopt.filter, "NONE");
  62. }
  63. const buffer = gltf.buffers[bufferId];
  64. this._hasMeshopt = hasMeshopt;
  65. this._meshoptByteStride = meshoptByteStride;
  66. this._meshoptCount = meshoptCount;
  67. this._meshoptMode = meshoptMode;
  68. this._meshoptFilter = meshoptFilter;
  69. this._resourceCache = resourceCache;
  70. this._gltfResource = gltfResource;
  71. this._baseResource = baseResource;
  72. this._buffer = buffer;
  73. this._bufferId = bufferId;
  74. this._byteOffset = byteOffset;
  75. this._byteLength = byteLength;
  76. this._cacheKey = cacheKey;
  77. this._bufferLoader = undefined;
  78. this._typedArray = undefined;
  79. this._state = ResourceLoaderState.UNLOADED;
  80. this._promise = undefined;
  81. }
  82. if (defined(Object.create)) {
  83. GltfBufferViewLoader.prototype = Object.create(ResourceLoader.prototype);
  84. GltfBufferViewLoader.prototype.constructor = GltfBufferViewLoader;
  85. }
  86. Object.defineProperties(GltfBufferViewLoader.prototype, {
  87. /**
  88. * The cache key of the resource.
  89. *
  90. * @memberof GltfBufferViewLoader.prototype
  91. *
  92. * @type {string}
  93. * @readonly
  94. * @private
  95. */
  96. cacheKey: {
  97. get: function () {
  98. return this._cacheKey;
  99. },
  100. },
  101. /**
  102. * The typed array containing buffer view data.
  103. *
  104. * @memberof GltfBufferViewLoader.prototype
  105. *
  106. * @type {Uint8Array}
  107. * @readonly
  108. * @private
  109. */
  110. typedArray: {
  111. get: function () {
  112. return this._typedArray;
  113. },
  114. },
  115. });
  116. async function loadResources(loader) {
  117. try {
  118. const bufferLoader = getBufferLoader(loader);
  119. loader._bufferLoader = bufferLoader;
  120. await bufferLoader.load();
  121. if (loader.isDestroyed()) {
  122. return;
  123. }
  124. const bufferTypedArray = bufferLoader.typedArray;
  125. const bufferViewTypedArray = new Uint8Array(
  126. bufferTypedArray.buffer,
  127. bufferTypedArray.byteOffset + loader._byteOffset,
  128. loader._byteLength
  129. );
  130. // Unload the buffer
  131. loader.unload();
  132. loader._typedArray = bufferViewTypedArray;
  133. if (loader._hasMeshopt) {
  134. const count = loader._meshoptCount;
  135. const byteStride = loader._meshoptByteStride;
  136. const result = new Uint8Array(count * byteStride);
  137. MeshoptDecoder.decodeGltfBuffer(
  138. result,
  139. count,
  140. byteStride,
  141. loader._typedArray,
  142. loader._meshoptMode,
  143. loader._meshoptFilter
  144. );
  145. loader._typedArray = result;
  146. }
  147. loader._state = ResourceLoaderState.READY;
  148. return loader;
  149. } catch (error) {
  150. if (loader.isDestroyed()) {
  151. return;
  152. }
  153. loader.unload();
  154. loader._state = ResourceLoaderState.FAILED;
  155. const errorMessage = "Failed to load buffer view";
  156. throw loader.getError(errorMessage, error);
  157. }
  158. }
  159. /**
  160. * Loads the resource.
  161. * @returns {Promise<GltfBufferViewLoader>} A promise which resolves to the loader when the resource loading is completed.
  162. * @private
  163. */
  164. GltfBufferViewLoader.prototype.load = async function () {
  165. if (defined(this._promise)) {
  166. return this._promise;
  167. }
  168. this._state = ResourceLoaderState.LOADING;
  169. this._promise = loadResources(this);
  170. return this._promise;
  171. };
  172. function getBufferLoader(bufferViewLoader) {
  173. const resourceCache = bufferViewLoader._resourceCache;
  174. const buffer = bufferViewLoader._buffer;
  175. if (defined(buffer.uri)) {
  176. const baseResource = bufferViewLoader._baseResource;
  177. const resource = baseResource.getDerivedResource({
  178. url: buffer.uri,
  179. });
  180. return resourceCache.getExternalBufferLoader({
  181. resource: resource,
  182. });
  183. }
  184. return resourceCache.getEmbeddedBufferLoader({
  185. parentResource: bufferViewLoader._gltfResource,
  186. bufferId: bufferViewLoader._bufferId,
  187. });
  188. }
  189. /**
  190. * Unloads the resource.
  191. * @private
  192. */
  193. GltfBufferViewLoader.prototype.unload = function () {
  194. if (defined(this._bufferLoader) && !this._bufferLoader.isDestroyed()) {
  195. this._resourceCache.unload(this._bufferLoader);
  196. }
  197. this._bufferLoader = undefined;
  198. this._typedArray = undefined;
  199. };
  200. export default GltfBufferViewLoader;