ResourceCacheKey.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  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 getAbsoluteUri from "../Core/getAbsoluteUri.js";
  6. import GltfLoaderUtil from "./GltfLoaderUtil.js";
  7. import hasExtension from "./hasExtension.js";
  8. /**
  9. * Compute cache keys for resources in {@link ResourceCache}.
  10. *
  11. * @namespace ResourceCacheKey
  12. *
  13. * @private
  14. */
  15. const ResourceCacheKey = {};
  16. function getExternalResourceCacheKey(resource) {
  17. return getAbsoluteUri(resource.url);
  18. }
  19. function getBufferViewCacheKey(bufferView) {
  20. let byteOffset = bufferView.byteOffset;
  21. let byteLength = bufferView.byteLength;
  22. if (hasExtension(bufferView, "EXT_meshopt_compression")) {
  23. const meshopt = bufferView.extensions.EXT_meshopt_compression;
  24. byteOffset = defaultValue(meshopt.byteOffset, 0);
  25. byteLength = meshopt.byteLength;
  26. }
  27. return `${byteOffset}-${byteOffset + byteLength}`;
  28. }
  29. function getAccessorCacheKey(accessor, bufferView) {
  30. const byteOffset = bufferView.byteOffset + accessor.byteOffset;
  31. const componentType = accessor.componentType;
  32. const type = accessor.type;
  33. const count = accessor.count;
  34. return `${byteOffset}-${componentType}-${type}-${count}`;
  35. }
  36. function getExternalBufferCacheKey(resource) {
  37. return getExternalResourceCacheKey(resource);
  38. }
  39. function getEmbeddedBufferCacheKey(parentResource, bufferId) {
  40. const parentCacheKey = getExternalResourceCacheKey(parentResource);
  41. return `${parentCacheKey}-buffer-id-${bufferId}`;
  42. }
  43. function getBufferCacheKey(buffer, bufferId, gltfResource, baseResource) {
  44. if (defined(buffer.uri)) {
  45. const resource = baseResource.getDerivedResource({
  46. url: buffer.uri,
  47. });
  48. return getExternalBufferCacheKey(resource);
  49. }
  50. return getEmbeddedBufferCacheKey(gltfResource, bufferId);
  51. }
  52. function getDracoCacheKey(gltf, draco, gltfResource, baseResource) {
  53. const bufferViewId = draco.bufferView;
  54. const bufferView = gltf.bufferViews[bufferViewId];
  55. const bufferId = bufferView.buffer;
  56. const buffer = gltf.buffers[bufferId];
  57. const bufferCacheKey = getBufferCacheKey(
  58. buffer,
  59. bufferId,
  60. gltfResource,
  61. baseResource
  62. );
  63. const bufferViewCacheKey = getBufferViewCacheKey(bufferView);
  64. return `${bufferCacheKey}-range-${bufferViewCacheKey}`;
  65. }
  66. function getImageCacheKey(gltf, imageId, gltfResource, baseResource) {
  67. const image = gltf.images[imageId];
  68. const bufferViewId = image.bufferView;
  69. const uri = image.uri;
  70. if (defined(uri)) {
  71. const resource = baseResource.getDerivedResource({
  72. url: uri,
  73. });
  74. return getExternalResourceCacheKey(resource);
  75. }
  76. const bufferView = gltf.bufferViews[bufferViewId];
  77. const bufferId = bufferView.buffer;
  78. const buffer = gltf.buffers[bufferId];
  79. const bufferCacheKey = getBufferCacheKey(
  80. buffer,
  81. bufferId,
  82. gltfResource,
  83. baseResource
  84. );
  85. const bufferViewCacheKey = getBufferViewCacheKey(bufferView);
  86. return `${bufferCacheKey}-range-${bufferViewCacheKey}`;
  87. }
  88. function getSamplerCacheKey(gltf, textureInfo) {
  89. const sampler = GltfLoaderUtil.createSampler({
  90. gltf: gltf,
  91. textureInfo: textureInfo,
  92. });
  93. return `${sampler.wrapS}-${sampler.wrapT}-${sampler.minificationFilter}-${sampler.magnificationFilter}`;
  94. }
  95. /**
  96. * Gets the schema cache key.
  97. *
  98. * @param {Object} options Object with the following properties:
  99. * @param {Object} [options.schema] An object that explicitly defines a schema JSON. Mutually exclusive with options.resource.
  100. * @param {Resource} [options.resource] The {@link Resource} pointing to the schema JSON. Mutually exclusive with options.schema.
  101. *
  102. * @returns {String} The schema cache key.
  103. *
  104. * @exception {DeveloperError} One of options.schema and options.resource must be defined.
  105. * @private
  106. */
  107. ResourceCacheKey.getSchemaCacheKey = function (options) {
  108. const schema = options.schema;
  109. const resource = options.resource;
  110. //>>includeStart('debug', pragmas.debug);
  111. if (defined(schema) === defined(resource)) {
  112. throw new DeveloperError(
  113. "One of options.schema and options.resource must be defined."
  114. );
  115. }
  116. //>>includeEnd('debug');
  117. if (defined(schema)) {
  118. return `embedded-schema:${JSON.stringify(schema)}`;
  119. }
  120. return `external-schema:${getExternalResourceCacheKey(resource)}`;
  121. };
  122. /**
  123. * Gets the external buffer cache key.
  124. *
  125. * @param {Object} options Object with the following properties:
  126. * @param {Resource} options.resource The {@link Resource} pointing to the external buffer.
  127. *
  128. * @returns {String} The external buffer cache key.
  129. * @private
  130. */
  131. ResourceCacheKey.getExternalBufferCacheKey = function (options) {
  132. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  133. const resource = options.resource;
  134. //>>includeStart('debug', pragmas.debug);
  135. Check.typeOf.object("options.resource", resource);
  136. //>>includeEnd('debug');
  137. return `external-buffer:${getExternalBufferCacheKey(resource)}`;
  138. };
  139. /**
  140. * Gets the embedded buffer cache key.
  141. *
  142. * @param {Object} options Object with the following properties:
  143. * @param {Resource} options.parentResource The {@link Resource} containing the embedded buffer.
  144. * @param {Number} options.bufferId A unique identifier of the embedded buffer within the parent resource.
  145. *
  146. * @returns {String} The embedded buffer cache key.
  147. * @private
  148. */
  149. ResourceCacheKey.getEmbeddedBufferCacheKey = function (options) {
  150. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  151. const parentResource = options.parentResource;
  152. const bufferId = options.bufferId;
  153. //>>includeStart('debug', pragmas.debug);
  154. Check.typeOf.object("options.parentResource", parentResource);
  155. Check.typeOf.number("options.bufferId", bufferId);
  156. //>>includeEnd('debug');
  157. return `embedded-buffer:${getEmbeddedBufferCacheKey(
  158. parentResource,
  159. bufferId
  160. )}`;
  161. };
  162. /**
  163. * Gets the glTF cache key.
  164. *
  165. * @param {Object} options Object with the following properties:
  166. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  167. *
  168. * @returns {String} The glTF cache key.
  169. * @private
  170. */
  171. ResourceCacheKey.getGltfCacheKey = function (options) {
  172. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  173. const gltfResource = options.gltfResource;
  174. //>>includeStart('debug', pragmas.debug);
  175. Check.typeOf.object("options.gltfResource", gltfResource);
  176. //>>includeEnd('debug');
  177. return `gltf:${getExternalResourceCacheKey(gltfResource)}`;
  178. };
  179. /**
  180. * Gets the buffer view cache key.
  181. *
  182. * @param {Object} options Object with the following properties:
  183. * @param {Object} options.gltf The glTF JSON.
  184. * @param {Number} options.bufferViewId The bufferView ID.
  185. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  186. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  187. *
  188. * @returns {String} The buffer view cache key.
  189. * @private
  190. */
  191. ResourceCacheKey.getBufferViewCacheKey = function (options) {
  192. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  193. const gltf = options.gltf;
  194. const bufferViewId = options.bufferViewId;
  195. const gltfResource = options.gltfResource;
  196. const baseResource = options.baseResource;
  197. //>>includeStart('debug', pragmas.debug);
  198. Check.typeOf.object("options.gltf", gltf);
  199. Check.typeOf.number("options.bufferViewId", bufferViewId);
  200. Check.typeOf.object("options.gltfResource", gltfResource);
  201. Check.typeOf.object("options.baseResource", baseResource);
  202. //>>includeEnd('debug');
  203. const bufferView = gltf.bufferViews[bufferViewId];
  204. let bufferId = bufferView.buffer;
  205. const buffer = gltf.buffers[bufferId];
  206. if (hasExtension(bufferView, "EXT_meshopt_compression")) {
  207. const meshopt = bufferView.extensions.EXT_meshopt_compression;
  208. bufferId = meshopt.buffer;
  209. }
  210. const bufferCacheKey = getBufferCacheKey(
  211. buffer,
  212. bufferId,
  213. gltfResource,
  214. baseResource
  215. );
  216. const bufferViewCacheKey = getBufferViewCacheKey(bufferView);
  217. return `buffer-view:${bufferCacheKey}-range-${bufferViewCacheKey}`;
  218. };
  219. /**
  220. * Gets the Draco cache key.
  221. *
  222. * @param {Object} options Object with the following properties:
  223. * @param {Object} options.gltf The glTF JSON.
  224. * @param {Object} options.draco The Draco extension object.
  225. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  226. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  227. *
  228. * @returns {String} The Draco cache key.
  229. * @private
  230. */
  231. ResourceCacheKey.getDracoCacheKey = function (options) {
  232. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  233. const gltf = options.gltf;
  234. const draco = options.draco;
  235. const gltfResource = options.gltfResource;
  236. const baseResource = options.baseResource;
  237. //>>includeStart('debug', pragmas.debug);
  238. Check.typeOf.object("options.gltf", gltf);
  239. Check.typeOf.object("options.draco", draco);
  240. Check.typeOf.object("options.gltfResource", gltfResource);
  241. Check.typeOf.object("options.baseResource", baseResource);
  242. //>>includeEnd('debug');
  243. return `draco:${getDracoCacheKey(gltf, draco, gltfResource, baseResource)}`;
  244. };
  245. /**
  246. * Gets the vertex buffer cache key.
  247. *
  248. * @param {Object} options Object with the following properties:
  249. * @param {Object} options.gltf The glTF JSON.
  250. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  251. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  252. * @param {Number} [options.bufferViewId] The bufferView ID corresponding to the vertex buffer.
  253. * @param {Object} [options.draco] The Draco extension object.
  254. * @param {String} [options.attributeSemantic] The attribute semantic, e.g. POSITION or NORMAL.
  255. * @param {Boolean} [dequantize=false] Determines whether or not the vertex buffer will be dequantized on the CPU.
  256. * @param {Boolean} [loadAsTypedArray=false] Load vertex buffer as a typed array instead of a GPU vertex buffer.
  257. *
  258. * @exception {DeveloperError} One of options.bufferViewId and options.draco must be defined.
  259. * @exception {DeveloperError} When options.draco is defined options.attributeSemantic must also be defined.
  260. *
  261. * @returns {String} The vertex buffer cache key.
  262. * @private
  263. */
  264. ResourceCacheKey.getVertexBufferCacheKey = function (options) {
  265. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  266. const gltf = options.gltf;
  267. const gltfResource = options.gltfResource;
  268. const baseResource = options.baseResource;
  269. const bufferViewId = options.bufferViewId;
  270. const draco = options.draco;
  271. const attributeSemantic = options.attributeSemantic;
  272. const dequantize = defaultValue(options.dequantize, false);
  273. const loadAsTypedArray = defaultValue(options.loadAsTypedArray, false);
  274. //>>includeStart('debug', pragmas.debug);
  275. Check.typeOf.object("options.gltf", gltf);
  276. Check.typeOf.object("options.gltfResource", gltfResource);
  277. Check.typeOf.object("options.baseResource", baseResource);
  278. const hasBufferViewId = defined(bufferViewId);
  279. const hasDraco = defined(draco);
  280. const hasAttributeSemantic = defined(attributeSemantic);
  281. if (hasBufferViewId === hasDraco) {
  282. throw new DeveloperError(
  283. "One of options.bufferViewId and options.draco must be defined."
  284. );
  285. }
  286. if (hasDraco && !hasAttributeSemantic) {
  287. throw new DeveloperError(
  288. "When options.draco is defined options.attributeSemantic must also be defined."
  289. );
  290. }
  291. if (hasDraco) {
  292. Check.typeOf.object("options.draco", draco);
  293. Check.typeOf.string("options.attributeSemantic", attributeSemantic);
  294. }
  295. //>>includeEnd('debug');
  296. let cacheKeySuffix = "";
  297. if (dequantize) {
  298. cacheKeySuffix += "-dequantize";
  299. }
  300. if (loadAsTypedArray) {
  301. cacheKeySuffix += "-typed-array";
  302. }
  303. if (defined(draco)) {
  304. const dracoCacheKey = getDracoCacheKey(
  305. gltf,
  306. draco,
  307. gltfResource,
  308. baseResource
  309. );
  310. return `vertex-buffer:${dracoCacheKey}-draco-${attributeSemantic}${cacheKeySuffix}`;
  311. }
  312. const bufferView = gltf.bufferViews[bufferViewId];
  313. const bufferId = bufferView.buffer;
  314. const buffer = gltf.buffers[bufferId];
  315. const bufferCacheKey = getBufferCacheKey(
  316. buffer,
  317. bufferId,
  318. gltfResource,
  319. baseResource
  320. );
  321. const bufferViewCacheKey = getBufferViewCacheKey(bufferView);
  322. return `vertex-buffer:${bufferCacheKey}-range-${bufferViewCacheKey}${cacheKeySuffix}`;
  323. };
  324. /**
  325. * Gets the index buffer cache key.
  326. *
  327. * @param {Object} options Object with the following properties:
  328. * @param {Object} options.gltf The glTF JSON.
  329. * @param {Number} options.accessorId The accessor ID corresponding to the index buffer.
  330. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  331. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  332. * @param {Object} [options.draco] The Draco extension object.
  333. * @param {Boolean} [loadAsTypedArray=false] Load index buffer as a typed array instead of a GPU index buffer.
  334. *
  335. * @returns {String} The index buffer cache key.
  336. * @private
  337. */
  338. ResourceCacheKey.getIndexBufferCacheKey = function (options) {
  339. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  340. const gltf = options.gltf;
  341. const accessorId = options.accessorId;
  342. const gltfResource = options.gltfResource;
  343. const baseResource = options.baseResource;
  344. const draco = options.draco;
  345. const loadAsTypedArray = defaultValue(options.loadAsTypedArray, false);
  346. //>>includeStart('debug', pragmas.debug);
  347. Check.typeOf.object("options.gltf", gltf);
  348. Check.typeOf.number("options.accessorId", accessorId);
  349. Check.typeOf.object("options.gltfResource", gltfResource);
  350. Check.typeOf.object("options.baseResource", baseResource);
  351. //>>includeEnd('debug');
  352. let cacheKeySuffix = "";
  353. if (loadAsTypedArray) {
  354. cacheKeySuffix += "-typed-array";
  355. }
  356. if (defined(draco)) {
  357. const dracoCacheKey = getDracoCacheKey(
  358. gltf,
  359. draco,
  360. gltfResource,
  361. baseResource
  362. );
  363. return `index-buffer:${dracoCacheKey}-draco${cacheKeySuffix}`;
  364. }
  365. const accessor = gltf.accessors[accessorId];
  366. const bufferViewId = accessor.bufferView;
  367. const bufferView = gltf.bufferViews[bufferViewId];
  368. const bufferId = bufferView.buffer;
  369. const buffer = gltf.buffers[bufferId];
  370. const bufferCacheKey = getBufferCacheKey(
  371. buffer,
  372. bufferId,
  373. gltfResource,
  374. baseResource
  375. );
  376. const accessorCacheKey = getAccessorCacheKey(accessor, bufferView);
  377. return `index-buffer:${bufferCacheKey}-accessor-${accessorCacheKey}${cacheKeySuffix}`;
  378. };
  379. /**
  380. * Gets the image cache key.
  381. *
  382. * @param {Object} options Object with the following properties:
  383. * @param {Object} options.gltf The glTF JSON.
  384. * @param {Number} options.imageId The image ID.
  385. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  386. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  387. *
  388. * @returns {String} The image cache key.
  389. * @private
  390. */
  391. ResourceCacheKey.getImageCacheKey = function (options) {
  392. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  393. const gltf = options.gltf;
  394. const imageId = options.imageId;
  395. const gltfResource = options.gltfResource;
  396. const baseResource = options.baseResource;
  397. //>>includeStart('debug', pragmas.debug);
  398. Check.typeOf.object("options.gltf", gltf);
  399. Check.typeOf.number("options.imageId", imageId);
  400. Check.typeOf.object("options.gltfResource", gltfResource);
  401. Check.typeOf.object("options.baseResource", baseResource);
  402. //>>includeEnd('debug');
  403. const imageCacheKey = getImageCacheKey(
  404. gltf,
  405. imageId,
  406. gltfResource,
  407. baseResource
  408. );
  409. return `image:${imageCacheKey}`;
  410. };
  411. /**
  412. * Gets the texture cache key.
  413. *
  414. * @param {Object} options Object with the following properties:
  415. * @param {Object} options.gltf The glTF JSON.
  416. * @param {Object} options.textureInfo The texture info object.
  417. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  418. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  419. * @param {SupportedImageFormats} options.supportedImageFormats The supported image formats.
  420. *
  421. * @returns {String} The texture cache key.
  422. * @private
  423. */
  424. ResourceCacheKey.getTextureCacheKey = function (options) {
  425. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  426. const gltf = options.gltf;
  427. const textureInfo = options.textureInfo;
  428. const gltfResource = options.gltfResource;
  429. const baseResource = options.baseResource;
  430. const supportedImageFormats = options.supportedImageFormats;
  431. //>>includeStart('debug', pragmas.debug);
  432. Check.typeOf.object("options.gltf", gltf);
  433. Check.typeOf.object("options.textureInfo", textureInfo);
  434. Check.typeOf.object("options.gltfResource", gltfResource);
  435. Check.typeOf.object("options.baseResource", baseResource);
  436. Check.typeOf.object("options.supportedImageFormats", supportedImageFormats);
  437. //>>includeEnd('debug');
  438. const textureId = textureInfo.index;
  439. const imageId = GltfLoaderUtil.getImageIdFromTexture({
  440. gltf: gltf,
  441. textureId: textureId,
  442. supportedImageFormats: supportedImageFormats,
  443. });
  444. const imageCacheKey = getImageCacheKey(
  445. gltf,
  446. imageId,
  447. gltfResource,
  448. baseResource
  449. );
  450. // Include the sampler cache key in the texture cache key since textures and
  451. // samplers are coupled in WebGL 1. When upgrading to WebGL 2 consider
  452. // removing the sampleCacheKey here.
  453. const samplerCacheKey = getSamplerCacheKey(gltf, textureInfo);
  454. return `texture:${imageCacheKey}-sampler-${samplerCacheKey}`;
  455. };
  456. export default ResourceCacheKey;