UNPKG

mdx-m3-viewer

Version:

A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.

106 lines (87 loc) 3.6 kB
import {base256ToString} from '../../../common/typecast'; import {decodeDxt1, decodeDxt3, decodeDxt5, decodeRgtc} from '../../../common/dxt'; import Texture from '../../texture'; let DDS_MAGIC = 0x20534444; let DDSD_MIPMAPCOUNT = 0x20000; let DDPF_FOURCC = 0x4; let FOURCC_DXT1 = 0x31545844; let FOURCC_DXT3 = 0x33545844; let FOURCC_DXT5 = 0x35545844; let FOURCC_ATI2 = 0x32495441; /** * A DDS texture handler. * * Largely based on https://github.com/toji/webctx-texture-utils/blob/master/texture-util/dds.js */ export default class DdsTexture extends Texture { /** * @param {ArrayBuffer} src */ load(src) { let gl = this.viewer.gl; let compressedTextures = this.viewer.webgl.extensions.compressedTextureS3tc; let header = new Int32Array(src, 0, 31); if (header[0] !== DDS_MAGIC) { throw new Error('Wrong magic number'); } if (!(header[20] & DDPF_FOURCC)) { throw new Error('Not DXT'); } let fourCC = header[21]; let blockBytes; let internalFormat; if (fourCC === FOURCC_DXT1) { blockBytes = 8; internalFormat = compressedTextures ? compressedTextures.COMPRESSED_RGBA_S3TC_DXT1_EXT : null; } else if (fourCC === FOURCC_DXT3) { blockBytes = 16; internalFormat = compressedTextures ? compressedTextures.COMPRESSED_RGBA_S3TC_DXT3_EXT : null; } else if (fourCC === FOURCC_DXT5) { blockBytes = 16; internalFormat = compressedTextures ? compressedTextures.COMPRESSED_RGBA_S3TC_DXT5_EXT : null; } else if (fourCC === FOURCC_ATI2) { blockBytes = 16; internalFormat = null; } else { throw new Error(`Unsupported FourCC: ${base256ToString(fourCC)}`); } let mipmapCount = 1; if (header[2] & DDSD_MIPMAPCOUNT) { mipmapCount = Math.max(1, header[7]); } let width = header[4]; let height = header[3]; let dataOffset = header[1] + 4; let dataLength; let data; const id = gl.createTexture(); this.width = width; this.height = height; this.webglResource = id; gl.bindTexture(gl.TEXTURE_2D, id); for (let i = 0; i < mipmapCount; i++) { dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes; // Let the GPU handle the compressed data if it supports it. if (internalFormat) { data = new Uint8Array(src, dataOffset, dataLength); gl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, data); // Otherwise, decode the data on the client. } else { data = new Uint16Array(src, dataOffset, dataLength / 2); if (fourCC === FOURCC_DXT1) { gl.texImage2D(gl.TEXTURE_2D, i, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, decodeDxt1(data, width, height)); } else if (fourCC === FOURCC_DXT3) { gl.texImage2D(gl.TEXTURE_2D, i, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, decodeDxt3(data, width, height)); } else if (fourCC === FOURCC_DXT5) { gl.texImage2D(gl.TEXTURE_2D, i, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, decodeDxt5(data, width, height)); } else { gl.texImage2D(gl.TEXTURE_2D, i, gl.LUMINANCE_ALPHA, width, height, 0, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE, decodeRgtc(new Uint8Array(src, dataOffset, dataLength), width, height)); } } dataOffset += dataLength; width = Math.max(width / 2, 1); height = Math.max(height / 2, 1); } this.viewer.webgl.setTextureMode(gl.REPEAT, gl.REPEAT, gl.LINEAR, mipmapCount > 1 ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR); } }