123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- import when from '../ThirdParty/when.js';
- import Check from './Check.js';
- import CompressedTextureBuffer from './CompressedTextureBuffer.js';
- import defined from './defined.js';
- import PixelFormat from './PixelFormat.js';
- import Resource from './Resource.js';
- import RuntimeError from './RuntimeError.js';
- import WebGLConstants from './WebGLConstants.js';
- /**
- * Asynchronously loads and parses the given URL to a KTX file or parses the raw binary data of a KTX file.
- * Returns a promise that will resolve to an object containing the image buffer, width, height and format once loaded,
- * or reject if the URL failed to load or failed to parse the data. The data is loaded
- * using XMLHttpRequest, which means that in order to make requests to another origin,
- * the server must have Cross-Origin Resource Sharing (CORS) headers enabled.
- * <p>
- * The following are part of the KTX format specification but are not supported:
- * <ul>
- * <li>Big-endian files</li>
- * <li>Metadata</li>
- * <li>3D textures</li>
- * <li>Texture Arrays</li>
- * <li>Cubemaps</li>
- * <li>Mipmaps</li>
- * </ul>
- * </p>
- *
- * @exports loadKTX
- *
- * @param {Resource|String|ArrayBuffer} resourceOrUrlOrBuffer The URL of the binary data or an ArrayBuffer.
- * @returns {Promise.<CompressedTextureBuffer>|undefined} A promise that will resolve to the requested data when loaded. Returns undefined if <code>request.throttle</code> is true and the request does not have high enough priority.
- *
- * @exception {RuntimeError} Invalid KTX file.
- * @exception {RuntimeError} File is the wrong endianness.
- * @exception {RuntimeError} glInternalFormat is not a valid format.
- * @exception {RuntimeError} glType must be zero when the texture is compressed.
- * @exception {RuntimeError} The type size for compressed textures must be 1.
- * @exception {RuntimeError} glFormat must be zero when the texture is compressed.
- * @exception {RuntimeError} Generating mipmaps for a compressed texture is unsupported.
- * @exception {RuntimeError} The base internal format must be the same as the format for uncompressed textures.
- * @exception {RuntimeError} 3D textures are not supported.
- * @exception {RuntimeError} Texture arrays are not supported.
- * @exception {RuntimeError} Cubemaps are not supported.
- *
- * @example
- * // load a single URL asynchronously
- * Cesium.loadKTX('some/url').then(function(ktxData) {
- * var width = ktxData.width;
- * var height = ktxData.height;
- * var format = ktxData.internalFormat;
- * var arrayBufferView = ktxData.bufferView;
- * // use the data to create a texture
- * }).otherwise(function(error) {
- * // an error occurred
- * });
- *
- * @see {@link https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/|KTX file format}
- * @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing}
- * @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A}
- */
- function loadKTX(resourceOrUrlOrBuffer) {
- //>>includeStart('debug', pragmas.debug);
- Check.defined('resourceOrUrlOrBuffer', resourceOrUrlOrBuffer);
- //>>includeEnd('debug');
- var loadPromise;
- if (resourceOrUrlOrBuffer instanceof ArrayBuffer || ArrayBuffer.isView(resourceOrUrlOrBuffer)) {
- loadPromise = when.resolve(resourceOrUrlOrBuffer);
- } else {
- var resource = Resource.createIfNeeded(resourceOrUrlOrBuffer);
- loadPromise = resource.fetchArrayBuffer();
- }
- if (!defined(loadPromise)) {
- return undefined;
- }
- return loadPromise.then(function(data) {
- if (defined(data)) {
- return parseKTX(data);
- }
- });
- }
- var fileIdentifier = [0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A];
- var endiannessTest = 0x04030201;
- var faceOrder = ['positiveX', 'negativeX', 'positiveY', 'negativeY', 'positiveZ', 'negativeZ'];
- var sizeOfUint32 = 4;
- function parseKTX(data) {
- var byteBuffer = new Uint8Array(data);
- var isKTX = true;
- var i;
- for (i = 0; i < fileIdentifier.length; ++i) {
- if (fileIdentifier[i] !== byteBuffer[i]) {
- isKTX = false;
- break;
- }
- }
- if (!isKTX) {
- throw new RuntimeError('Invalid KTX file.');
- }
- var view;
- var byteOffset;
- if (defined(data.buffer)) {
- view = new DataView(data.buffer);
- byteOffset = data.byteOffset;
- } else {
- view = new DataView(data);
- byteOffset = 0;
- }
- byteOffset += 12; // skip identifier
- var endianness = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- if (endianness !== endiannessTest) {
- throw new RuntimeError('File is the wrong endianness.');
- }
- var glType = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var glTypeSize = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var glFormat = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var glInternalFormat = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var glBaseInternalFormat = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var pixelWidth = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var pixelHeight = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var pixelDepth = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var numberOfArrayElements = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var numberOfFaces = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var numberOfMipmapLevels = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var bytesOfKeyValueByteSize = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- // skip metadata
- byteOffset += bytesOfKeyValueByteSize;
- var imageSize = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var texture;
- if (defined(data.buffer)) {
- texture = new Uint8Array(data.buffer, byteOffset, imageSize);
- } else {
- texture = new Uint8Array(data, byteOffset, imageSize);
- }
- // Some tools use a sized internal format.
- // See table 2: https://www.opengl.org/sdk/docs/man/html/glTexImage2D.xhtml
- if (glInternalFormat === WebGLConstants.RGB8) {
- glInternalFormat = PixelFormat.RGB;
- } else if (glInternalFormat === WebGLConstants.RGBA8) {
- glInternalFormat = PixelFormat.RGBA;
- }
- if (!PixelFormat.validate(glInternalFormat)) {
- throw new RuntimeError('glInternalFormat is not a valid format.');
- }
- if (PixelFormat.isCompressedFormat(glInternalFormat)) {
- if (glType !== 0) {
- throw new RuntimeError('glType must be zero when the texture is compressed.');
- }
- if (glTypeSize !== 1) {
- throw new RuntimeError('The type size for compressed textures must be 1.');
- }
- if (glFormat !== 0) {
- throw new RuntimeError('glFormat must be zero when the texture is compressed.');
- }
- } else if (glType !== WebGLConstants.UNSIGNED_BYTE) {
- throw new RuntimeError('Only unsigned byte buffers are supported.');
- } else if (glBaseInternalFormat !== glFormat) {
- throw new RuntimeError('The base internal format must be the same as the format for uncompressed textures.');
- }
- if (pixelDepth !== 0) {
- throw new RuntimeError('3D textures are unsupported.');
- }
- if (numberOfArrayElements !== 0) {
- throw new RuntimeError('Texture arrays are unsupported.');
- }
- var offset = texture.byteOffset;
- var mipmaps = new Array(numberOfMipmapLevels);
- for (i = 0; i < numberOfMipmapLevels; ++i) {
- var level = mipmaps[i] = {};
- for (var j = 0; j < numberOfFaces; ++j) {
- var width = pixelWidth >> i;
- var height = pixelHeight >> i;
- var levelSize = PixelFormat.isCompressedFormat(glInternalFormat) ?
- PixelFormat.compressedTextureSizeInBytes(glInternalFormat, width, height) :
- PixelFormat.textureSizeInBytes(glInternalFormat, glType, width, height);
- var levelBuffer = new Uint8Array(texture.buffer, offset, levelSize);
- level[faceOrder[j]] = new CompressedTextureBuffer(glInternalFormat, width, height, levelBuffer);
- offset += levelSize;
- }
- offset += 3 - ((offset + 3) % 4) + 4;
- }
- var result = mipmaps;
- if (numberOfFaces === 1) {
- for (i = 0; i < numberOfMipmapLevels; ++i) {
- result[i] = result[i][faceOrder[0]];
- }
- }
- if (numberOfMipmapLevels === 1) {
- result = result[0];
- }
- return result;
- }
- export default loadKTX;
|