智慧水务管理系统 - 精河县供水工程综合管理平台

GltfJsonLoader.js 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import Check from "../Core/Check.js";
  2. import Frozen from "../Core/Frozen.js";
  3. import defined from "../Core/defined.js";
  4. import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js";
  5. import getMagic from "../Core/getMagic.js";
  6. import isDataUri from "../Core/isDataUri.js";
  7. import Resource from "../Core/Resource.js";
  8. import RuntimeError from "../Core/RuntimeError.js";
  9. import addDefaults from "./GltfPipeline/addDefaults.js";
  10. import addPipelineExtras from "./GltfPipeline/addPipelineExtras.js";
  11. import ForEach from "./GltfPipeline/ForEach.js";
  12. import parseGlb from "./GltfPipeline/parseGlb.js";
  13. import removePipelineExtras from "./GltfPipeline/removePipelineExtras.js";
  14. import updateVersion from "./GltfPipeline/updateVersion.js";
  15. import usesExtension from "./GltfPipeline/usesExtension.js";
  16. import ResourceLoader from "./ResourceLoader.js";
  17. import ResourceLoaderState from "./ResourceLoaderState.js";
  18. import ModelUtility from "./Model/ModelUtility.js";
  19. /**
  20. * Loads a glTF JSON from a glTF or glb.
  21. * <p>
  22. * Implements the {@link ResourceLoader} interface.
  23. * </p>
  24. *
  25. * @private
  26. */
  27. class GltfJsonLoader extends ResourceLoader {
  28. /**
  29. * @param {object} options Object with the following properties:
  30. * @param {ResourceCache} options.resourceCache The {@link ResourceCache} (to avoid circular dependencies).
  31. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  32. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  33. * @param {Uint8Array} [options.typedArray] The typed array containing the glTF contents.
  34. * @param {object} [options.gltfJson] The parsed glTF JSON contents.
  35. * @param {string} [options.cacheKey] The cache key of the resource.
  36. */
  37. constructor(options) {
  38. super();
  39. options = options ?? Frozen.EMPTY_OBJECT;
  40. const resourceCache = options.resourceCache;
  41. const gltfResource = options.gltfResource;
  42. const baseResource = options.baseResource;
  43. const typedArray = options.typedArray;
  44. const gltfJson = options.gltfJson;
  45. const cacheKey = options.cacheKey;
  46. //>>includeStart('debug', pragmas.debug);
  47. Check.typeOf.func("options.resourceCache", resourceCache);
  48. Check.typeOf.object("options.gltfResource", gltfResource);
  49. Check.typeOf.object("options.baseResource", baseResource);
  50. //>>includeEnd('debug');
  51. this._resourceCache = resourceCache;
  52. this._gltfResource = gltfResource;
  53. this._baseResource = baseResource;
  54. this._typedArray = typedArray;
  55. this._gltfJson = gltfJson;
  56. this._cacheKey = cacheKey;
  57. this._gltf = undefined;
  58. this._bufferLoaders = [];
  59. this._state = ResourceLoaderState.UNLOADED;
  60. this._promise = undefined;
  61. }
  62. /**
  63. * The cache key of the resource.
  64. *
  65. *
  66. * @type {string}
  67. * @readonly
  68. * @private
  69. */
  70. get cacheKey() {
  71. return this._cacheKey;
  72. }
  73. /**
  74. * The glTF JSON.
  75. *
  76. *
  77. * @type {object}
  78. * @readonly
  79. * @private
  80. */
  81. get gltf() {
  82. return this._gltf;
  83. }
  84. /**
  85. * Loads the resource.
  86. * @returns {Promise<GltfJsonLoader>} A promise which resolves to the loader when the resource loading is completed.
  87. * @private
  88. */
  89. async load() {
  90. if (defined(this._promise)) {
  91. return this._promise;
  92. }
  93. this._state = ResourceLoaderState.LOADING;
  94. if (defined(this._gltfJson)) {
  95. this._promise = processGltfJson(this, this._gltfJson);
  96. return this._promise;
  97. }
  98. if (defined(this._typedArray)) {
  99. this._promise = processGltfTypedArray(this, this._typedArray);
  100. return this._promise;
  101. }
  102. this._promise = loadFromUri(this);
  103. return this._promise;
  104. }
  105. /**
  106. * Unloads the resource.
  107. * @private
  108. */
  109. unload() {
  110. const bufferLoaders = this._bufferLoaders;
  111. const bufferLoadersLength = bufferLoaders.length;
  112. for (let i = 0; i < bufferLoadersLength; ++i) {
  113. bufferLoaders[i] =
  114. !bufferLoaders[i].isDestroyed() &&
  115. this._resourceCache.unload(bufferLoaders[i]);
  116. }
  117. this._bufferLoaders.length = 0;
  118. this._gltf = undefined;
  119. }
  120. /**
  121. * Exposed for testing
  122. *
  123. * @private
  124. */
  125. _fetchGltf() {
  126. return this._gltfResource.fetchArrayBuffer();
  127. }
  128. }
  129. async function loadFromUri(gltfJsonLoader) {
  130. let typedArray;
  131. try {
  132. const arrayBuffer = await gltfJsonLoader._fetchGltf();
  133. if (gltfJsonLoader.isDestroyed()) {
  134. return;
  135. }
  136. typedArray = new Uint8Array(arrayBuffer);
  137. } catch (error) {
  138. if (gltfJsonLoader.isDestroyed()) {
  139. return;
  140. }
  141. handleError(gltfJsonLoader, error);
  142. }
  143. return processGltfTypedArray(gltfJsonLoader, typedArray);
  144. }
  145. function handleError(gltfJsonLoader, error) {
  146. gltfJsonLoader.unload();
  147. gltfJsonLoader._state = ResourceLoaderState.FAILED;
  148. const errorMessage = `Failed to load glTF: ${gltfJsonLoader._gltfResource.url}`;
  149. throw gltfJsonLoader.getError(errorMessage, error);
  150. }
  151. async function upgradeVersion(gltfJsonLoader, gltf) {
  152. if (
  153. defined(gltf.asset) &&
  154. gltf.asset.version === "2.0" &&
  155. !usesExtension(gltf, "KHR_techniques_webgl") &&
  156. !usesExtension(gltf, "KHR_materials_common")
  157. ) {
  158. return Promise.resolve();
  159. }
  160. // Load all buffers into memory. updateVersion will read and in some cases modify
  161. // the buffer data, which it accesses from buffer.extras._pipeline.source
  162. const promises = [];
  163. ForEach.buffer(gltf, function (buffer) {
  164. if (
  165. !defined(buffer.extras._pipeline.source) && // Ignore uri if this buffer uses the glTF 1.0 KHR_binary_glTF extension
  166. defined(buffer.uri)
  167. ) {
  168. const resource = gltfJsonLoader._baseResource.getDerivedResource({
  169. url: buffer.uri,
  170. });
  171. const resourceCache = gltfJsonLoader._resourceCache;
  172. const bufferLoader = resourceCache.getExternalBufferLoader({
  173. resource: resource,
  174. });
  175. gltfJsonLoader._bufferLoaders.push(bufferLoader);
  176. promises.push(
  177. bufferLoader.load().then(function () {
  178. if (bufferLoader.isDestroyed()) {
  179. return;
  180. }
  181. buffer.extras._pipeline.source = bufferLoader.typedArray;
  182. }),
  183. );
  184. }
  185. });
  186. await Promise.all(promises);
  187. updateVersion(gltf);
  188. }
  189. function decodeDataUris(gltf) {
  190. const promises = [];
  191. ForEach.buffer(gltf, function (buffer) {
  192. const bufferUri = buffer.uri;
  193. if (
  194. !defined(buffer.extras._pipeline.source) && // Ignore uri if this buffer uses the glTF 1.0 KHR_binary_glTF extension
  195. defined(bufferUri) &&
  196. isDataUri(bufferUri)
  197. ) {
  198. delete buffer.uri; // Delete the data URI to keep the cached glTF JSON small
  199. promises.push(
  200. Resource.fetchArrayBuffer(bufferUri).then(function (arrayBuffer) {
  201. buffer.extras._pipeline.source = new Uint8Array(arrayBuffer);
  202. }),
  203. );
  204. }
  205. });
  206. return Promise.all(promises);
  207. }
  208. function loadEmbeddedBuffers(gltfJsonLoader, gltf) {
  209. const promises = [];
  210. ForEach.buffer(gltf, function (buffer, bufferId) {
  211. const source = buffer.extras._pipeline.source;
  212. if (defined(source) && !defined(buffer.uri)) {
  213. const resourceCache = gltfJsonLoader._resourceCache;
  214. const bufferLoader = resourceCache.getEmbeddedBufferLoader({
  215. parentResource: gltfJsonLoader._gltfResource,
  216. bufferId: bufferId,
  217. typedArray: source,
  218. });
  219. gltfJsonLoader._bufferLoaders.push(bufferLoader);
  220. promises.push(bufferLoader.load());
  221. }
  222. });
  223. return Promise.all(promises);
  224. }
  225. async function processGltfJson(gltfJsonLoader, gltf) {
  226. try {
  227. addPipelineExtras(gltf);
  228. await decodeDataUris(gltf);
  229. await upgradeVersion(gltfJsonLoader, gltf);
  230. addDefaults(gltf);
  231. await loadEmbeddedBuffers(gltfJsonLoader, gltf);
  232. removePipelineExtras(gltf);
  233. const version = gltf.asset.version;
  234. if (version !== "1.0" && version !== "2.0") {
  235. throw new RuntimeError(`Unsupported glTF version: ${version}`);
  236. }
  237. const extensionsRequired = gltf.extensionsRequired;
  238. if (defined(extensionsRequired)) {
  239. ModelUtility.checkSupportedExtensions(extensionsRequired);
  240. }
  241. gltfJsonLoader._gltf = gltf;
  242. gltfJsonLoader._state = ResourceLoaderState.READY;
  243. return gltfJsonLoader;
  244. } catch (error) {
  245. if (gltfJsonLoader.isDestroyed()) {
  246. return;
  247. }
  248. handleError(gltfJsonLoader, error);
  249. }
  250. }
  251. async function processGltfTypedArray(gltfJsonLoader, typedArray) {
  252. let gltf;
  253. try {
  254. if (getMagic(typedArray) === "glTF") {
  255. gltf = parseGlb(typedArray);
  256. } else {
  257. gltf = getJsonFromTypedArray(typedArray);
  258. }
  259. } catch (error) {
  260. if (gltfJsonLoader.isDestroyed()) {
  261. return;
  262. }
  263. handleError(gltfJsonLoader, error);
  264. }
  265. return processGltfJson(gltfJsonLoader, gltf);
  266. }
  267. export default GltfJsonLoader;