ResourceCache.js 23 KB

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