GltfStructuralMetadataLoader.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  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 parseStructuralMetadata from "./parseStructuralMetadata.js";
  6. import parseFeatureMetadataLegacy from "./parseFeatureMetadataLegacy.js";
  7. import ResourceCache from "./ResourceCache.js";
  8. import ResourceLoader from "./ResourceLoader.js";
  9. import ResourceLoaderState from "./ResourceLoaderState.js";
  10. /**
  11. * Loads glTF structural metadata
  12. * <p>
  13. * Implements the {@link ResourceLoader} interface.
  14. * </p>
  15. *
  16. * @alias GltfStructuralMetadataLoader
  17. * @constructor
  18. * @augments ResourceLoader
  19. *
  20. * @param {object} options Object with the following properties:
  21. * @param {object} options.gltf The glTF JSON.
  22. * @param {string} [options.extension] The <code>EXT_structural_metadata</code> extension object. If this is undefined, then extensionLegacy must be defined.
  23. * @param {string} [options.extensionLegacy] The legacy <code>EXT_feature_metadata</code> extension for backwards compatibility.
  24. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  25. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  26. * @param {SupportedImageFormats} options.supportedImageFormats The supported image formats.
  27. * @param {FrameState} options.frameState The frame state.
  28. * @param {string} [options.cacheKey] The cache key of the resource.
  29. * @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.
  30. *
  31. * @private
  32. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  33. */
  34. function GltfStructuralMetadataLoader(options) {
  35. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  36. const gltf = options.gltf;
  37. const extension = options.extension;
  38. const extensionLegacy = options.extensionLegacy;
  39. const gltfResource = options.gltfResource;
  40. const baseResource = options.baseResource;
  41. const supportedImageFormats = options.supportedImageFormats;
  42. const frameState = options.frameState;
  43. const cacheKey = options.cacheKey;
  44. const asynchronous = defaultValue(options.asynchronous, true);
  45. //>>includeStart('debug', pragmas.debug);
  46. Check.typeOf.object("options.gltf", gltf);
  47. Check.typeOf.object("options.gltfResource", gltfResource);
  48. Check.typeOf.object("options.baseResource", baseResource);
  49. Check.typeOf.object("options.supportedImageFormats", supportedImageFormats);
  50. Check.typeOf.object("options.frameState", frameState);
  51. if (!defined(options.extension) && !defined(options.extensionLegacy)) {
  52. throw new DeveloperError(
  53. "One of options.extension or options.extensionLegacy must be specified"
  54. );
  55. }
  56. //>>includeEnd('debug');
  57. this._gltfResource = gltfResource;
  58. this._baseResource = baseResource;
  59. this._gltf = gltf;
  60. this._extension = extension;
  61. this._extensionLegacy = extensionLegacy;
  62. this._supportedImageFormats = supportedImageFormats;
  63. this._frameState = frameState;
  64. this._cacheKey = cacheKey;
  65. this._asynchronous = asynchronous;
  66. this._bufferViewLoaders = [];
  67. this._bufferViewIds = [];
  68. this._textureLoaders = [];
  69. this._textureIds = [];
  70. this._schemaLoader = undefined;
  71. this._structuralMetadata = undefined;
  72. this._state = ResourceLoaderState.UNLOADED;
  73. this._promise = undefined;
  74. }
  75. if (defined(Object.create)) {
  76. GltfStructuralMetadataLoader.prototype = Object.create(
  77. ResourceLoader.prototype
  78. );
  79. GltfStructuralMetadataLoader.prototype.constructor = GltfStructuralMetadataLoader;
  80. }
  81. Object.defineProperties(GltfStructuralMetadataLoader.prototype, {
  82. /**
  83. * The cache key of the resource.
  84. *
  85. * @memberof GltfStructuralMetadataLoader.prototype
  86. *
  87. * @type {string}
  88. * @readonly
  89. * @private
  90. */
  91. cacheKey: {
  92. get: function () {
  93. return this._cacheKey;
  94. },
  95. },
  96. /**
  97. * The parsed structural metadata
  98. *
  99. * @memberof GltfStructuralMetadataLoader.prototype
  100. *
  101. * @type {StructuralMetadata}
  102. * @readonly
  103. * @private
  104. */
  105. structuralMetadata: {
  106. get: function () {
  107. return this._structuralMetadata;
  108. },
  109. },
  110. });
  111. async function loadResources(loader) {
  112. try {
  113. const bufferViewsPromise = loadBufferViews(loader);
  114. const texturesPromise = loadTextures(loader);
  115. const schemaPromise = loadSchema(loader);
  116. await Promise.all([bufferViewsPromise, texturesPromise, schemaPromise]);
  117. if (loader.isDestroyed()) {
  118. return;
  119. }
  120. loader._gltf = undefined; // No longer need to hold onto the glTF
  121. loader._state = ResourceLoaderState.LOADED;
  122. return loader;
  123. } catch (error) {
  124. if (loader.isDestroyed()) {
  125. return;
  126. }
  127. loader.unload();
  128. loader._state = ResourceLoaderState.FAILED;
  129. const errorMessage = "Failed to load structural metadata";
  130. throw loader.getError(errorMessage, error);
  131. }
  132. }
  133. /**
  134. * Loads the resource.
  135. * @returns {Promise<GltfStructuralMetadataLoader>} A promise which resolves to the loader when the resource loading is completed.
  136. * @private
  137. */
  138. GltfStructuralMetadataLoader.prototype.load = function () {
  139. if (defined(this._promise)) {
  140. return this._promise;
  141. }
  142. this._state = ResourceLoaderState.LOADING;
  143. this._promise = loadResources(this);
  144. return this._promise;
  145. };
  146. function gatherBufferViewIdsFromProperties(properties, bufferViewIdSet) {
  147. for (const propertyId in properties) {
  148. if (properties.hasOwnProperty(propertyId)) {
  149. const property = properties[propertyId];
  150. const values = property.values;
  151. const arrayOffsets = property.arrayOffsets;
  152. const stringOffsets = property.stringOffsets;
  153. // Using an object like a mathematical set
  154. if (defined(values)) {
  155. bufferViewIdSet[values] = true;
  156. }
  157. if (defined(arrayOffsets)) {
  158. bufferViewIdSet[arrayOffsets] = true;
  159. }
  160. if (defined(stringOffsets)) {
  161. bufferViewIdSet[stringOffsets] = true;
  162. }
  163. }
  164. }
  165. }
  166. function gatherBufferViewIdsFromPropertiesLegacy(properties, bufferViewIdSet) {
  167. for (const propertyId in properties) {
  168. if (properties.hasOwnProperty(propertyId)) {
  169. const property = properties[propertyId];
  170. const bufferView = property.bufferView;
  171. const arrayOffsetBufferView = property.arrayOffsetBufferView;
  172. const stringOffsetBufferView = property.stringOffsetBufferView;
  173. // Using an object like a mathematical set
  174. if (defined(bufferView)) {
  175. bufferViewIdSet[bufferView] = true;
  176. }
  177. if (defined(arrayOffsetBufferView)) {
  178. bufferViewIdSet[arrayOffsetBufferView] = true;
  179. }
  180. if (defined(stringOffsetBufferView)) {
  181. bufferViewIdSet[stringOffsetBufferView] = true;
  182. }
  183. }
  184. }
  185. }
  186. function gatherUsedBufferViewIds(extension) {
  187. const propertyTables = extension.propertyTables;
  188. const bufferViewIdSet = {};
  189. if (defined(propertyTables)) {
  190. for (let i = 0; i < propertyTables.length; i++) {
  191. const propertyTable = propertyTables[i];
  192. gatherBufferViewIdsFromProperties(
  193. propertyTable.properties,
  194. bufferViewIdSet
  195. );
  196. }
  197. }
  198. return bufferViewIdSet;
  199. }
  200. function gatherUsedBufferViewIdsLegacy(extensionLegacy) {
  201. const featureTables = extensionLegacy.featureTables;
  202. const bufferViewIdSet = {};
  203. if (defined(featureTables)) {
  204. for (const featureTableId in featureTables) {
  205. if (featureTables.hasOwnProperty(featureTableId)) {
  206. const featureTable = featureTables[featureTableId];
  207. const properties = featureTable.properties;
  208. if (defined(properties)) {
  209. gatherBufferViewIdsFromPropertiesLegacy(properties, bufferViewIdSet);
  210. }
  211. }
  212. }
  213. }
  214. return bufferViewIdSet;
  215. }
  216. async function loadBufferViews(structuralMetadataLoader) {
  217. let bufferViewIds;
  218. if (defined(structuralMetadataLoader._extension)) {
  219. bufferViewIds = gatherUsedBufferViewIds(
  220. structuralMetadataLoader._extension
  221. );
  222. } else {
  223. bufferViewIds = gatherUsedBufferViewIdsLegacy(
  224. structuralMetadataLoader._extensionLegacy
  225. );
  226. }
  227. // Load the buffer views
  228. const bufferViewPromises = [];
  229. for (const bufferViewId in bufferViewIds) {
  230. if (bufferViewIds.hasOwnProperty(bufferViewId)) {
  231. const bufferViewLoader = ResourceCache.getBufferViewLoader({
  232. gltf: structuralMetadataLoader._gltf,
  233. bufferViewId: parseInt(bufferViewId),
  234. gltfResource: structuralMetadataLoader._gltfResource,
  235. baseResource: structuralMetadataLoader._baseResource,
  236. });
  237. structuralMetadataLoader._bufferViewLoaders.push(bufferViewLoader);
  238. structuralMetadataLoader._bufferViewIds.push(bufferViewId);
  239. bufferViewPromises.push(bufferViewLoader.load());
  240. }
  241. }
  242. return Promise.all(bufferViewPromises);
  243. }
  244. function gatherUsedTextureIds(structuralMetadataExtension) {
  245. // Gather the used textures
  246. const textureIds = {};
  247. const propertyTextures = structuralMetadataExtension.propertyTextures;
  248. if (defined(propertyTextures)) {
  249. for (let i = 0; i < propertyTextures.length; i++) {
  250. const propertyTexture = propertyTextures[i];
  251. const properties = propertyTexture.properties;
  252. if (defined(properties)) {
  253. gatherTextureIdsFromProperties(properties, textureIds);
  254. }
  255. }
  256. }
  257. return textureIds;
  258. }
  259. function gatherTextureIdsFromProperties(properties, textureIds) {
  260. for (const propertyId in properties) {
  261. if (properties.hasOwnProperty(propertyId)) {
  262. // in EXT_structural_metadata the property is a valid textureInfo.
  263. const textureInfo = properties[propertyId];
  264. textureIds[textureInfo.index] = textureInfo;
  265. }
  266. }
  267. }
  268. function gatherUsedTextureIdsLegacy(extensionLegacy) {
  269. // Gather the used textures
  270. const textureIds = {};
  271. const featureTextures = extensionLegacy.featureTextures;
  272. if (defined(featureTextures)) {
  273. for (const featureTextureId in featureTextures) {
  274. if (featureTextures.hasOwnProperty(featureTextureId)) {
  275. const featureTexture = featureTextures[featureTextureId];
  276. const properties = featureTexture.properties;
  277. if (defined(properties)) {
  278. gatherTextureIdsFromPropertiesLegacy(properties, textureIds);
  279. }
  280. }
  281. }
  282. }
  283. return textureIds;
  284. }
  285. function gatherTextureIdsFromPropertiesLegacy(properties, textureIds) {
  286. for (const propertyId in properties) {
  287. if (properties.hasOwnProperty(propertyId)) {
  288. const property = properties[propertyId];
  289. const textureInfo = property.texture;
  290. textureIds[textureInfo.index] = textureInfo;
  291. }
  292. }
  293. }
  294. function loadTextures(structuralMetadataLoader) {
  295. let textureIds;
  296. if (defined(structuralMetadataLoader._extension)) {
  297. textureIds = gatherUsedTextureIds(structuralMetadataLoader._extension);
  298. } else {
  299. textureIds = gatherUsedTextureIdsLegacy(
  300. structuralMetadataLoader._extensionLegacy
  301. );
  302. }
  303. const gltf = structuralMetadataLoader._gltf;
  304. const gltfResource = structuralMetadataLoader._gltfResource;
  305. const baseResource = structuralMetadataLoader._baseResource;
  306. const supportedImageFormats = structuralMetadataLoader._supportedImageFormats;
  307. const frameState = structuralMetadataLoader._frameState;
  308. const asynchronous = structuralMetadataLoader._asynchronous;
  309. // Load the textures
  310. const texturePromises = [];
  311. for (const textureId in textureIds) {
  312. if (textureIds.hasOwnProperty(textureId)) {
  313. const textureLoader = ResourceCache.getTextureLoader({
  314. gltf: gltf,
  315. textureInfo: textureIds[textureId],
  316. gltfResource: gltfResource,
  317. baseResource: baseResource,
  318. supportedImageFormats: supportedImageFormats,
  319. frameState: frameState,
  320. asynchronous: asynchronous,
  321. });
  322. structuralMetadataLoader._textureLoaders.push(textureLoader);
  323. structuralMetadataLoader._textureIds.push(textureId);
  324. texturePromises.push(textureLoader.load());
  325. }
  326. }
  327. return Promise.all(texturePromises);
  328. }
  329. async function loadSchema(structuralMetadataLoader) {
  330. const extension = defaultValue(
  331. structuralMetadataLoader._extension,
  332. structuralMetadataLoader._extensionLegacy
  333. );
  334. let schemaLoader;
  335. if (defined(extension.schemaUri)) {
  336. const resource = structuralMetadataLoader._baseResource.getDerivedResource({
  337. url: extension.schemaUri,
  338. });
  339. schemaLoader = ResourceCache.getSchemaLoader({
  340. resource: resource,
  341. });
  342. } else {
  343. schemaLoader = ResourceCache.getSchemaLoader({
  344. schema: extension.schema,
  345. });
  346. }
  347. structuralMetadataLoader._schemaLoader = schemaLoader;
  348. await schemaLoader.load();
  349. if (!schemaLoader.isDestroyed()) {
  350. return schemaLoader.schema;
  351. }
  352. }
  353. /**
  354. * Processes the resource until it becomes ready.
  355. *
  356. * @param {FrameState} frameState The frame state.
  357. * @private
  358. */
  359. GltfStructuralMetadataLoader.prototype.process = function (frameState) {
  360. //>>includeStart('debug', pragmas.debug);
  361. Check.typeOf.object("frameState", frameState);
  362. //>>includeEnd('debug');
  363. if (this._state === ResourceLoaderState.READY) {
  364. return true;
  365. }
  366. if (this._state !== ResourceLoaderState.LOADED) {
  367. return false;
  368. }
  369. const textureLoaders = this._textureLoaders;
  370. const textureLoadersLength = textureLoaders.length;
  371. let ready = true;
  372. for (let i = 0; i < textureLoadersLength; ++i) {
  373. const textureLoader = textureLoaders[i];
  374. const textureReady = textureLoader.process(frameState);
  375. ready = ready && textureReady;
  376. }
  377. if (!ready) {
  378. return false;
  379. }
  380. const schema = this._schemaLoader.schema;
  381. const bufferViews = {};
  382. for (let i = 0; i < this._bufferViewIds.length; ++i) {
  383. const bufferViewId = this._bufferViewIds[i];
  384. const bufferViewLoader = this._bufferViewLoaders[i];
  385. if (!bufferViewLoader.isDestroyed()) {
  386. // Copy the typed array and let the underlying ArrayBuffer be freed
  387. const bufferViewTypedArray = new Uint8Array(bufferViewLoader.typedArray);
  388. bufferViews[bufferViewId] = bufferViewTypedArray;
  389. }
  390. }
  391. const textures = {};
  392. for (let i = 0; i < this._textureIds.length; ++i) {
  393. const textureId = this._textureIds[i];
  394. const textureLoader = textureLoaders[i];
  395. if (!textureLoader.isDestroyed()) {
  396. textures[textureId] = textureLoader.texture;
  397. }
  398. }
  399. if (defined(this._extension)) {
  400. this._structuralMetadata = parseStructuralMetadata({
  401. extension: this._extension,
  402. schema: schema,
  403. bufferViews: bufferViews,
  404. textures: textures,
  405. });
  406. } else {
  407. this._structuralMetadata = parseFeatureMetadataLegacy({
  408. extension: this._extensionLegacy,
  409. schema: schema,
  410. bufferViews: bufferViews,
  411. textures: textures,
  412. });
  413. }
  414. // Buffer views can be unloaded after the data has been copied
  415. unloadBufferViews(this);
  416. this._state = ResourceLoaderState.READY;
  417. return true;
  418. };
  419. function unloadBufferViews(structuralMetadataLoader) {
  420. const bufferViewLoaders = structuralMetadataLoader._bufferViewLoaders;
  421. const bufferViewLoadersLength = bufferViewLoaders.length;
  422. for (let i = 0; i < bufferViewLoadersLength; ++i) {
  423. ResourceCache.unload(bufferViewLoaders[i]);
  424. }
  425. structuralMetadataLoader._bufferViewLoaders.length = 0;
  426. structuralMetadataLoader._bufferViewIds.length = 0;
  427. }
  428. function unloadTextures(structuralMetadataLoader) {
  429. const textureLoaders = structuralMetadataLoader._textureLoaders;
  430. const textureLoadersLength = textureLoaders.length;
  431. for (let i = 0; i < textureLoadersLength; ++i) {
  432. ResourceCache.unload(textureLoaders[i]);
  433. }
  434. structuralMetadataLoader._textureLoaders.length = 0;
  435. structuralMetadataLoader._textureIds.length = 0;
  436. }
  437. /**
  438. * Unloads the resource.
  439. * @private
  440. */
  441. GltfStructuralMetadataLoader.prototype.unload = function () {
  442. unloadBufferViews(this);
  443. unloadTextures(this);
  444. if (defined(this._schemaLoader)) {
  445. ResourceCache.unload(this._schemaLoader);
  446. }
  447. this._schemaLoader = undefined;
  448. this._structuralMetadata = undefined;
  449. };
  450. export default GltfStructuralMetadataLoader;