UNPKG

mdx-m3-viewer

Version:

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

271 lines 11.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.decodeRgtc = exports.decodeDxt5 = exports.decodeDxt3 = exports.decodeDxt1 = void 0; const convertbitrange_1 = require("./convertbitrange"); const dxt4to8 = (0, convertbitrange_1.default)(4, 8); const dxt5to8 = (0, convertbitrange_1.default)(5, 8); const dxt6to8 = (0, convertbitrange_1.default)(6, 8); const dx1colors = new Uint8Array(16); const dx3colors = new Uint8Array(12); const dx5alphas = new Uint8Array(8); const red = new Uint8Array(8); const green = new Uint8Array(8); function dx1Colors(out, color0, color1) { const r0 = ((color0 >> 11) & 31) * dxt5to8; const g0 = ((color0 >> 5) & 63) * dxt6to8; const b0 = (color0 & 31) * dxt5to8; const r1 = ((color1 >> 11) & 31) * dxt5to8; const g1 = ((color1 >> 5) & 63) * dxt6to8; const b1 = (color1 & 31) * dxt5to8; // Minimum and maximum colors. out[0] = r0; out[1] = g0; out[2] = b0; out[3] = 255; out[4] = r1; out[5] = g1; out[6] = b1; out[7] = 255; // Interpolated colors. if (color0 > color1) { out[8] = (5 * r0 + 3 * r1) >> 3; out[9] = (5 * g0 + 3 * g1) >> 3; out[10] = (5 * b0 + 3 * b1) >> 3; out[11] = 255; out[12] = (5 * r1 + 3 * r0) >> 3; out[13] = (5 * g1 + 3 * g0) >> 3; out[14] = (5 * b1 + 3 * b0) >> 3; out[15] = 255; } else { out[8] = (r0 + r1) >> 1; out[9] = (g0 + g1) >> 1; out[10] = (b0 + b1) >> 1; out[11] = 255; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 0; } } function dx3Colors(out, color0, color1) { const r0 = ((color0 >> 11) & 31) * dxt5to8; const g0 = ((color0 >> 5) & 63) * dxt6to8; const b0 = (color0 & 31) * dxt5to8; const r1 = ((color1 >> 11) & 31) * dxt5to8; const g1 = ((color1 >> 5) & 63) * dxt6to8; const b1 = (color1 & 31) * dxt5to8; // Minimum and maximum colors. out[0] = r0; out[1] = g0; out[2] = b0; out[3] = r1; out[4] = g1; out[5] = b1; // Interpolated colors. out[6] = (5 * r0 + 3 * r1) >> 3; out[7] = (5 * g0 + 3 * g1) >> 3; out[8] = (5 * b0 + 3 * b1) >> 3; out[9] = (5 * r1 + 3 * r0) >> 3; out[10] = (5 * g1 + 3 * g0) >> 3; out[11] = (5 * b1 + 3 * b0) >> 3; } function dx5Alphas(out, alpha0, alpha1) { // Minimum and maximum alphas. out[0] = alpha0; out[1] = alpha1; // Interpolated alphas. if (alpha0 > alpha1) { out[2] = (54 * alpha0 + 9 * alpha1) >> 6; out[3] = (45 * alpha0 + 18 * alpha1) >> 6; out[4] = (36 * alpha0 + 27 * alpha1) >> 6; out[5] = (27 * alpha0 + 36 * alpha1) >> 6; out[6] = (18 * alpha0 + 45 * alpha1) >> 6; out[7] = (9 * alpha0 + 54 * alpha1) >> 6; } else { out[2] = (12 * alpha0 + 3 * alpha1) >> 4; out[3] = (9 * alpha0 + 6 * alpha1) >> 4; out[4] = (6 * alpha0 + 9 * alpha1) >> 4; out[5] = (3 * alpha0 + 12 * alpha1) >> 4; out[6] = 0; out[7] = 255; } } function rgColors(out, color0, color1) { // Minimum and maximum red colors. out[0] = color0; out[1] = color1; // Interpolated red colors. if (color0 > color1) { out[2] = (6 * color0 + 1 * color1) / 7; out[3] = (5 * color0 + 2 * color1) / 7; out[4] = (4 * color0 + 3 * color1) / 7; out[5] = (3 * color0 + 4 * color1) / 7; out[6] = (2 * color0 + 5 * color1) / 7; out[7] = (1 * color0 + 6 * color1) / 7; } else { out[2] = (4 * color0 + 1 * color1) / 5; out[3] = (3 * color0 + 2 * color1) / 5; out[4] = (2 * color0 + 3 * color1) / 5; out[5] = (1 * color0 + 4 * color1) / 5; out[6] = 0; out[7] = 1; } } /** * Decodes DXT1 data to a Uint8Array typed array with 8-8-8-8 RGBA bits. * * DXT1 is also known as BC1. */ function decodeDxt1(src, width, height) { const dst = new Uint8Array(width * height * 4); for (let blockY = 0, blockHeight = height / 4; blockY < blockHeight; blockY++) { for (let blockX = 0, blockWidth = width / 4; blockX < blockWidth; blockX++) { const i = 8 * (blockY * blockWidth + blockX); // Get the color values. dx1Colors(dx1colors, src[i] + 256 * src[i + 1], src[i + 2] + 256 * src[i + 3]); // The offset to the first pixel in the destination. const dstI = (blockY * 16) * width + blockX * 16; // All 32 color bits. const bits = src[i + 4] | (src[i + 5] << 8) | (src[i + 6] << 16) | (src[i + 7] << 24); for (let row = 0; row < 4; row++) { const rowOffset = row * 8; const dstOffset = dstI + row * width * 4; for (let column = 0; column < 4; column++) { const dstIndex = dstOffset + column * 4; const colorOffset = ((bits >> (rowOffset + column * 2)) & 3) * 4; dst[dstIndex + 0] = dx1colors[colorOffset + 0]; dst[dstIndex + 1] = dx1colors[colorOffset + 1]; dst[dstIndex + 2] = dx1colors[colorOffset + 2]; dst[dstIndex + 3] = dx1colors[colorOffset + 3]; } } } } return dst; } exports.decodeDxt1 = decodeDxt1; /** * Decodes DXT3 data to a Uint8Array typed array with 8-8-8-8 RGBA bits. * * DXT3 is also known as BC2. */ function decodeDxt3(src, width, height) { const dst = new Uint8Array(width * height * 4); const rowBytes = width * 4; for (let blockY = 0, blockHeight = height / 4; blockY < blockHeight; blockY++) { for (let blockX = 0, blockWidth = width / 4; blockX < blockWidth; blockX++) { const i = 16 * (blockY * blockWidth + blockX); // Get the color values. dx3Colors(dx3colors, src[i + 8] + 256 * src[i + 9], src[i + 10] + 256 * src[i + 11]); let dstI = (blockY * 16) * width + blockX * 16; for (let row = 0; row < 4; row++) { // Get 16 bits of alpha indices. const alphaBits = src[i + row * 2] + 256 * src[i + 1 + row * 2]; // Get 8 bits of color indices. const colorBits = src[i + 12 + row]; for (let column = 0; column < 4; column++) { const dstIndex = dstI + column * 4; const colorIndex = ((colorBits >> (column * 2)) & 3) * 3; dst[dstIndex + 0] = dx3colors[colorIndex + 0]; dst[dstIndex + 1] = dx3colors[colorIndex + 1]; dst[dstIndex + 2] = dx3colors[colorIndex + 2]; dst[dstIndex + 3] = ((alphaBits >> (column * 4)) & 0xf) * dxt4to8; } dstI += rowBytes; } } } return dst; } exports.decodeDxt3 = decodeDxt3; /** * Decodes DXT5 data to a Uint8Array typed array with 8-8-8-8 RGBA bits. * * DXT5 is also known as BC3. */ function decodeDxt5(src, width, height) { const dst = new Uint8Array(width * height * 4); const rowBytes = width * 4; for (let blockY = 0, blockHeight = height / 4; blockY < blockHeight; blockY++) { for (let blockX = 0, blockWidth = width / 4; blockX < blockWidth; blockX++) { const i = 16 * (blockY * blockWidth + blockX); // Get the alpha values. dx5Alphas(dx5alphas, src[i], src[i + 1]); // Get the color values. dx3Colors(dx3colors, src[i + 8] + 256 * src[i + 9], src[i + 10] + 256 * src[i + 11]); // The offset to the first pixel in the destination. let dstI = (blockY * 16) * width + blockX * 16; // The outer loop is only needed because JS bitwise operators only work on 32bit integers, while the alpha flags contain 48 bits. // Processing is instead done in two blocks, where each one handles 24 bits, or two rows of 4 pixels. for (let block = 0; block < 2; block++) { const alphaOffset = i + 2 + block * 3; const colorOffset = i + 12 + block * 2; // 24 alpha bits. const alphaBits = src[alphaOffset] + 256 * (src[alphaOffset + 1] + 256 * src[alphaOffset + 2]); // Go over two rows. for (let row = 0; row < 2; row++) { const colorBits = src[colorOffset + row]; // Go over four columns. for (let column = 0; column < 4; column++) { const dstIndex = dstI + column * 4; const colorIndex = ((colorBits >> (column * 2)) & 3) * 3; const alphaIndex = (alphaBits >> (row * 12 + column * 3)) & 7; // Set the pixel. dst[dstIndex + 0] = dx3colors[colorIndex + 0]; dst[dstIndex + 1] = dx3colors[colorIndex + 1]; dst[dstIndex + 2] = dx3colors[colorIndex + 2]; dst[dstIndex + 3] = dx5alphas[alphaIndex]; } // Next row. dstI += rowBytes; } } } } return dst; } exports.decodeDxt5 = decodeDxt5; /** * Decodes RGTC data to a Uint8Array typed array with 8-8 RG bits. * * RGTC is also known as BC5, ATI2, and 3Dc. */ function decodeRgtc(src, width, height) { const dst = new Uint8Array(width * height * 2); const rowBytes = width * 2; for (let blockY = 0, blockHeight = height / 4; blockY < blockHeight; blockY++) { for (let blockX = 0, blockWidth = width / 4; blockX < blockWidth; blockX++) { const i = 16 * (blockY * blockWidth + blockX); // Get the red colors. rgColors(red, src[i], src[i + 1]); // Get the green colors. rgColors(green, src[i + 8], src[i + 9]); // The offset to the first pixel in the destination. let dstI = (blockY * 8) * width + blockX * 8; // Split to two blocks of two rows, because there are 48 color bits. for (let block = 0; block < 2; block++) { const blockOffset = i + block * 3; // Get 24 bits of the color indices. const redbits = src[blockOffset + 2] + 256 * (src[blockOffset + 3] + 256 * src[blockOffset + 4]); const greenbits = src[blockOffset + 10] + 256 * (src[blockOffset + 11] + 256 * src[blockOffset + 12]); for (let row = 0; row < 2; row++) { const rowOffset = row * 4; for (let column = 0; column < 4; column++) { const dstOffset = dstI + column * 2; const shifts = 3 * (rowOffset + column); dst[dstOffset + 1] = red[(redbits >> shifts) & 7]; dst[dstOffset + 2] = green[(greenbits >> shifts) & 7]; } // Next row. dstI += rowBytes; } } } } return dst; } exports.decodeRgtc = decodeRgtc; //# sourceMappingURL=dxt.js.map