ogl-typescript
Version:
TypeScript port of OGL WebGL Library
181 lines • 6.8 kB
JavaScript
import { Texture } from '../core/Texture';
import { KTXTexture } from './KTXTexture';
// For compressed textures, generate using https://github.com/TimvanScherpenzeel/texture-compressor
let cache = {};
const supportedExtensions = [];
export class TextureLoader {
static load(gl, { src, // string or object of extension:src key-values
// {
// pvrtc: '...ktx',
// s3tc: '...ktx',
// etc: '...ktx',
// etc1: '...ktx',
// astc: '...ktx',
// webp: '...webp',
// jpg: '...jpg',
// png: '...png',
// }
// Only props relevant to KTXTexture
wrapS = gl.CLAMP_TO_EDGE, wrapT = gl.CLAMP_TO_EDGE, anisotropy = 0,
// For regular images
format = gl.RGBA, internalFormat = format, generateMipmaps = true, minFilter = generateMipmaps ? gl.NEAREST_MIPMAP_LINEAR : gl.LINEAR, magFilter = gl.LINEAR, premultiplyAlpha = false, unpackAlignment = 4, flipY = true, } = {}) {
const support = TextureLoader.getSupportedExtensions(gl);
let ext = 'none';
// If src is string, determine which format from the extension
if (typeof src === 'string') {
ext = src.split('.').pop().split('?')[0].toLowerCase();
}
// If src is object, use supported extensions and provided list to choose best option
// Get first supported match, so put in order of preference
if (typeof src === 'object') {
for (const prop in src) {
if (support.includes(prop.toLowerCase())) {
ext = prop.toLowerCase();
src = src[prop];
break;
}
}
}
// Stringify props
const cacheID = String(src) +
wrapS +
wrapT +
anisotropy +
format +
internalFormat +
generateMipmaps +
minFilter +
magFilter +
premultiplyAlpha +
unpackAlignment +
flipY +
gl.renderer.id;
// Check cache for existing texture
if (cache[cacheID])
return cache[cacheID];
let texture;
switch (ext) {
case 'ktx':
case 'pvrtc':
case 's3tc':
case 'etc':
case 'etc1':
case 'astc':
// Load compressed texture using KTX format
texture = new KTXTexture(gl, {
src: src,
wrapS,
wrapT,
anisotropy,
minFilter,
magFilter,
});
texture.loaded = this.loadKTX(src, texture);
break;
case 'webp':
case 'jpg':
case 'jpeg':
case 'png':
texture = new Texture(gl, {
wrapS,
wrapT,
anisotropy,
format,
internalFormat,
generateMipmaps,
minFilter,
magFilter,
premultiplyAlpha,
unpackAlignment,
flipY,
});
texture.loaded = this.loadImage(gl, src, texture);
break;
default:
console.warn('No supported format supplied');
texture = new Texture(gl);
}
texture.ext = ext;
cache[cacheID] = texture;
return texture;
}
static getSupportedExtensions(gl) {
if (supportedExtensions.length)
return supportedExtensions;
const extensions = {
pvrtc: gl.renderer.getExtension('WEBGL_compressed_texture_pvrtc') || gl.renderer.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'),
s3tc: gl.renderer.getExtension('WEBGL_compressed_texture_s3tc') ||
gl.renderer.getExtension('MOZ_WEBGL_compressed_texture_s3tc') ||
gl.renderer.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc'),
etc: gl.renderer.getExtension('WEBGL_compressed_texture_etc'),
etc1: gl.renderer.getExtension('WEBGL_compressed_texture_etc1'),
astc: gl.renderer.getExtension('WEBGL_compressed_texture_astc'),
};
for (const ext in extensions)
if (extensions[ext])
supportedExtensions.push(ext);
// Check for WebP support
if (detectWebP)
supportedExtensions.push('webp');
// Formats supported by all
supportedExtensions.push('png', 'jpg');
return supportedExtensions;
}
static loadKTX(src, texture) {
return fetch(src)
.then((res) => res.arrayBuffer())
.then((buffer) => texture.parseBuffer(buffer));
}
static loadImage(gl, src, texture) {
return decodeImage(src).then((imgBmp) => {
// Catch non POT textures and update params to avoid errors
if (!powerOfTwo(imgBmp.width) || !powerOfTwo(imgBmp.height)) {
if (texture.generateMipmaps)
texture.generateMipmaps = false;
if (texture.minFilter === gl.NEAREST_MIPMAP_LINEAR)
texture.minFilter = gl.LINEAR;
if (texture.wrapS === gl.REPEAT)
texture.wrapS = texture.wrapT = gl.CLAMP_TO_EDGE;
}
texture.image = imgBmp;
// For createImageBitmap, close once uploaded
texture.onUpdate = () => {
if (imgBmp.close)
imgBmp.close();
texture.onUpdate = null;
};
return imgBmp;
});
}
static clearCache() {
cache = {};
}
}
function detectWebP() {
return document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0;
}
function powerOfTwo(value) {
return Math.log2(value) % 1 === 0;
}
function decodeImage(src) {
return new Promise((resolve) => {
const img = new Image();
img.crossOrigin = '';
img.src = src;
// Only chrome's implementation of createImageBitmap is fully supported
// const isChrome = navigator.userAgent.toLowerCase().includes('chrome');
// if (!!window.createImageBitmap && isChrome) {
// img.onload = () => {
// createImageBitmap(img, {
// imageOrientation: 'flipY',
// premultiplyAlpha: 'none',
// }).then(imgBmp => {
// resolve(imgBmp);
// });
// };
// } else {
img.onload = () => resolve(img);
// }
});
}
//# sourceMappingURL=TextureLoader.js.map