ResourceCache.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. import Check from "../Core/Check.js";
  2. import defaultValue from "../Core/defaultValue.js";
  3. import defined from "../Core/defined.js";
  4. import DeveloperError from "../Core/DeveloperError.js";
  5. import BufferLoader from "./BufferLoader.js";
  6. import GltfBufferViewLoader from "./GltfBufferViewLoader.js";
  7. import GltfDracoLoader from "./GltfDracoLoader.js";
  8. import GltfImageLoader from "./GltfImageLoader.js";
  9. import GltfIndexBufferLoader from "./GltfIndexBufferLoader.js";
  10. import GltfJsonLoader from "./GltfJsonLoader.js";
  11. import GltfTextureLoader from "./GltfTextureLoader.js";
  12. import GltfVertexBufferLoader from "./GltfVertexBufferLoader.js";
  13. import MetadataSchemaLoader from "./MetadataSchemaLoader.js";
  14. import ResourceCacheKey from "./ResourceCacheKey.js";
  15. import ResourceCacheStatistics from "./ResourceCacheStatistics.js";
  16. /**
  17. * Cache for resources shared across 3D Tiles and glTF.
  18. *
  19. * @namespace ResourceCache
  20. *
  21. * @private
  22. */
  23. function ResourceCache() {}
  24. ResourceCache.cacheEntries = {};
  25. // Statistics about binary data stored in the resource cache
  26. ResourceCache.statistics = new ResourceCacheStatistics();
  27. /**
  28. * A reference-counted cache entry.
  29. *
  30. * @param {ResourceLoader} resourceLoader The resource.
  31. *
  32. * @alias CacheEntry
  33. * @constructor
  34. *
  35. * @private
  36. */
  37. function CacheEntry(resourceLoader) {
  38. this.referenceCount = 1;
  39. this.resourceLoader = resourceLoader;
  40. // For unit testing only
  41. this._statisticsPromise = undefined;
  42. }
  43. /**
  44. * Gets a resource from the cache. If the resource exists its reference count is
  45. * incremented. Otherwise, if no resource loader exists, undefined is returned.
  46. *
  47. * @param {string} cacheKey The cache key of the resource.
  48. *
  49. * @returns {ResourceLoader|undefined} The resource.
  50. * @private
  51. */
  52. ResourceCache.get = function (cacheKey) {
  53. //>>includeStart('debug', pragmas.debug);
  54. Check.typeOf.string("cacheKey", cacheKey);
  55. //>>includeEnd('debug');
  56. const cacheEntry = ResourceCache.cacheEntries[cacheKey];
  57. if (defined(cacheEntry)) {
  58. ++cacheEntry.referenceCount;
  59. return cacheEntry.resourceLoader;
  60. }
  61. return undefined;
  62. };
  63. /**
  64. * Adds it to the cache.
  65. *
  66. * @param {ResourceLoader} resourceLoader The resource.
  67. * @returns {ResourceLoader} The resource.
  68. *
  69. * @exception {DeveloperError} Resource with this cacheKey is already in the cache
  70. * @private
  71. */
  72. ResourceCache.add = function (resourceLoader) {
  73. //>>includeStart('debug', pragmas.debug);
  74. Check.typeOf.object("resourceLoader", resourceLoader);
  75. //>>includeEnd('debug');
  76. const cacheKey = resourceLoader.cacheKey;
  77. //>>includeStart('debug', pragmas.debug);
  78. Check.typeOf.string("options.resourceLoader.cacheKey", cacheKey);
  79. if (defined(ResourceCache.cacheEntries[cacheKey])) {
  80. throw new DeveloperError(
  81. `Resource with this cacheKey is already in the cache: ${cacheKey}`
  82. );
  83. }
  84. //>>includeEnd('debug');
  85. ResourceCache.cacheEntries[cacheKey] = new CacheEntry(resourceLoader);
  86. return resourceLoader;
  87. };
  88. /**
  89. * Unloads a resource from the cache. When the reference count hits zero the
  90. * resource is destroyed.
  91. *
  92. * @param {ResourceLoader} resourceLoader The resource.
  93. *
  94. * @exception {DeveloperError} Resource is not in the cache.
  95. * @exception {DeveloperError} Cannot unload resource that has no references.
  96. * @private
  97. */
  98. ResourceCache.unload = function (resourceLoader) {
  99. //>>includeStart('debug', pragmas.debug);
  100. Check.typeOf.object("resourceLoader", resourceLoader);
  101. //>>includeEnd('debug');
  102. const cacheKey = resourceLoader.cacheKey;
  103. const cacheEntry = ResourceCache.cacheEntries[cacheKey];
  104. //>>includeStart('debug', pragmas.debug);
  105. if (!defined(cacheEntry)) {
  106. throw new DeveloperError(`Resource is not in the cache: ${cacheKey}`);
  107. }
  108. //>>includeEnd('debug');
  109. --cacheEntry.referenceCount;
  110. if (cacheEntry.referenceCount === 0) {
  111. ResourceCache.statistics.removeLoader(resourceLoader);
  112. resourceLoader.destroy();
  113. delete ResourceCache.cacheEntries[cacheKey];
  114. }
  115. };
  116. /**
  117. * Gets an existing schema loader from the cache, or creates a new loader if one does not already exist.
  118. *
  119. * @param {object} options Object with the following properties:
  120. * @param {object} [options.schema] An object that explicitly defines a schema JSON. Mutually exclusive with options.resource.
  121. * @param {Resource} [options.resource] The {@link Resource} pointing to the schema JSON. Mutually exclusive with options.schema.
  122. *
  123. * @returns {MetadataSchemaLoader} The cached schema resource.
  124. *
  125. * @exception {DeveloperError} One of options.schema and options.resource must be defined.
  126. * @private
  127. */
  128. ResourceCache.getSchemaLoader = function (options) {
  129. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  130. const schema = options.schema;
  131. const resource = options.resource;
  132. //>>includeStart('debug', pragmas.debug);
  133. if (defined(schema) === defined(resource)) {
  134. throw new DeveloperError(
  135. "One of options.schema and options.resource must be defined."
  136. );
  137. }
  138. //>>includeEnd('debug');
  139. const cacheKey = ResourceCacheKey.getSchemaCacheKey({
  140. schema: schema,
  141. resource: resource,
  142. });
  143. let schemaLoader = ResourceCache.get(cacheKey);
  144. if (defined(schemaLoader)) {
  145. return schemaLoader;
  146. }
  147. schemaLoader = new MetadataSchemaLoader({
  148. schema: schema,
  149. resource: resource,
  150. cacheKey: cacheKey,
  151. });
  152. return ResourceCache.add(schemaLoader);
  153. };
  154. /**
  155. * Gets an existing embedded buffer loader from the cache, or creates a new loader if one does not already exist.
  156. *
  157. * @param {object} options Object with the following properties:
  158. * @param {Resource} options.parentResource The {@link Resource} containing the embedded buffer.
  159. * @param {number} options.bufferId A unique identifier of the embedded buffer within the parent resource.
  160. * @param {Uint8Array} [options.typedArray] The typed array containing the embedded buffer contents.
  161. *
  162. * @returns {BufferLoader} The cached buffer loader.
  163. * @private
  164. */
  165. ResourceCache.getEmbeddedBufferLoader = function (options) {
  166. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  167. const parentResource = options.parentResource;
  168. const bufferId = options.bufferId;
  169. const typedArray = options.typedArray;
  170. //>>includeStart('debug', pragmas.debug);
  171. Check.typeOf.object("options.parentResource", parentResource);
  172. Check.typeOf.number("options.bufferId", bufferId);
  173. //>>includeEnd('debug');
  174. const cacheKey = ResourceCacheKey.getEmbeddedBufferCacheKey({
  175. parentResource: parentResource,
  176. bufferId: bufferId,
  177. });
  178. let bufferLoader = ResourceCache.get(cacheKey);
  179. if (defined(bufferLoader)) {
  180. return bufferLoader;
  181. }
  182. //>>includeStart('debug', pragmas.debug);
  183. Check.typeOf.object("options.typedArray", typedArray);
  184. //>>includeEnd('debug');
  185. bufferLoader = new BufferLoader({
  186. typedArray: typedArray,
  187. cacheKey: cacheKey,
  188. });
  189. return ResourceCache.add(bufferLoader);
  190. };
  191. /**
  192. * Gets an existing external buffer from loader the cache, or creates a new loader if one does not already exist.
  193. *
  194. * @param {object} options Object with the following properties:
  195. * @param {Resource} options.resource The {@link Resource} pointing to the external buffer.
  196. *
  197. * @returns {BufferLoader} The cached buffer loader.
  198. * @private
  199. */
  200. ResourceCache.getExternalBufferLoader = function (options) {
  201. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  202. const resource = options.resource;
  203. //>>includeStart('debug', pragmas.debug);
  204. Check.typeOf.object("options.resource", resource);
  205. //>>includeEnd('debug');
  206. const cacheKey = ResourceCacheKey.getExternalBufferCacheKey({
  207. resource: resource,
  208. });
  209. let bufferLoader = ResourceCache.get(cacheKey);
  210. if (defined(bufferLoader)) {
  211. return bufferLoader;
  212. }
  213. bufferLoader = new BufferLoader({
  214. resource: resource,
  215. cacheKey: cacheKey,
  216. });
  217. return ResourceCache.add(bufferLoader);
  218. };
  219. /**
  220. * Gets an existing glTF JSON loader from the cache, or creates a new loader if one does not already exist.
  221. *
  222. * @param {object} options Object with the following properties:
  223. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  224. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  225. * @param {Uint8Array} [options.typedArray] The typed array containing the glTF contents.
  226. * @param {object} [options.gltfJson] The parsed glTF JSON contents.
  227. *
  228. * @returns {GltfJsonLoader} The cached glTF JSON loader.
  229. * @private
  230. */
  231. ResourceCache.getGltfJsonLoader = function (options) {
  232. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  233. const gltfResource = options.gltfResource;
  234. const baseResource = options.baseResource;
  235. const typedArray = options.typedArray;
  236. const gltfJson = options.gltfJson;
  237. //>>includeStart('debug', pragmas.debug);
  238. Check.typeOf.object("options.gltfResource", gltfResource);
  239. Check.typeOf.object("options.baseResource", baseResource);
  240. //>>includeEnd('debug');
  241. const cacheKey = ResourceCacheKey.getGltfCacheKey({
  242. gltfResource: gltfResource,
  243. });
  244. let gltfJsonLoader = ResourceCache.get(cacheKey);
  245. if (defined(gltfJsonLoader)) {
  246. return gltfJsonLoader;
  247. }
  248. gltfJsonLoader = new GltfJsonLoader({
  249. resourceCache: ResourceCache,
  250. gltfResource: gltfResource,
  251. baseResource: baseResource,
  252. typedArray: typedArray,
  253. gltfJson: gltfJson,
  254. cacheKey: cacheKey,
  255. });
  256. return ResourceCache.add(gltfJsonLoader);
  257. };
  258. /**
  259. * Gets an existing glTF buffer view from the cache, or creates a new loader if one does not already exist.
  260. *
  261. * @param {object} options Object with the following properties:
  262. * @param {object} options.gltf The glTF JSON.
  263. * @param {number} options.bufferViewId The bufferView ID.
  264. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  265. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  266. *
  267. * @returns {GltfBufferViewLoader} The cached buffer view loader.
  268. * @private
  269. */
  270. ResourceCache.getBufferViewLoader = function (options) {
  271. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  272. const gltf = options.gltf;
  273. const bufferViewId = options.bufferViewId;
  274. const gltfResource = options.gltfResource;
  275. const baseResource = options.baseResource;
  276. //>>includeStart('debug', pragmas.debug);
  277. Check.typeOf.object("options.gltf", gltf);
  278. Check.typeOf.number("options.bufferViewId", bufferViewId);
  279. Check.typeOf.object("options.gltfResource", gltfResource);
  280. Check.typeOf.object("options.baseResource", baseResource);
  281. //>>includeEnd('debug');
  282. const cacheKey = ResourceCacheKey.getBufferViewCacheKey({
  283. gltf: gltf,
  284. bufferViewId: bufferViewId,
  285. gltfResource: gltfResource,
  286. baseResource: baseResource,
  287. });
  288. let bufferViewLoader = ResourceCache.get(cacheKey);
  289. if (defined(bufferViewLoader)) {
  290. return bufferViewLoader;
  291. }
  292. bufferViewLoader = new GltfBufferViewLoader({
  293. resourceCache: ResourceCache,
  294. gltf: gltf,
  295. bufferViewId: bufferViewId,
  296. gltfResource: gltfResource,
  297. baseResource: baseResource,
  298. cacheKey: cacheKey,
  299. });
  300. return ResourceCache.add(bufferViewLoader);
  301. };
  302. /**
  303. * Gets an existing Draco data from the cache, or creates a new loader if one does not already exist.
  304. *
  305. * @param {object} options Object with the following properties:
  306. * @param {object} options.gltf The glTF JSON.
  307. * @param {object} options.draco The Draco extension object.
  308. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  309. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  310. *
  311. * @returns {GltfDracoLoader} The cached Draco loader.
  312. * @private
  313. */
  314. ResourceCache.getDracoLoader = function (options) {
  315. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  316. const gltf = options.gltf;
  317. const draco = options.draco;
  318. const gltfResource = options.gltfResource;
  319. const baseResource = options.baseResource;
  320. //>>includeStart('debug', pragmas.debug);
  321. Check.typeOf.object("options.gltf", gltf);
  322. Check.typeOf.object("options.draco", draco);
  323. Check.typeOf.object("options.gltfResource", gltfResource);
  324. Check.typeOf.object("options.baseResource", baseResource);
  325. //>>includeEnd('debug');
  326. const cacheKey = ResourceCacheKey.getDracoCacheKey({
  327. gltf: gltf,
  328. draco: draco,
  329. gltfResource: gltfResource,
  330. baseResource: baseResource,
  331. });
  332. let dracoLoader = ResourceCache.get(cacheKey);
  333. if (defined(dracoLoader)) {
  334. return dracoLoader;
  335. }
  336. dracoLoader = new GltfDracoLoader({
  337. resourceCache: ResourceCache,
  338. gltf: gltf,
  339. draco: draco,
  340. gltfResource: gltfResource,
  341. baseResource: baseResource,
  342. cacheKey: cacheKey,
  343. });
  344. return ResourceCache.add(dracoLoader);
  345. };
  346. /**
  347. * Gets an existing glTF vertex buffer from the cache, or creates a new loader if one does not already exist.
  348. *
  349. * @param {object} options Object with the following properties:
  350. * @param {object} options.gltf The glTF JSON.
  351. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  352. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  353. * @param {FrameState} options.frameState The frame state.
  354. * @param {number} [options.bufferViewId] The bufferView ID corresponding to the vertex buffer.
  355. * @param {object} [options.draco] The Draco extension object.
  356. * @param {string} [options.attributeSemantic] The attribute semantic, e.g. POSITION or NORMAL.
  357. * @param {number} [options.accessorId] The accessor ID.
  358. * @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.
  359. * @param {boolean} [options.dequantize=false] Determines whether or not the vertex buffer will be dequantized on the CPU.
  360. * @param {boolean} [options.loadBuffer=false] Load vertex buffer as a GPU vertex buffer.
  361. * @param {boolean} [options.loadTypedArray=false] Load vertex buffer as a typed array.
  362. * @exception {DeveloperError} One of options.bufferViewId and options.draco must be defined.
  363. * @exception {DeveloperError} When options.draco is defined options.attributeSemantic must also be defined.
  364. * @exception {DeveloperError} When options.draco is defined options.accessorId must also be defined.
  365. *
  366. * @returns {GltfVertexBufferLoader} The cached vertex buffer loader.
  367. * @private
  368. */
  369. ResourceCache.getVertexBufferLoader = function (options) {
  370. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  371. const gltf = options.gltf;
  372. const gltfResource = options.gltfResource;
  373. const baseResource = options.baseResource;
  374. const frameState = options.frameState;
  375. const bufferViewId = options.bufferViewId;
  376. const draco = options.draco;
  377. const attributeSemantic = options.attributeSemantic;
  378. const accessorId = options.accessorId;
  379. const asynchronous = defaultValue(options.asynchronous, true);
  380. const dequantize = defaultValue(options.dequantize, false);
  381. const loadBuffer = defaultValue(options.loadBuffer, false);
  382. const loadTypedArray = defaultValue(options.loadTypedArray, false);
  383. //>>includeStart('debug', pragmas.debug);
  384. Check.typeOf.object("options.gltf", gltf);
  385. Check.typeOf.object("options.gltfResource", gltfResource);
  386. Check.typeOf.object("options.baseResource", baseResource);
  387. Check.typeOf.object("options.frameState", frameState);
  388. if (!loadBuffer && !loadTypedArray) {
  389. throw new DeveloperError(
  390. "At least one of loadBuffer and loadTypedArray must be true."
  391. );
  392. }
  393. const hasBufferViewId = defined(bufferViewId);
  394. const hasDraco = hasDracoCompression(draco, attributeSemantic);
  395. const hasAttributeSemantic = defined(attributeSemantic);
  396. const hasAccessorId = defined(accessorId);
  397. if (hasBufferViewId === hasDraco) {
  398. throw new DeveloperError(
  399. "One of options.bufferViewId and options.draco must be defined."
  400. );
  401. }
  402. if (hasDraco && !hasAttributeSemantic) {
  403. throw new DeveloperError(
  404. "When options.draco is defined options.attributeSemantic must also be defined."
  405. );
  406. }
  407. if (hasDraco && !hasAccessorId) {
  408. throw new DeveloperError(
  409. "When options.draco is defined options.haAccessorId must also be defined."
  410. );
  411. }
  412. if (hasDraco) {
  413. Check.typeOf.object("options.draco", draco);
  414. Check.typeOf.string("options.attributeSemantic", attributeSemantic);
  415. Check.typeOf.number("options.accessorId", accessorId);
  416. }
  417. //>>includeEnd('debug');
  418. const cacheKey = ResourceCacheKey.getVertexBufferCacheKey({
  419. gltf: gltf,
  420. gltfResource: gltfResource,
  421. baseResource: baseResource,
  422. frameState: frameState,
  423. bufferViewId: bufferViewId,
  424. draco: draco,
  425. attributeSemantic: attributeSemantic,
  426. dequantize: dequantize,
  427. loadBuffer: loadBuffer,
  428. loadTypedArray: loadTypedArray,
  429. });
  430. let vertexBufferLoader = ResourceCache.get(cacheKey);
  431. if (defined(vertexBufferLoader)) {
  432. return vertexBufferLoader;
  433. }
  434. vertexBufferLoader = new GltfVertexBufferLoader({
  435. resourceCache: ResourceCache,
  436. gltf: gltf,
  437. gltfResource: gltfResource,
  438. baseResource: baseResource,
  439. bufferViewId: bufferViewId,
  440. draco: draco,
  441. attributeSemantic: attributeSemantic,
  442. accessorId: accessorId,
  443. cacheKey: cacheKey,
  444. asynchronous: asynchronous,
  445. dequantize: dequantize,
  446. loadBuffer: loadBuffer,
  447. loadTypedArray: loadTypedArray,
  448. });
  449. return ResourceCache.add(vertexBufferLoader);
  450. };
  451. function hasDracoCompression(draco, semantic) {
  452. return (
  453. defined(draco) &&
  454. defined(draco.attributes) &&
  455. defined(draco.attributes[semantic])
  456. );
  457. }
  458. /**
  459. * Gets an existing glTF index buffer from the cache, or creates a new loader if one does not already exist.
  460. *
  461. * @param {object} options Object with the following properties:
  462. * @param {object} options.gltf The glTF JSON.
  463. * @param {number} options.accessorId The accessor ID corresponding to the index buffer.
  464. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  465. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  466. * @param {FrameState} options.frameState The frame state.
  467. * @param {object} [options.draco] The Draco extension object.
  468. * @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.
  469. * @param {boolean} [options.loadBuffer=false] Load index buffer as a GPU index buffer.
  470. * @param {boolean} [options.loadTypedArray=false] Load index buffer as a typed array.
  471. * @returns {GltfIndexBufferLoader} The cached index buffer loader.
  472. * @private
  473. */
  474. ResourceCache.getIndexBufferLoader = function (options) {
  475. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  476. const gltf = options.gltf;
  477. const accessorId = options.accessorId;
  478. const gltfResource = options.gltfResource;
  479. const baseResource = options.baseResource;
  480. const frameState = options.frameState;
  481. const draco = options.draco;
  482. const asynchronous = defaultValue(options.asynchronous, true);
  483. const loadBuffer = defaultValue(options.loadBuffer, false);
  484. const loadTypedArray = defaultValue(options.loadTypedArray, false);
  485. //>>includeStart('debug', pragmas.debug);
  486. Check.typeOf.object("options.gltf", gltf);
  487. Check.typeOf.number("options.accessorId", accessorId);
  488. Check.typeOf.object("options.gltfResource", gltfResource);
  489. Check.typeOf.object("options.baseResource", baseResource);
  490. Check.typeOf.object("options.frameState", frameState);
  491. if (!loadBuffer && !loadTypedArray) {
  492. throw new DeveloperError(
  493. "At least one of loadBuffer and loadTypedArray must be true."
  494. );
  495. }
  496. //>>includeEnd('debug');
  497. const cacheKey = ResourceCacheKey.getIndexBufferCacheKey({
  498. gltf: gltf,
  499. accessorId: accessorId,
  500. gltfResource: gltfResource,
  501. baseResource: baseResource,
  502. frameState: frameState,
  503. draco: draco,
  504. loadBuffer: loadBuffer,
  505. loadTypedArray: loadTypedArray,
  506. });
  507. let indexBufferLoader = ResourceCache.get(cacheKey);
  508. if (defined(indexBufferLoader)) {
  509. return indexBufferLoader;
  510. }
  511. indexBufferLoader = new GltfIndexBufferLoader({
  512. resourceCache: ResourceCache,
  513. gltf: gltf,
  514. accessorId: accessorId,
  515. gltfResource: gltfResource,
  516. baseResource: baseResource,
  517. draco: draco,
  518. cacheKey: cacheKey,
  519. asynchronous: asynchronous,
  520. loadBuffer: loadBuffer,
  521. loadTypedArray: loadTypedArray,
  522. });
  523. return ResourceCache.add(indexBufferLoader);
  524. };
  525. /**
  526. * Gets an existing glTF image from the cache, or creates a new loader if one does not already exist.
  527. *
  528. * @param {object} options Object with the following properties:
  529. * @param {object} options.gltf The glTF JSON.
  530. * @param {number} options.imageId The image ID.
  531. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  532. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  533. *
  534. * @returns {GltfImageLoader} The cached image loader.
  535. * @private
  536. */
  537. ResourceCache.getImageLoader = function (options) {
  538. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  539. const gltf = options.gltf;
  540. const imageId = options.imageId;
  541. const gltfResource = options.gltfResource;
  542. const baseResource = options.baseResource;
  543. //>>includeStart('debug', pragmas.debug);
  544. Check.typeOf.object("options.gltf", gltf);
  545. Check.typeOf.number("options.imageId", imageId);
  546. Check.typeOf.object("options.gltfResource", gltfResource);
  547. Check.typeOf.object("options.baseResource", baseResource);
  548. //>>includeEnd('debug');
  549. const cacheKey = ResourceCacheKey.getImageCacheKey({
  550. gltf: gltf,
  551. imageId: imageId,
  552. gltfResource: gltfResource,
  553. baseResource: baseResource,
  554. });
  555. let imageLoader = ResourceCache.get(cacheKey);
  556. if (defined(imageLoader)) {
  557. return imageLoader;
  558. }
  559. imageLoader = new GltfImageLoader({
  560. resourceCache: ResourceCache,
  561. gltf: gltf,
  562. imageId: imageId,
  563. gltfResource: gltfResource,
  564. baseResource: baseResource,
  565. cacheKey: cacheKey,
  566. });
  567. return ResourceCache.add(imageLoader);
  568. };
  569. /**
  570. * Gets an existing glTF texture from the cache, or creates a new loader if one does not already exist.
  571. *
  572. * @param {object} options Object with the following properties:
  573. * @param {object} options.gltf The glTF JSON.
  574. * @param {object} options.textureInfo The texture info object.
  575. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  576. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  577. * @param {SupportedImageFormats} options.supportedImageFormats The supported image formats.
  578. * @param {FrameState} options.frameState The frame state.
  579. * @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.
  580. *
  581. * @returns {GltfTextureLoader} The cached texture loader.
  582. * @private
  583. */
  584. ResourceCache.getTextureLoader = function (options) {
  585. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  586. const gltf = options.gltf;
  587. const textureInfo = options.textureInfo;
  588. const gltfResource = options.gltfResource;
  589. const baseResource = options.baseResource;
  590. const supportedImageFormats = options.supportedImageFormats;
  591. const frameState = options.frameState;
  592. const asynchronous = defaultValue(options.asynchronous, true);
  593. //>>includeStart('debug', pragmas.debug);
  594. Check.typeOf.object("options.gltf", gltf);
  595. Check.typeOf.object("options.textureInfo", textureInfo);
  596. Check.typeOf.object("options.gltfResource", gltfResource);
  597. Check.typeOf.object("options.baseResource", baseResource);
  598. Check.typeOf.object("options.supportedImageFormats", supportedImageFormats);
  599. Check.typeOf.object("options.frameState", frameState);
  600. //>>includeEnd('debug');
  601. const cacheKey = ResourceCacheKey.getTextureCacheKey({
  602. gltf: gltf,
  603. textureInfo: textureInfo,
  604. gltfResource: gltfResource,
  605. baseResource: baseResource,
  606. supportedImageFormats: supportedImageFormats,
  607. frameState: frameState,
  608. });
  609. let textureLoader = ResourceCache.get(cacheKey);
  610. if (defined(textureLoader)) {
  611. return textureLoader;
  612. }
  613. textureLoader = new GltfTextureLoader({
  614. resourceCache: ResourceCache,
  615. gltf: gltf,
  616. textureInfo: textureInfo,
  617. gltfResource: gltfResource,
  618. baseResource: baseResource,
  619. supportedImageFormats: supportedImageFormats,
  620. cacheKey: cacheKey,
  621. asynchronous: asynchronous,
  622. });
  623. return ResourceCache.add(textureLoader);
  624. };
  625. /**
  626. * Unload everything from the cache. This is used for unit testing.
  627. *
  628. * @private
  629. */
  630. ResourceCache.clearForSpecs = function () {
  631. // Unload in the order below. This prevents an unload function from unloading
  632. // a resource that has already been unloaded.
  633. const precedence = [
  634. GltfVertexBufferLoader,
  635. GltfIndexBufferLoader,
  636. GltfDracoLoader,
  637. GltfTextureLoader,
  638. GltfImageLoader,
  639. GltfBufferViewLoader,
  640. BufferLoader,
  641. MetadataSchemaLoader,
  642. GltfJsonLoader,
  643. ];
  644. let cacheKey;
  645. const cacheEntries = ResourceCache.cacheEntries;
  646. const cacheEntriesSorted = [];
  647. for (cacheKey in cacheEntries) {
  648. if (cacheEntries.hasOwnProperty(cacheKey)) {
  649. cacheEntriesSorted.push(cacheEntries[cacheKey]);
  650. }
  651. }
  652. cacheEntriesSorted.sort(function (a, b) {
  653. const indexA = precedence.indexOf(a.resourceLoader.constructor);
  654. const indexB = precedence.indexOf(b.resourceLoader.constructor);
  655. return indexA - indexB;
  656. });
  657. const cacheEntriesLength = cacheEntriesSorted.length;
  658. for (let i = 0; i < cacheEntriesLength; ++i) {
  659. const cacheEntry = cacheEntriesSorted[i];
  660. cacheKey = cacheEntry.resourceLoader.cacheKey;
  661. if (defined(cacheEntries[cacheKey])) {
  662. cacheEntry.resourceLoader.destroy();
  663. delete cacheEntries[cacheKey];
  664. }
  665. }
  666. ResourceCache.statistics.clear();
  667. };
  668. export default ResourceCache;