mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +02:00
feat: generalization
This commit is contained in:
1 parent
c624136dc7
commit
436a519db8
3 files changed
+318
-317
No files matched your search
Vendored
+12
@@ -16,6 +16,7 @@
|
|||||||
"cleandist",
|
"cleandist",
|
||||||
"cockroackdb",
|
"cockroackdb",
|
||||||
"corsproxy",
|
"corsproxy",
|
||||||
|
"CUBEMAP",
|
||||||
"CUBETEXCOORD",
|
"CUBETEXCOORD",
|
||||||
"customperiod",
|
"customperiod",
|
||||||
"datasource",
|
"datasource",
|
||||||
@@ -42,10 +43,15 @@
|
|||||||
"krieg",
|
"krieg",
|
||||||
"Leaderboard",
|
"Leaderboard",
|
||||||
"leaderboards",
|
"leaderboards",
|
||||||
|
"LINEARSIZE",
|
||||||
"loddist",
|
"loddist",
|
||||||
"lods",
|
"lods",
|
||||||
"metallicness",
|
"metallicness",
|
||||||
|
"MIPMAPCOUNT",
|
||||||
"MURICA",
|
"MURICA",
|
||||||
|
"NEGATIVEX",
|
||||||
|
"NEGATIVEY",
|
||||||
|
"NEGATIVEZ",
|
||||||
"nmatname",
|
"nmatname",
|
||||||
"noout",
|
"noout",
|
||||||
"normalmap",
|
"normalmap",
|
||||||
@@ -53,9 +59,13 @@
|
|||||||
"outdir",
|
"outdir",
|
||||||
"outfile",
|
"outfile",
|
||||||
"ownedtanks",
|
"ownedtanks",
|
||||||
|
"PIXELFORMAT",
|
||||||
"playerachievements",
|
"playerachievements",
|
||||||
"playerinfo",
|
"playerinfo",
|
||||||
"pngjs",
|
"pngjs",
|
||||||
|
"POSITIVEX",
|
||||||
|
"POSITIVEY",
|
||||||
|
"POSITIVEZ",
|
||||||
"ratingsleaderboard",
|
"ratingsleaderboard",
|
||||||
"ratingsplot",
|
"ratingsplot",
|
||||||
"researchable",
|
"researchable",
|
||||||
@@ -82,8 +92,10 @@
|
|||||||
"texconv",
|
"texconv",
|
||||||
"TEXCOORD",
|
"TEXCOORD",
|
||||||
"tresabhi",
|
"tresabhi",
|
||||||
|
"TYPELESS",
|
||||||
"ubjson",
|
"ubjson",
|
||||||
"Unicum",
|
"Unicum",
|
||||||
|
"UNORM",
|
||||||
"Unregistering",
|
"Unregistering",
|
||||||
"vickers",
|
"vickers",
|
||||||
"vitepress",
|
"vitepress",
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import { readDVPLFile } from '../blitz/readDVPLFile';
|
import { readDVPLFile } from '../blitz/readDVPLFile';
|
||||||
import { DdsStream } from '../streams/dds';
|
import { DdsStream } from '../streams/dds';
|
||||||
import { ToJpgOptions } from './toJpg';
|
|
||||||
|
|
||||||
export async function readTexture(
|
export async function readTexture(path: string) {
|
||||||
path: string,
|
console.log(path);
|
||||||
options?: Partial<Omit<ToJpgOptions, 'format'>>,
|
|
||||||
) {
|
|
||||||
const ddsTexturePath = path.replace('.tex', '.dx11.dds.dvpl');
|
const ddsTexturePath = path.replace('.tex', '.dx11.dds.dvpl');
|
||||||
const decompressedDvpl = await readDVPLFile(ddsTexturePath);
|
const decompressedDvpl = await readDVPLFile(ddsTexturePath);
|
||||||
const stream = new DdsStream(decompressedDvpl);
|
const stream = new DdsStream(decompressedDvpl);
|
||||||
|
|||||||
+303
-312
@@ -1,5 +1,4 @@
|
|||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import { readDVPLFile } from '../blitz/readDVPLFile';
|
|
||||||
import { WindowsStream } from './windows';
|
import { WindowsStream } from './windows';
|
||||||
|
|
||||||
enum DxgiFormat {
|
enum DxgiFormat {
|
||||||
@@ -172,16 +171,241 @@ enum DdsCaps2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DdsStream extends WindowsStream {
|
export class DdsStream extends WindowsStream {
|
||||||
|
magicNumber() {
|
||||||
|
const magic = this.dword();
|
||||||
|
|
||||||
|
if (magic !== 0x20534444) {
|
||||||
|
throw new Error(`Invalid DDS magic number: 0x${magic.toString(16)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return magic;
|
||||||
|
}
|
||||||
|
pixelFormat() {
|
||||||
|
return {
|
||||||
|
size: this.dword(),
|
||||||
|
flags: this.dword() as DdpfFlags,
|
||||||
|
fourCC: this.ascii(4),
|
||||||
|
rgbBitCount: this.dword(),
|
||||||
|
rBitMask: this.dword(),
|
||||||
|
gBitMask: this.dword(),
|
||||||
|
bBitMask: this.dword(),
|
||||||
|
aBitMask: this.dword(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
header() {
|
||||||
|
return {
|
||||||
|
size: this.dword(),
|
||||||
|
flags: this.dword() as DdsdFlags,
|
||||||
|
height: this.dword(),
|
||||||
|
width: this.dword(),
|
||||||
|
pitchOrLinearSize: this.dword(),
|
||||||
|
depth: this.dword(),
|
||||||
|
mipMapCount: this.dword(),
|
||||||
|
reserved1: times(11, () => this.dword()),
|
||||||
|
pf: this.pixelFormat(),
|
||||||
|
caps: this.dword() as DdsCaps,
|
||||||
|
caps2: this.dword() as DdsCaps2,
|
||||||
|
caps3: this.dword(),
|
||||||
|
caps4: this.dword(),
|
||||||
|
reserved2: times(1, () => this.dword()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
headerDxt10() {
|
||||||
|
return {
|
||||||
|
dxgiFormat: this.dword() as DxgiFormat,
|
||||||
|
resourceDimension: this.dword() as DdsDimension,
|
||||||
|
miscFlag: this.dword(),
|
||||||
|
arraySize: this.dword(),
|
||||||
|
miscFlags2: this.dword(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
assetFactor4(width: number, height: number) {
|
||||||
|
if (width % 4 !== 0 || height % 4 !== 0) {
|
||||||
|
throw new RangeError('Width and height must be divisible by 4');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iterateBlocks(
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
iterator: (load: {
|
||||||
|
data: Buffer;
|
||||||
|
blockPosX: number;
|
||||||
|
blockPosY: number;
|
||||||
|
}) => void,
|
||||||
|
) {
|
||||||
|
this.assetFactor4(width, height);
|
||||||
|
const data = Buffer.alloc(width * height * 4);
|
||||||
|
const blocksX = width / 4;
|
||||||
|
const blocksY = height / 4;
|
||||||
|
|
||||||
|
times(blocksX * blocksY, (index) => {
|
||||||
|
const blockPosX = (index % blocksX) * 4;
|
||||||
|
const blockPosY = Math.floor(index / blocksX) * 4;
|
||||||
|
|
||||||
|
iterator({ data, blockPosX, blockPosY });
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
channels: 4,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
iteratePixels(
|
||||||
|
width: number,
|
||||||
|
blockPosX: number,
|
||||||
|
blockPosY: number,
|
||||||
|
iterator: (load: { index: number; bufferIndex: number }) => void,
|
||||||
|
) {
|
||||||
|
times(16, (index) => {
|
||||||
|
const pixelPosX = index % 4;
|
||||||
|
const pixelPosY = Math.floor(index / 4);
|
||||||
|
const x = blockPosX + pixelPosX;
|
||||||
|
const y = blockPosY + pixelPosY;
|
||||||
|
const bufferIndex = 4 * (y * width + x);
|
||||||
|
|
||||||
|
iterator({ index, bufferIndex });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bc1ColorInt() {
|
||||||
|
return this.read(2).readUInt16LE();
|
||||||
|
}
|
||||||
|
bc1Color() {
|
||||||
|
return [...this.bc2Color(), 1];
|
||||||
|
}
|
||||||
|
bc1ColorInterpolations() {
|
||||||
|
const int0 = this.bc1ColorInt();
|
||||||
|
const color0 = this.bc1Color();
|
||||||
|
const int1 = this.bc1ColorInt();
|
||||||
|
const color1 = this.bc1Color();
|
||||||
|
const alpha = int0 < int1;
|
||||||
|
const color2 = alpha
|
||||||
|
? color0.map((channel0, index) => (channel0 + color1[index]) / 2)
|
||||||
|
: color0.map((channel0, index) => (2 * channel0 + color1[index]) / 3);
|
||||||
|
const color3 = alpha
|
||||||
|
? [0, 0, 0, 0]
|
||||||
|
: color0.map((channel0, index) => (channel0 + 2 * color1[index]) / 3);
|
||||||
|
|
||||||
|
return {
|
||||||
|
[0b00]: color0,
|
||||||
|
[0b01]: color1,
|
||||||
|
[0b10]: color2,
|
||||||
|
[0b11]: color3,
|
||||||
|
} as Record<number, number[]>;
|
||||||
|
}
|
||||||
|
bc1ColorKeys() {
|
||||||
|
const buffer = this.consume(4);
|
||||||
|
const d = (buffer[0] & 0b11000000) >>> 6;
|
||||||
|
const c = (buffer[0] & 0b110000) >>> 4;
|
||||||
|
const b = (buffer[0] & 0b1100) >>> 2;
|
||||||
|
const a = buffer[0] & 0b11;
|
||||||
|
const h = (buffer[1] & 0b11000000) >>> 6;
|
||||||
|
const g = (buffer[1] & 0b110000) >>> 4;
|
||||||
|
const f = (buffer[1] & 0b1100) >>> 2;
|
||||||
|
const e = buffer[1] & 0b11;
|
||||||
|
const l = (buffer[2] & 0b11000000) >>> 6;
|
||||||
|
const k = (buffer[2] & 0b110000) >>> 4;
|
||||||
|
const j = (buffer[2] & 0b1100) >>> 2;
|
||||||
|
const i = buffer[2] & 0b11;
|
||||||
|
const p = (buffer[3] & 0b11000000) >>> 6;
|
||||||
|
const o = (buffer[3] & 0b110000) >>> 4;
|
||||||
|
const n = (buffer[3] & 0b1100) >>> 2;
|
||||||
|
const m = buffer[3] & 0b11;
|
||||||
|
|
||||||
|
return [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p];
|
||||||
|
}
|
||||||
|
|
||||||
|
bc2AlphaInterpolations() {
|
||||||
|
const alphaBuffer = this.consume(8);
|
||||||
|
|
||||||
|
return times(16, (index) => {
|
||||||
|
const bufferIndex = Math.floor(index / 2);
|
||||||
|
const buffer = alphaBuffer[bufferIndex];
|
||||||
|
const alpha = index % 2 ? buffer & 0b1111 : (buffer & 0b11110000) >>> 4;
|
||||||
|
return alpha / 0xf;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
bc2Color() {
|
||||||
|
const buffer = this.consume(2);
|
||||||
|
|
||||||
|
return [
|
||||||
|
((buffer[1] & 0b11111000) >>> 3) / 0x1f,
|
||||||
|
(((buffer[1] & 0b111) << 3) | ((buffer[0] & 0b11100000) >>> 5)) / 0x3f,
|
||||||
|
(buffer[0] & 0b11111) / 0x1f,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
bc2ColorInterpolations() {
|
||||||
|
const color0 = this.bc1Color();
|
||||||
|
const color1 = this.bc1Color();
|
||||||
|
const color2 = color0.map(
|
||||||
|
(channel0, index) => (2 * channel0 + color1[index]) / 3,
|
||||||
|
);
|
||||||
|
const color3 = color0.map(
|
||||||
|
(channel0, index) => (channel0 + 2 * color1[index]) / 3,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
[0b00]: color0,
|
||||||
|
[0b01]: color1,
|
||||||
|
[0b10]: color2,
|
||||||
|
[0b11]: color3,
|
||||||
|
} as Record<number, number[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
bc3Alpha() {
|
||||||
|
return this.uint8() / 0xff;
|
||||||
|
}
|
||||||
|
bc3AlphaInterpolations() {
|
||||||
|
const a0 = this.bc3Alpha();
|
||||||
|
const a1 = this.bc3Alpha();
|
||||||
|
const full = a0 > a1;
|
||||||
|
const a2 = full ? (6 * a0 + a1) / 7 : (4 * a0 + a1) / 5;
|
||||||
|
const a3 = full ? (5 * a0 + 2 * a1) / 7 : (3 * a0 + 2 * a1) / 5;
|
||||||
|
const a4 = full ? (4 * a0 + 3 * a1) / 7 : (2 * a0 + 3 * a1) / 5;
|
||||||
|
const a5 = full ? (3 * a0 + 4 * a1) / 7 : (1 * a0 + 4 * a1) / 5;
|
||||||
|
const a6 = full ? (2 * a0 + 5 * a1) / 7 : 0;
|
||||||
|
const a7 = full ? (1 * a0 + 6 * a1) / 7 : 255;
|
||||||
|
|
||||||
|
return {
|
||||||
|
[0b000]: a0,
|
||||||
|
[0b001]: a1,
|
||||||
|
[0b010]: a2,
|
||||||
|
[0b011]: a3,
|
||||||
|
[0b100]: a4,
|
||||||
|
[0b101]: a5,
|
||||||
|
[0b110]: a6,
|
||||||
|
[0b111]: a7,
|
||||||
|
} as Record<number, number>;
|
||||||
|
}
|
||||||
|
bc3AlphaKeys() {
|
||||||
|
const buffer = this.consume(6);
|
||||||
|
const h = (buffer[0] & 0b11100000) >>> 5;
|
||||||
|
const g = (buffer[0] & 0b11100) >>> 2;
|
||||||
|
const f = ((buffer[0] & 0b11) << 1) | ((buffer[1] & 0b10000000) >>> 7);
|
||||||
|
const e = (buffer[1] & 0b1110000) >>> 4;
|
||||||
|
const d = (buffer[1] & 0b1110) >>> 1;
|
||||||
|
const c = ((buffer[1] & 0b1) << 2) | ((buffer[2] & 0b11000000) >>> 6);
|
||||||
|
const b = (buffer[2] & 0b111000) >>> 3;
|
||||||
|
const a = buffer[2] & 0b111;
|
||||||
|
const p = (buffer[1] & 0b11100000) >>> 5;
|
||||||
|
const o = (buffer[1] & 0b11100) >>> 2;
|
||||||
|
const n = ((buffer[1] & 0b11) << 1) | ((buffer[2] & 0b10000000) >>> 7);
|
||||||
|
const m = (buffer[2] & 0b1110000) >>> 4;
|
||||||
|
const l = (buffer[2] & 0b1110) >>> 1;
|
||||||
|
const k = ((buffer[2] & 0b1) << 2) | ((buffer[3] & 0b11000000) >>> 6);
|
||||||
|
const j = (buffer[3] & 0b111000) >>> 3;
|
||||||
|
const i = buffer[3] & 0b111;
|
||||||
|
|
||||||
|
return [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p];
|
||||||
|
}
|
||||||
|
|
||||||
async dds() {
|
async dds() {
|
||||||
this.magicNumber();
|
this.magicNumber();
|
||||||
const header = this.header();
|
const header = this.header();
|
||||||
|
|
||||||
// if (header.pf.flags !== DdpfFlags.FOURCC) {
|
|
||||||
// throw new TypeError(
|
|
||||||
// `fourCC flag must be present, found ${DdsdFlags[header.pf.flags]}`,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
let dxgiFormat: DxgiFormat;
|
let dxgiFormat: DxgiFormat;
|
||||||
|
|
||||||
switch (header.pf.fourCC) {
|
switch (header.pf.fourCC) {
|
||||||
@@ -208,127 +432,97 @@ export class DdsStream extends WindowsStream {
|
|||||||
case DxgiFormat.BC1_TYPELESS:
|
case DxgiFormat.BC1_TYPELESS:
|
||||||
case DxgiFormat.BC1_UNORM:
|
case DxgiFormat.BC1_UNORM:
|
||||||
case DxgiFormat.BC1_UNORM_SRGB: {
|
case DxgiFormat.BC1_UNORM_SRGB: {
|
||||||
this.assetFactor4(header.width, header.height);
|
return this.iterateBlocks(
|
||||||
const data = Buffer.alloc(header.width * header.height * 4);
|
header.width,
|
||||||
const blocksX = header.width / 4;
|
header.height,
|
||||||
const blocksY = header.height / 4;
|
({ data, blockPosX, blockPosY }) => {
|
||||||
|
const colorInterpolations = this.bc1ColorInterpolations();
|
||||||
|
const colorKeys = this.bc1ColorKeys();
|
||||||
|
|
||||||
times(blocksX * blocksY, (blockIndex) => {
|
this.iteratePixels(
|
||||||
const blockPosX = (blockIndex % blocksX) * 4;
|
header.width,
|
||||||
const blockPosY = Math.floor(blockIndex / blocksX) * 4;
|
blockPosX,
|
||||||
const interpolations = this.bc1ColorInterpolations();
|
blockPosY,
|
||||||
const indices = this.bc1ColorIndices();
|
({ index, bufferIndex }) => {
|
||||||
|
const colorKey = colorKeys[index];
|
||||||
|
const color = colorInterpolations[colorKey].map((standard) =>
|
||||||
|
Math.round(255 * standard),
|
||||||
|
);
|
||||||
|
|
||||||
indices.forEach((index, indexIndex) => {
|
data[bufferIndex] = color[0];
|
||||||
let pixelPosX = indexIndex % 4;
|
data[bufferIndex + 1] = color[1];
|
||||||
let pixelPosY = Math.floor(indexIndex / 4);
|
data[bufferIndex + 2] = color[2];
|
||||||
let x = blockPosX + pixelPosX;
|
data[bufferIndex + 3] = color[3];
|
||||||
let y = blockPosY + pixelPosY;
|
},
|
||||||
|
|
||||||
const bufferIndex = 4 * (y * header.width + x);
|
|
||||||
const color = interpolations[index].map((standard) =>
|
|
||||||
Math.round(255 * standard),
|
|
||||||
);
|
);
|
||||||
|
},
|
||||||
data[bufferIndex] = color[0];
|
);
|
||||||
data[bufferIndex + 1] = color[1];
|
|
||||||
data[bufferIndex + 2] = color[2];
|
|
||||||
data[bufferIndex + 3] = color[3];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
width: header.width,
|
|
||||||
height: header.height,
|
|
||||||
channels: 4 as const,
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case DxgiFormat.BC2_TYPELESS:
|
case DxgiFormat.BC2_TYPELESS:
|
||||||
case DxgiFormat.BC2_UNORM:
|
case DxgiFormat.BC2_UNORM:
|
||||||
case DxgiFormat.BC2_UNORM_SRGB: {
|
case DxgiFormat.BC2_UNORM_SRGB: {
|
||||||
this.assetFactor4(header.width, header.height);
|
return this.iterateBlocks(
|
||||||
const data = Buffer.alloc(header.width * header.height * 4);
|
header.width,
|
||||||
const blocksX = header.width / 4;
|
header.height,
|
||||||
const blocksY = header.height / 4;
|
({ data, blockPosX, blockPosY }) => {
|
||||||
|
const alphaInterpolations = this.bc2AlphaInterpolations();
|
||||||
|
const colorInterpolations = this.bc2ColorInterpolations();
|
||||||
|
const colorKeys = this.bc1ColorKeys();
|
||||||
|
|
||||||
times(blocksX * blocksY, (blockIndex) => {
|
this.iteratePixels(
|
||||||
const blockPosX = (blockIndex % blocksX) * 4;
|
header.width,
|
||||||
const blockPosY = Math.floor(blockIndex / blocksX) * 4;
|
blockPosX,
|
||||||
const alphas = this.bc2Alphas();
|
blockPosY,
|
||||||
const interpolations = this.bc2ColorInterpolations();
|
({ index, bufferIndex }) => {
|
||||||
const indices = this.bc1ColorIndices();
|
const colorKey = colorKeys[index];
|
||||||
|
const color = colorInterpolations[colorKey].map((standard) =>
|
||||||
|
Math.round(255 * standard),
|
||||||
|
);
|
||||||
|
const alpha = alphaInterpolations[colorKey] * 255;
|
||||||
|
|
||||||
indices.forEach((index, indexIndex) => {
|
data[bufferIndex] = color[0];
|
||||||
let pixelPosX = indexIndex % 4;
|
data[bufferIndex + 1] = color[1];
|
||||||
let pixelPosY = Math.floor(indexIndex / 4);
|
data[bufferIndex + 2] = color[2];
|
||||||
let x = blockPosX + pixelPosX;
|
data[bufferIndex + 3] = alpha;
|
||||||
let y = blockPosY + pixelPosY;
|
},
|
||||||
|
|
||||||
const bufferIndex = 4 * (y * header.width + x);
|
|
||||||
const color = interpolations[index].map((standard) =>
|
|
||||||
Math.round(255 * standard),
|
|
||||||
);
|
);
|
||||||
const alpha = alphas[index] * 255;
|
},
|
||||||
|
);
|
||||||
data[bufferIndex] = color[0];
|
|
||||||
data[bufferIndex + 1] = color[1];
|
|
||||||
data[bufferIndex + 2] = color[2];
|
|
||||||
data[bufferIndex + 3] = alpha;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
width: header.width,
|
|
||||||
height: header.height,
|
|
||||||
channels: 4 as const,
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case DxgiFormat.BC3_TYPELESS:
|
case DxgiFormat.BC3_TYPELESS:
|
||||||
case DxgiFormat.BC3_UNORM:
|
case DxgiFormat.BC3_UNORM:
|
||||||
case DxgiFormat.BC3_UNORM_SRGB: {
|
case DxgiFormat.BC3_UNORM_SRGB: {
|
||||||
this.assetFactor4(header.width, header.height);
|
return this.iterateBlocks(
|
||||||
const data = Buffer.alloc(header.width * header.height * 4);
|
header.width,
|
||||||
const blocksX = header.width / 4;
|
header.height,
|
||||||
const blocksY = header.height / 4;
|
({ data, blockPosX, blockPosY }) => {
|
||||||
|
const alphaInterpolations = this.bc3AlphaInterpolations();
|
||||||
|
const alphaKeys = this.bc3AlphaKeys();
|
||||||
|
const colorInterpolations = this.bc2ColorInterpolations();
|
||||||
|
const colorKeys = this.bc1ColorKeys();
|
||||||
|
|
||||||
times(blocksX * blocksY, (blockIndex) => {
|
this.iteratePixels(
|
||||||
const blockPosX = (blockIndex % blocksX) * 4;
|
header.width,
|
||||||
const blockPosY = Math.floor(blockIndex / blocksX) * 4;
|
blockPosX,
|
||||||
const alphaInterpolations = this.bc3AlphaInterpolations();
|
blockPosY,
|
||||||
const alphaIndices = this.bc3AlphaIndices();
|
({ index, bufferIndex }) => {
|
||||||
const colorInterpolations = this.bc2ColorInterpolations();
|
const colorKey = colorKeys[index];
|
||||||
const colorIndices = this.bc1ColorIndices();
|
const alphaKey = alphaKeys[index];
|
||||||
|
const color = colorInterpolations[colorKey].map((standard) =>
|
||||||
|
Math.round(255 * standard),
|
||||||
|
);
|
||||||
|
const alpha = alphaInterpolations[alphaKey] * 255;
|
||||||
|
|
||||||
colorIndices.forEach((colorIndex, colorIndexIndex) => {
|
data[bufferIndex] = color[0];
|
||||||
const alphaIndex = alphaIndices[colorIndexIndex];
|
data[bufferIndex + 1] = color[1];
|
||||||
let pixelPosX = colorIndexIndex % 4;
|
data[bufferIndex + 2] = color[2];
|
||||||
let pixelPosY = Math.floor(colorIndexIndex / 4);
|
data[bufferIndex + 3] = alpha;
|
||||||
let x = blockPosX + pixelPosX;
|
},
|
||||||
let y = blockPosY + pixelPosY;
|
|
||||||
|
|
||||||
const bufferIndex = 4 * (y * header.width + x);
|
|
||||||
const color = colorInterpolations[colorIndex].map((standard) =>
|
|
||||||
Math.round(255 * standard),
|
|
||||||
);
|
);
|
||||||
const alpha = alphaInterpolations[alphaIndex] * 255;
|
},
|
||||||
|
);
|
||||||
data[bufferIndex] = color[0];
|
|
||||||
data[bufferIndex + 1] = color[1];
|
|
||||||
data[bufferIndex + 2] = color[2];
|
|
||||||
data[bufferIndex + 3] = alpha;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
width: header.width,
|
|
||||||
height: header.height,
|
|
||||||
channels: 4 as const,
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -337,207 +531,4 @@ export class DdsStream extends WindowsStream {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bc3AlphaIndices() {
|
|
||||||
const buffer = this.consume(6);
|
|
||||||
const h = (buffer[0] & 0b11100000) >>> 5;
|
|
||||||
const g = (buffer[0] & 0b11100) >>> 2;
|
|
||||||
const f = ((buffer[0] & 0b11) << 1) | ((buffer[1] & 0b10000000) >>> 7);
|
|
||||||
const e = (buffer[1] & 0b1110000) >>> 4;
|
|
||||||
const d = (buffer[1] & 0b1110) >>> 1;
|
|
||||||
const c = ((buffer[1] & 0b1) << 2) | ((buffer[2] & 0b11000000) >>> 6);
|
|
||||||
const b = (buffer[2] & 0b111000) >>> 3;
|
|
||||||
const a = buffer[2] & 0b111;
|
|
||||||
const p = (buffer[1] & 0b11100000) >>> 5;
|
|
||||||
const o = (buffer[1] & 0b11100) >>> 2;
|
|
||||||
const n = ((buffer[1] & 0b11) << 1) | ((buffer[2] & 0b10000000) >>> 7);
|
|
||||||
const m = (buffer[2] & 0b1110000) >>> 4;
|
|
||||||
const l = (buffer[2] & 0b1110) >>> 1;
|
|
||||||
const k = ((buffer[2] & 0b1) << 2) | ((buffer[3] & 0b11000000) >>> 6);
|
|
||||||
const j = (buffer[3] & 0b111000) >>> 3;
|
|
||||||
const i = buffer[3] & 0b111;
|
|
||||||
|
|
||||||
return [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p];
|
|
||||||
}
|
|
||||||
|
|
||||||
bc3AlphaInterpolations() {
|
|
||||||
const a0 = this.bc3Alpha();
|
|
||||||
const a1 = this.bc3Alpha();
|
|
||||||
const full = a0 > a1;
|
|
||||||
const a2 = full ? (6 * a0 + a1) / 7 : (4 * a0 + a1) / 5;
|
|
||||||
const a3 = full ? (5 * a0 + 2 * a1) / 7 : (3 * a0 + 2 * a1) / 5;
|
|
||||||
const a4 = full ? (4 * a0 + 3 * a1) / 7 : (2 * a0 + 3 * a1) / 5;
|
|
||||||
const a5 = full ? (3 * a0 + 4 * a1) / 7 : (1 * a0 + 4 * a1) / 5;
|
|
||||||
const a6 = full ? (2 * a0 + 5 * a1) / 7 : 0;
|
|
||||||
const a7 = full ? (1 * a0 + 6 * a1) / 7 : 255;
|
|
||||||
|
|
||||||
return {
|
|
||||||
[0b000]: a0,
|
|
||||||
[0b001]: a1,
|
|
||||||
[0b010]: a2,
|
|
||||||
[0b011]: a3,
|
|
||||||
[0b100]: a4,
|
|
||||||
[0b101]: a5,
|
|
||||||
[0b110]: a6,
|
|
||||||
[0b111]: a7,
|
|
||||||
} as Record<number, number>;
|
|
||||||
}
|
|
||||||
|
|
||||||
bc3Alpha() {
|
|
||||||
return this.uint8() / 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
bc2Alphas() {
|
|
||||||
const alphaBuffer = this.consume(8);
|
|
||||||
|
|
||||||
return times(16, (index) => {
|
|
||||||
const bufferIndex = Math.floor(index / 2);
|
|
||||||
const buffer = alphaBuffer[bufferIndex];
|
|
||||||
const alpha = index % 2 ? buffer & 0b1111 : (buffer & 0b11110000) >>> 4;
|
|
||||||
return alpha / 0xf;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
assetFactor4(width: number, height: number) {
|
|
||||||
if (width % 4 !== 0 || height % 4 !== 0) {
|
|
||||||
throw new RangeError('Width and height must be divisible by 4');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bc2Color() {
|
|
||||||
const buffer = this.consume(2);
|
|
||||||
|
|
||||||
return [
|
|
||||||
((buffer[1] & 0b11111000) >>> 3) / 0x1f,
|
|
||||||
(((buffer[1] & 0b111) << 3) | ((buffer[0] & 0b11100000) >>> 5)) / 0x3f,
|
|
||||||
(buffer[0] & 0b11111) / 0x1f,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
bc1Color() {
|
|
||||||
return [...this.bc2Color(), 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
bc1ColorInt() {
|
|
||||||
return this.read(2).readUInt16LE();
|
|
||||||
}
|
|
||||||
|
|
||||||
bc2ColorInterpolations() {
|
|
||||||
const color0 = this.bc1Color();
|
|
||||||
const color1 = this.bc1Color();
|
|
||||||
const color2 = color0.map(
|
|
||||||
(channel0, index) => (2 * channel0 + color1[index]) / 3,
|
|
||||||
);
|
|
||||||
const color3 = color0.map(
|
|
||||||
(channel0, index) => (channel0 + 2 * color1[index]) / 3,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
[0b00]: color0,
|
|
||||||
[0b01]: color1,
|
|
||||||
[0b10]: color2,
|
|
||||||
[0b11]: color3,
|
|
||||||
} as Record<number, number[]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
bc1ColorInterpolations() {
|
|
||||||
const int0 = this.bc1ColorInt();
|
|
||||||
const color0 = this.bc1Color();
|
|
||||||
const int1 = this.bc1ColorInt();
|
|
||||||
const color1 = this.bc1Color();
|
|
||||||
const alpha = int0 < int1;
|
|
||||||
const color2 = alpha
|
|
||||||
? color0.map((channel0, index) => (channel0 + color1[index]) / 2)
|
|
||||||
: color0.map((channel0, index) => (2 * channel0 + color1[index]) / 3);
|
|
||||||
const color3 = alpha
|
|
||||||
? [0, 0, 0, 0]
|
|
||||||
: color0.map((channel0, index) => (channel0 + 2 * color1[index]) / 3);
|
|
||||||
|
|
||||||
return {
|
|
||||||
[0b00]: color0,
|
|
||||||
[0b01]: color1,
|
|
||||||
[0b10]: color2,
|
|
||||||
[0b11]: color3,
|
|
||||||
} as Record<number, number[]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
bc1ColorIndices() {
|
|
||||||
const buffer = this.consume(4);
|
|
||||||
const d = (buffer[0] & 0b11000000) >>> 6;
|
|
||||||
const c = (buffer[0] & 0b110000) >>> 4;
|
|
||||||
const b = (buffer[0] & 0b1100) >>> 2;
|
|
||||||
const a = buffer[0] & 0b11;
|
|
||||||
const h = (buffer[1] & 0b11000000) >>> 6;
|
|
||||||
const g = (buffer[1] & 0b110000) >>> 4;
|
|
||||||
const f = (buffer[1] & 0b1100) >>> 2;
|
|
||||||
const e = buffer[1] & 0b11;
|
|
||||||
const l = (buffer[2] & 0b11000000) >>> 6;
|
|
||||||
const k = (buffer[2] & 0b110000) >>> 4;
|
|
||||||
const j = (buffer[2] & 0b1100) >>> 2;
|
|
||||||
const i = buffer[2] & 0b11;
|
|
||||||
const p = (buffer[3] & 0b11000000) >>> 6;
|
|
||||||
const o = (buffer[3] & 0b110000) >>> 4;
|
|
||||||
const n = (buffer[3] & 0b1100) >>> 2;
|
|
||||||
const m = buffer[3] & 0b11;
|
|
||||||
|
|
||||||
return [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p];
|
|
||||||
}
|
|
||||||
|
|
||||||
magicNumber() {
|
|
||||||
const magic = this.dword();
|
|
||||||
|
|
||||||
if (magic !== 0x20534444) {
|
|
||||||
throw new Error(`Invalid DDS magic number: 0x${magic.toString(16)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return magic;
|
|
||||||
}
|
|
||||||
|
|
||||||
header() {
|
|
||||||
return {
|
|
||||||
size: this.dword(),
|
|
||||||
flags: this.dword() as DdsdFlags,
|
|
||||||
height: this.dword(),
|
|
||||||
width: this.dword(),
|
|
||||||
pitchOrLinearSize: this.dword(),
|
|
||||||
depth: this.dword(),
|
|
||||||
mipMapCount: this.dword(),
|
|
||||||
reserved1: times(11, () => this.dword()),
|
|
||||||
pf: this.pixelFormat(),
|
|
||||||
caps: this.dword() as DdsCaps,
|
|
||||||
caps2: this.dword() as DdsCaps2,
|
|
||||||
caps3: this.dword(),
|
|
||||||
caps4: this.dword(),
|
|
||||||
reserved2: times(1, () => this.dword()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pixelFormat() {
|
|
||||||
return {
|
|
||||||
size: this.dword(),
|
|
||||||
flags: this.dword() as DdpfFlags,
|
|
||||||
fourCC: this.ascii(4),
|
|
||||||
rgbBitCount: this.dword(),
|
|
||||||
rBitMask: this.dword(),
|
|
||||||
gBitMask: this.dword(),
|
|
||||||
bBitMask: this.dword(),
|
|
||||||
aBitMask: this.dword(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
headerDxt10() {
|
|
||||||
return {
|
|
||||||
dxgiFormat: this.dword() as DxgiFormat,
|
|
||||||
resourceDimension: this.dword() as DdsDimension,
|
|
||||||
miscFlag: this.dword(),
|
|
||||||
arraySize: this.dword(),
|
|
||||||
miscFlags2: this.dword(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new DdsStream(
|
|
||||||
await readDVPLFile(
|
|
||||||
'C:/Program Files (x86)/Steam/steamapps/common/World of Tanks Blitz/Data/3d/Tanks/GB/images_pbr/Chieftain_Mk6_BC.dx11.dds.dvpl',
|
|
||||||
),
|
|
||||||
).dds();
|
|
||||||
Reference in new issue
Block a user