GltfIndexBufferLoader.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. import Check from "../Core/Check.js";
  2. import ComponentDatatype from "../Core/ComponentDatatype.js";
  3. import defaultValue from "../Core/defaultValue.js";
  4. import defer from "../Core/defer.js";
  5. import defined from "../Core/defined.js";
  6. import IndexDatatype from "../Core/IndexDatatype.js";
  7. import Buffer from "../Renderer/Buffer.js";
  8. import BufferUsage from "../Renderer/BufferUsage.js";
  9. import JobType from "./JobType.js";
  10. import ResourceLoader from "./ResourceLoader.js";
  11. import ResourceLoaderState from "./ResourceLoaderState.js";
  12. /**
  13. * Loads an index buffer from a glTF accessor.
  14. * <p>
  15. * Implements the {@link ResourceLoader} interface.
  16. * </p>
  17. *
  18. * @alias GltfIndexBufferLoader
  19. * @constructor
  20. * @augments ResourceLoader
  21. *
  22. * @param {Object} options Object with the following properties:
  23. * @param {ResourceCache} options.resourceCache The {@link ResourceCache} (to avoid circular dependencies).
  24. * @param {Object} options.gltf The glTF JSON.
  25. * @param {Number} options.accessorId The accessor ID corresponding to the index buffer.
  26. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  27. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  28. * @param {Object} [options.draco] The Draco extension object.
  29. * @param {String} [options.cacheKey] The cache key of the resource.
  30. * @param {Boolean} [options.asynchronous=true] Determines if WebGL resource creation will be spread out over several frames or block until all WebGL resources are created.
  31. * @param {Boolean} [loadAsTypedArray=false] Load index buffer as a typed array instead of a GPU index buffer.
  32. *
  33. * @private
  34. */
  35. export default function GltfIndexBufferLoader(options) {
  36. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  37. const resourceCache = options.resourceCache;
  38. const gltf = options.gltf;
  39. const accessorId = options.accessorId;
  40. const gltfResource = options.gltfResource;
  41. const baseResource = options.baseResource;
  42. const draco = options.draco;
  43. const cacheKey = options.cacheKey;
  44. const asynchronous = defaultValue(options.asynchronous, true);
  45. const loadAsTypedArray = defaultValue(options.loadAsTypedArray, false);
  46. //>>includeStart('debug', pragmas.debug);
  47. Check.typeOf.func("options.resourceCache", resourceCache);
  48. Check.typeOf.object("options.gltf", gltf);
  49. Check.typeOf.number("options.accessorId", accessorId);
  50. Check.typeOf.object("options.gltfResource", gltfResource);
  51. Check.typeOf.object("options.baseResource", baseResource);
  52. //>>includeEnd('debug');
  53. const indexDatatype = gltf.accessors[accessorId].componentType;
  54. this._resourceCache = resourceCache;
  55. this._gltfResource = gltfResource;
  56. this._baseResource = baseResource;
  57. this._gltf = gltf;
  58. this._accessorId = accessorId;
  59. this._indexDatatype = indexDatatype;
  60. this._draco = draco;
  61. this._cacheKey = cacheKey;
  62. this._asynchronous = asynchronous;
  63. this._loadAsTypedArray = loadAsTypedArray;
  64. this._bufferViewLoader = undefined;
  65. this._dracoLoader = undefined;
  66. this._typedArray = undefined;
  67. this._buffer = undefined;
  68. this._state = ResourceLoaderState.UNLOADED;
  69. this._promise = defer();
  70. }
  71. if (defined(Object.create)) {
  72. GltfIndexBufferLoader.prototype = Object.create(ResourceLoader.prototype);
  73. GltfIndexBufferLoader.prototype.constructor = GltfIndexBufferLoader;
  74. }
  75. Object.defineProperties(GltfIndexBufferLoader.prototype, {
  76. /**
  77. * A promise that resolves to the resource when the resource is ready.
  78. *
  79. * @memberof GltfIndexBufferLoader.prototype
  80. *
  81. * @type {Promise.<GltfIndexBufferLoader>}
  82. * @readonly
  83. * @private
  84. */
  85. promise: {
  86. get: function () {
  87. return this._promise.promise;
  88. },
  89. },
  90. /**
  91. * The cache key of the resource.
  92. *
  93. * @memberof GltfIndexBufferLoader.prototype
  94. *
  95. * @type {String}
  96. * @readonly
  97. * @private
  98. */
  99. cacheKey: {
  100. get: function () {
  101. return this._cacheKey;
  102. },
  103. },
  104. /**
  105. * The index buffer. This is only defined when <code>loadAsTypedArray</code> is false.
  106. *
  107. * @memberof GltfIndexBufferLoader.prototype
  108. *
  109. * @type {Buffer}
  110. * @readonly
  111. * @private
  112. */
  113. buffer: {
  114. get: function () {
  115. return this._buffer;
  116. },
  117. },
  118. /**
  119. * The typed array containing indices. This is only defined when <code>loadAsTypedArray</code> is true.
  120. *
  121. * @memberof GltfIndexBufferLoader.prototype
  122. *
  123. * @type {Uint8Array|Uint16Array|Uint32Array}
  124. * @readonly
  125. * @private
  126. */
  127. typedArray: {
  128. get: function () {
  129. return this._typedArray;
  130. },
  131. },
  132. /**
  133. * The index datatype after decode.
  134. *
  135. * @memberof GltfIndexBufferLoader.prototype
  136. *
  137. * @type {IndexDatatype}
  138. * @readonly
  139. * @private
  140. */
  141. indexDatatype: {
  142. get: function () {
  143. return this._indexDatatype;
  144. },
  145. },
  146. });
  147. /**
  148. * Loads the resource.
  149. * @private
  150. */
  151. GltfIndexBufferLoader.prototype.load = function () {
  152. if (defined(this._draco)) {
  153. loadFromDraco(this);
  154. } else {
  155. loadFromBufferView(this);
  156. }
  157. };
  158. function loadFromDraco(indexBufferLoader) {
  159. const resourceCache = indexBufferLoader._resourceCache;
  160. const dracoLoader = resourceCache.loadDraco({
  161. gltf: indexBufferLoader._gltf,
  162. draco: indexBufferLoader._draco,
  163. gltfResource: indexBufferLoader._gltfResource,
  164. baseResource: indexBufferLoader._baseResource,
  165. });
  166. indexBufferLoader._dracoLoader = dracoLoader;
  167. indexBufferLoader._state = ResourceLoaderState.LOADING;
  168. dracoLoader.promise
  169. .then(function () {
  170. if (indexBufferLoader.isDestroyed()) {
  171. return;
  172. }
  173. // Now wait for process() to run to finish loading
  174. const typedArray = dracoLoader.decodedData.indices.typedArray;
  175. indexBufferLoader._typedArray = typedArray;
  176. // The index datatype may be a smaller datatype after draco decode
  177. indexBufferLoader._indexDatatype = ComponentDatatype.fromTypedArray(
  178. typedArray
  179. );
  180. indexBufferLoader._state = ResourceLoaderState.PROCESSING;
  181. })
  182. .catch(function (error) {
  183. if (indexBufferLoader.isDestroyed()) {
  184. return;
  185. }
  186. handleError(indexBufferLoader, error);
  187. });
  188. }
  189. function loadFromBufferView(indexBufferLoader) {
  190. const gltf = indexBufferLoader._gltf;
  191. const accessorId = indexBufferLoader._accessorId;
  192. const accessor = gltf.accessors[accessorId];
  193. const bufferViewId = accessor.bufferView;
  194. const resourceCache = indexBufferLoader._resourceCache;
  195. const bufferViewLoader = resourceCache.loadBufferView({
  196. gltf: gltf,
  197. bufferViewId: bufferViewId,
  198. gltfResource: indexBufferLoader._gltfResource,
  199. baseResource: indexBufferLoader._baseResource,
  200. });
  201. indexBufferLoader._state = ResourceLoaderState.LOADING;
  202. indexBufferLoader._bufferViewLoader = bufferViewLoader;
  203. bufferViewLoader.promise
  204. .then(function () {
  205. if (indexBufferLoader.isDestroyed()) {
  206. return;
  207. }
  208. // Now wait for process() to run to finish loading
  209. const bufferViewTypedArray = bufferViewLoader.typedArray;
  210. indexBufferLoader._typedArray = createIndicesTypedArray(
  211. indexBufferLoader,
  212. bufferViewTypedArray
  213. );
  214. indexBufferLoader._state = ResourceLoaderState.PROCESSING;
  215. })
  216. .catch(function (error) {
  217. if (indexBufferLoader.isDestroyed()) {
  218. return;
  219. }
  220. handleError(indexBufferLoader, error);
  221. });
  222. }
  223. function createIndicesTypedArray(indexBufferLoader, bufferViewTypedArray) {
  224. const gltf = indexBufferLoader._gltf;
  225. const accessorId = indexBufferLoader._accessorId;
  226. const accessor = gltf.accessors[accessorId];
  227. const count = accessor.count;
  228. const indexDatatype = accessor.componentType;
  229. const arrayBuffer = bufferViewTypedArray.buffer;
  230. const byteOffset = bufferViewTypedArray.byteOffset + accessor.byteOffset;
  231. let typedArray;
  232. if (indexDatatype === IndexDatatype.UNSIGNED_BYTE) {
  233. typedArray = new Uint8Array(arrayBuffer, byteOffset, count);
  234. } else if (indexDatatype === IndexDatatype.UNSIGNED_SHORT) {
  235. typedArray = new Uint16Array(arrayBuffer, byteOffset, count);
  236. } else if (indexDatatype === IndexDatatype.UNSIGNED_INT) {
  237. typedArray = new Uint32Array(arrayBuffer, byteOffset, count);
  238. }
  239. return typedArray;
  240. }
  241. function handleError(indexBufferLoader, error) {
  242. indexBufferLoader.unload();
  243. indexBufferLoader._state = ResourceLoaderState.FAILED;
  244. const errorMessage = "Failed to load index buffer";
  245. error = indexBufferLoader.getError(errorMessage, error);
  246. indexBufferLoader._promise.reject(error);
  247. }
  248. function CreateIndexBufferJob() {
  249. this.typedArray = undefined;
  250. this.indexDatatype = undefined;
  251. this.context = undefined;
  252. this.buffer = undefined;
  253. }
  254. CreateIndexBufferJob.prototype.set = function (
  255. typedArray,
  256. indexDatatype,
  257. context
  258. ) {
  259. this.typedArray = typedArray;
  260. this.indexDatatype = indexDatatype;
  261. this.context = context;
  262. };
  263. CreateIndexBufferJob.prototype.execute = function () {
  264. this.buffer = createIndexBuffer(
  265. this.typedArray,
  266. this.indexDatatype,
  267. this.context
  268. );
  269. };
  270. function createIndexBuffer(typedArray, indexDatatype, context) {
  271. const buffer = Buffer.createIndexBuffer({
  272. typedArray: typedArray,
  273. context: context,
  274. usage: BufferUsage.STATIC_DRAW,
  275. indexDatatype: indexDatatype,
  276. });
  277. buffer.vertexArrayDestroyable = false;
  278. return buffer;
  279. }
  280. const scratchIndexBufferJob = new CreateIndexBufferJob();
  281. /**
  282. * Processes the resource until it becomes ready.
  283. *
  284. * @param {FrameState} frameState The frame state.
  285. * @private
  286. */
  287. GltfIndexBufferLoader.prototype.process = function (frameState) {
  288. //>>includeStart('debug', pragmas.debug);
  289. Check.typeOf.object("frameState", frameState);
  290. //>>includeEnd('debug');
  291. if (this._state === ResourceLoaderState.READY) {
  292. return;
  293. }
  294. const typedArray = this._typedArray;
  295. const indexDatatype = this._indexDatatype;
  296. if (defined(this._dracoLoader)) {
  297. this._dracoLoader.process(frameState);
  298. }
  299. if (defined(this._bufferViewLoader)) {
  300. this._bufferViewLoader.process(frameState);
  301. }
  302. if (!defined(typedArray)) {
  303. // Buffer view hasn't been loaded yet
  304. return;
  305. }
  306. // WebGL1 has no way to retrieve the contents of buffers that are
  307. // on the GPU. Therefore, the index buffer is stored in CPU memory
  308. // in case it needs to be referenced later.
  309. const useWebgl2 = frameState.context.webgl2;
  310. if (this._loadAsTypedArray || !useWebgl2) {
  311. // Unload everything except the typed array
  312. this.unload();
  313. this._typedArray = typedArray;
  314. this._state = ResourceLoaderState.READY;
  315. this._promise.resolve(this);
  316. return;
  317. }
  318. let buffer;
  319. if (this._asynchronous) {
  320. const indexBufferJob = scratchIndexBufferJob;
  321. indexBufferJob.set(typedArray, indexDatatype, frameState.context);
  322. const jobScheduler = frameState.jobScheduler;
  323. if (!jobScheduler.execute(indexBufferJob, JobType.BUFFER)) {
  324. // Job scheduler is full. Try again next frame.
  325. return;
  326. }
  327. buffer = indexBufferJob.buffer;
  328. } else {
  329. buffer = createIndexBuffer(typedArray, indexDatatype, frameState.context);
  330. }
  331. // Unload everything except the index buffer
  332. this.unload();
  333. this._buffer = buffer;
  334. this._state = ResourceLoaderState.READY;
  335. this._promise.resolve(this);
  336. };
  337. /**
  338. * Unloads the resource.
  339. * @private
  340. */
  341. GltfIndexBufferLoader.prototype.unload = function () {
  342. if (defined(this._buffer)) {
  343. this._buffer.destroy();
  344. }
  345. const resourceCache = this._resourceCache;
  346. if (defined(this._bufferViewLoader)) {
  347. resourceCache.unload(this._bufferViewLoader);
  348. }
  349. if (defined(this._dracoLoader)) {
  350. resourceCache.unload(this._dracoLoader);
  351. }
  352. this._bufferViewLoader = undefined;
  353. this._dracoLoader = undefined;
  354. this._typedArray = undefined;
  355. this._buffer = undefined;
  356. this._gltf = undefined;
  357. };