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

GltfTextureLoader.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. import Check from "../Core/Check.js";
  2. import CesiumMath from "../Core/Math.js";
  3. import Frozen from "../Core/Frozen.js";
  4. import defined from "../Core/defined.js";
  5. import PixelFormat from "../Core/PixelFormat.js";
  6. import Texture from "../Renderer/Texture.js";
  7. import TextureMinificationFilter from "../Renderer/TextureMinificationFilter.js";
  8. import TextureWrap from "../Renderer/TextureWrap.js";
  9. import GltfLoaderUtil from "./GltfLoaderUtil.js";
  10. import JobType from "./JobType.js";
  11. import ResourceLoader from "./ResourceLoader.js";
  12. import ResourceLoaderState from "./ResourceLoaderState.js";
  13. import resizeImageToNextPowerOfTwo from "../Core/resizeImageToNextPowerOfTwo.js";
  14. /**
  15. * Loads a glTF texture.
  16. * <p>
  17. * Implements the {@link ResourceLoader} interface.
  18. * </p>
  19. *
  20. * @private
  21. */
  22. class GltfTextureLoader extends ResourceLoader {
  23. /**
  24. * @param {object} options Object with the following properties:
  25. * @param {ResourceCache} options.resourceCache The {@link ResourceCache} (to avoid circular dependencies).
  26. * @param {object} options.gltf The glTF JSON.
  27. * @param {object} options.textureInfo The texture info object.
  28. * @param {Resource} options.gltfResource The {@link Resource} containing the glTF.
  29. * @param {Resource} options.baseResource The {@link Resource} that paths in the glTF JSON are relative to.
  30. * @param {SupportedImageFormats} options.supportedImageFormats The supported image formats.
  31. * @param {string} [options.cacheKey] The cache key of the resource.
  32. * @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.
  33. */
  34. constructor(options) {
  35. super();
  36. options = options ?? Frozen.EMPTY_OBJECT;
  37. const resourceCache = options.resourceCache;
  38. const gltf = options.gltf;
  39. const textureInfo = options.textureInfo;
  40. const gltfResource = options.gltfResource;
  41. const baseResource = options.baseResource;
  42. const supportedImageFormats = options.supportedImageFormats;
  43. const cacheKey = options.cacheKey;
  44. const asynchronous = options.asynchronous ?? true;
  45. //>>includeStart('debug', pragmas.debug);
  46. Check.typeOf.func("options.resourceCache", resourceCache);
  47. Check.typeOf.object("options.gltf", gltf);
  48. Check.typeOf.object("options.textureInfo", textureInfo);
  49. Check.typeOf.object("options.gltfResource", gltfResource);
  50. Check.typeOf.object("options.baseResource", baseResource);
  51. Check.typeOf.object("options.supportedImageFormats", supportedImageFormats);
  52. //>>includeEnd('debug');
  53. const textureId = textureInfo.index;
  54. // imageId is guaranteed to be defined otherwise the GltfTextureLoader
  55. // wouldn't have been created
  56. const imageId = GltfLoaderUtil.getImageIdFromTexture({
  57. gltf: gltf,
  58. textureId: textureId,
  59. supportedImageFormats: supportedImageFormats,
  60. });
  61. this._resourceCache = resourceCache;
  62. this._gltf = gltf;
  63. this._textureInfo = textureInfo;
  64. this._imageId = imageId;
  65. this._gltfResource = gltfResource;
  66. this._baseResource = baseResource;
  67. this._cacheKey = cacheKey;
  68. this._asynchronous = asynchronous;
  69. this._imageLoader = undefined;
  70. this._image = undefined;
  71. this._mipLevels = undefined;
  72. this._texture = undefined;
  73. this._state = ResourceLoaderState.UNLOADED;
  74. this._promise = undefined;
  75. }
  76. /**
  77. * The cache key of the resource.
  78. *
  79. *
  80. * @type {string}
  81. * @readonly
  82. * @private
  83. */
  84. get cacheKey() {
  85. return this._cacheKey;
  86. }
  87. /**
  88. * The texture.
  89. *
  90. *
  91. * @type {Texture}
  92. * @readonly
  93. * @private
  94. */
  95. get texture() {
  96. return this._texture;
  97. }
  98. /**
  99. * Loads the resource.
  100. * @returns {Promise<GltfDracoLoader>} A promise which resolves to the loader when the resource loading is completed.
  101. * @private
  102. */
  103. async load() {
  104. if (defined(this._promise)) {
  105. return this._promise;
  106. }
  107. this._state = ResourceLoaderState.LOADING;
  108. this._promise = loadResources(this);
  109. return this._promise;
  110. }
  111. /**
  112. * Processes the resource until it becomes ready.
  113. *
  114. * @param {FrameState} frameState The frame state.
  115. * @returns {boolean} true once all resourced are ready.
  116. * @private
  117. */
  118. process(frameState) {
  119. //>>includeStart('debug', pragmas.debug);
  120. Check.typeOf.object("frameState", frameState);
  121. //>>includeEnd('debug');
  122. if (this._state === ResourceLoaderState.READY) {
  123. return true;
  124. }
  125. if (
  126. this._state !== ResourceLoaderState.LOADED &&
  127. this._state !== ResourceLoaderState.PROCESSING
  128. ) {
  129. return false;
  130. }
  131. if (defined(this._texture)) {
  132. // Already created texture
  133. return false;
  134. }
  135. if (!defined(this._image)) {
  136. // Not ready to create texture
  137. return false;
  138. }
  139. this._state = ResourceLoaderState.PROCESSING;
  140. let texture;
  141. if (this._asynchronous) {
  142. const textureJob = scratchTextureJob;
  143. textureJob.set(
  144. this._gltf,
  145. this._textureInfo,
  146. this._cacheKey,
  147. this._image,
  148. this._mipLevels,
  149. frameState.context,
  150. );
  151. const jobScheduler = frameState.jobScheduler;
  152. if (!jobScheduler.execute(textureJob, JobType.TEXTURE)) {
  153. // Job scheduler is full. Try again next frame.
  154. return false;
  155. }
  156. texture = textureJob.texture;
  157. } else {
  158. texture = createTexture(
  159. this._gltf,
  160. this._textureInfo,
  161. this._cacheKey,
  162. this._image,
  163. this._mipLevels,
  164. frameState.context,
  165. );
  166. }
  167. // Unload everything except the texture
  168. this.unload();
  169. this._texture = texture;
  170. this._state = ResourceLoaderState.READY;
  171. this._resourceCache.statistics.addTextureLoader(this);
  172. return true;
  173. }
  174. /**
  175. * Unloads the resource.
  176. * @private
  177. */
  178. unload() {
  179. if (defined(this._texture)) {
  180. this._texture.destroy();
  181. }
  182. if (defined(this._imageLoader) && !this._imageLoader.isDestroyed()) {
  183. this._resourceCache.unload(this._imageLoader);
  184. }
  185. this._imageLoader = undefined;
  186. this._image = undefined;
  187. this._mipLevels = undefined;
  188. this._texture = undefined;
  189. this._gltf = undefined;
  190. }
  191. }
  192. class CreateTextureJob {
  193. constructor() {
  194. this.gltf = undefined;
  195. this.textureInfo = undefined;
  196. this.textureId = undefined;
  197. this.image = undefined;
  198. this.context = undefined;
  199. this.texture = undefined;
  200. }
  201. set(gltf, textureInfo, textureId, image, mipLevels, context) {
  202. this.gltf = gltf;
  203. this.textureInfo = textureInfo;
  204. this.textureId = textureId;
  205. this.image = image;
  206. this.mipLevels = mipLevels;
  207. this.context = context;
  208. }
  209. execute() {
  210. this.texture = createTexture(
  211. this.gltf,
  212. this.textureInfo,
  213. this.textureId,
  214. this.image,
  215. this.mipLevels,
  216. this.context,
  217. );
  218. }
  219. }
  220. function createTexture(
  221. gltf,
  222. textureInfo,
  223. textureId,
  224. image,
  225. mipLevels,
  226. context,
  227. ) {
  228. // internalFormat is only defined for CompressedTextureBuffer
  229. const internalFormat = image.internalFormat;
  230. let compressedTextureNoMipmap = false;
  231. if (PixelFormat.isCompressedFormat(internalFormat) && !defined(mipLevels)) {
  232. compressedTextureNoMipmap = true;
  233. }
  234. const sampler = GltfLoaderUtil.createSampler({
  235. gltf: gltf,
  236. textureInfo: textureInfo,
  237. compressedTextureNoMipmap: compressedTextureNoMipmap,
  238. });
  239. const minFilter = sampler.minificationFilter;
  240. const wrapS = sampler.wrapS;
  241. const wrapT = sampler.wrapT;
  242. const samplerRequiresMipmap =
  243. minFilter === TextureMinificationFilter.NEAREST_MIPMAP_NEAREST ||
  244. minFilter === TextureMinificationFilter.NEAREST_MIPMAP_LINEAR ||
  245. minFilter === TextureMinificationFilter.LINEAR_MIPMAP_NEAREST ||
  246. minFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR;
  247. // generateMipmap is disallowed for compressed textures. Compressed textures
  248. // can have mipmaps but they must come with the KTX2 instead of generated by
  249. // WebGL. Also note from the KHR_texture_basisu spec:
  250. //
  251. // When a texture refers to a sampler with mipmap minification or when the
  252. // sampler is undefined, the KTX2 image SHOULD contain a full mip pyramid.
  253. //
  254. const generateMipmap = !defined(internalFormat) && samplerRequiresMipmap;
  255. // WebGL 1 requires power-of-two texture dimensions for mipmapping and REPEAT/MIRRORED_REPEAT wrap modes.
  256. const requiresPowerOfTwo =
  257. generateMipmap ||
  258. wrapS === TextureWrap.REPEAT ||
  259. wrapS === TextureWrap.MIRRORED_REPEAT ||
  260. wrapT === TextureWrap.REPEAT ||
  261. wrapT === TextureWrap.MIRRORED_REPEAT;
  262. const nonPowerOfTwo =
  263. !CesiumMath.isPowerOfTwo(image.width) ||
  264. !CesiumMath.isPowerOfTwo(image.height);
  265. const requiresResize = requiresPowerOfTwo && nonPowerOfTwo;
  266. let texture;
  267. if (defined(internalFormat)) {
  268. if (
  269. !context.webgl2 &&
  270. PixelFormat.isCompressedFormat(internalFormat) &&
  271. nonPowerOfTwo &&
  272. requiresPowerOfTwo
  273. ) {
  274. console.warn(
  275. "Compressed texture uses REPEAT or MIRRORED_REPEAT texture wrap mode and dimensions are not powers of two. The texture may be rendered incorrectly.",
  276. );
  277. }
  278. texture = Texture.create({
  279. id: textureId,
  280. context: context,
  281. source: {
  282. arrayBufferView: image.bufferView, // Only defined for CompressedTextureBuffer
  283. mipLevels: mipLevels,
  284. },
  285. width: image.width,
  286. height: image.height,
  287. pixelFormat: image.internalFormat, // Only defined for CompressedTextureBuffer
  288. sampler: sampler,
  289. });
  290. } else {
  291. if (requiresResize) {
  292. image = resizeImageToNextPowerOfTwo(image);
  293. }
  294. texture = Texture.create({
  295. id: textureId,
  296. context: context,
  297. source: image,
  298. sampler: sampler,
  299. flipY: false,
  300. skipColorSpaceConversion: true,
  301. });
  302. }
  303. if (generateMipmap) {
  304. texture.generateMipmap();
  305. }
  306. return texture;
  307. }
  308. const scratchTextureJob = new CreateTextureJob();
  309. async function loadResources(loader) {
  310. const resourceCache = loader._resourceCache;
  311. try {
  312. const imageLoader = resourceCache.getImageLoader({
  313. gltf: loader._gltf,
  314. imageId: loader._imageId,
  315. gltfResource: loader._gltfResource,
  316. baseResource: loader._baseResource,
  317. });
  318. loader._imageLoader = imageLoader;
  319. await imageLoader.load();
  320. if (loader.isDestroyed()) {
  321. return;
  322. }
  323. // Now wait for process() to run to finish loading
  324. loader._image = imageLoader.image;
  325. loader._mipLevels = imageLoader.mipLevels;
  326. loader._state = ResourceLoaderState.LOADED;
  327. return loader;
  328. } catch (error) {
  329. if (loader.isDestroyed()) {
  330. return;
  331. }
  332. loader.unload();
  333. loader._state = ResourceLoaderState.FAILED;
  334. const errorMessage = "Failed to load texture";
  335. throw loader.getError(errorMessage, error);
  336. }
  337. }
  338. export default GltfTextureLoader;