mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
feat: dds decompression
This commit is contained in:
1 parent
6361b09129
commit
4ffb7cac7c
6 files changed
+164
-17
No files matched your search
@@ -519,9 +519,9 @@ if (allTargets || targets?.includes('tankModels')) {
|
||||
const nationVehicleId = tank.id;
|
||||
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
|
||||
|
||||
// if (id !== 15697) continue; // chieftain TODO: investigate vertices stream over read
|
||||
if (id !== 15697) continue; // chieftain TODO: investigate vertices stream over read
|
||||
// if (id !== 4417) continue; // amx m4 mle
|
||||
if (id !== 7297) continue; // 60tp
|
||||
// if (id !== 7297) continue; // 60tp
|
||||
console.log(`Building model ${id} @ ${nation}/${tankIndex}`);
|
||||
|
||||
const parameters = await readYAMLDVPL<TankParameters>(
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { times } from 'lodash';
|
||||
import { Vector4Tuple } from 'three';
|
||||
import { BinaryStream } from './binary';
|
||||
|
||||
const MAX_UINT5 = 2 ** 5 - 1;
|
||||
const MAX_UINT6 = 2 ** 6 - 1;
|
||||
|
||||
type InterpolationKey = 0b00 | 0b01 | 0b10 | 0b11;
|
||||
|
||||
export class Bc1Stream extends BinaryStream {
|
||||
color() {
|
||||
const r = this.consume(5) / MAX_UINT5;
|
||||
const g = this.consume(6) / MAX_UINT6;
|
||||
const b = this.consume(5) / MAX_UINT5;
|
||||
|
||||
return [r, g, b, 1];
|
||||
}
|
||||
|
||||
interpolation() {
|
||||
const color0 = this.color();
|
||||
const color1 = this.color();
|
||||
const color2 = color0.map(
|
||||
(channel0, index) => (channel0 + color1[index]) / 2,
|
||||
);
|
||||
const color3 = [...color2.slice(0, -1), 0];
|
||||
|
||||
return {
|
||||
[0b00]: color0,
|
||||
[0b01]: color1,
|
||||
[0b10]: color2,
|
||||
[0b11]: color3,
|
||||
};
|
||||
}
|
||||
|
||||
blockFloat() {
|
||||
const interpolation = this.interpolation();
|
||||
return times(16, () => interpolation[this.consume(2) as InterpolationKey]);
|
||||
}
|
||||
|
||||
block() {
|
||||
return this.blockFloat().map(
|
||||
(pixel) =>
|
||||
pixel.map((channel) => Math.round(channel * 255)) as Vector4Tuple,
|
||||
);
|
||||
}
|
||||
|
||||
blocks(count: number) {
|
||||
return times(count, () => this.block());
|
||||
}
|
||||
|
||||
bc1(width: number, height: number) {
|
||||
if (width % 4 !== 0 || height % 4 !== 0) {
|
||||
throw new RangeError('Width and height must be divisible by 4');
|
||||
}
|
||||
|
||||
const count = (width / 4) * (height / 4);
|
||||
const blocks = this.blocks(count);
|
||||
const image = Buffer.alloc(width * height * 4);
|
||||
let pixelIndex = 0;
|
||||
|
||||
for (let y = 0; y < width; y++) {
|
||||
const yOffset = Math.floor(y / 4) * (width / 4);
|
||||
|
||||
for (let x = 0; x < height; x++) {
|
||||
const xOffset = Math.floor(x / 4);
|
||||
const block = blocks[yOffset + xOffset];
|
||||
const pixel = block[Math.floor(y % 4) * 4 + Math.floor(x % 4)];
|
||||
|
||||
pixel.forEach((channel) => (image[pixelIndex++] = channel));
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
type Bit = 0 | 1;
|
||||
|
||||
export class BinaryStream {
|
||||
public index = 0;
|
||||
bits: Bit[] = [];
|
||||
|
||||
constructor(buffer: Buffer) {
|
||||
for (let bufferIndex = 0; bufferIndex < buffer.length; bufferIndex++) {
|
||||
const byte = buffer[bufferIndex];
|
||||
|
||||
for (let byteIndex = 7; byteIndex >= 0; byteIndex--) {
|
||||
this.bits.push(((byte >> byteIndex) & 1) as Bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skip(size: number) {
|
||||
this.index += size;
|
||||
}
|
||||
read(size: number) {
|
||||
if (this.index + size > this.bits.length) {
|
||||
throw new RangeError('Cannot read beyond end of stream');
|
||||
}
|
||||
|
||||
return this.bits
|
||||
.slice(this.index, this.index + size)
|
||||
.reduce((accumulator, bit) => (accumulator << 1) | bit, 0 as number);
|
||||
}
|
||||
consume(size: number) {
|
||||
const value = this.read(size);
|
||||
this.skip(size);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+47
-10
@@ -1,5 +1,8 @@
|
||||
import { writeFileSync } from 'fs';
|
||||
import { times } from 'lodash';
|
||||
import sharp from 'sharp';
|
||||
import { readDVPLFile } from '../blitz/readDVPLFile';
|
||||
import { Bc1Stream } from './bc1';
|
||||
import { WindowsStream } from './windows';
|
||||
|
||||
enum DxgiFormat {
|
||||
@@ -127,7 +130,7 @@ enum DxgiFormat {
|
||||
FORCE_UINT = 0xffffffff,
|
||||
}
|
||||
|
||||
enum D3d10ResourceDimension {
|
||||
enum DdsDimension {
|
||||
UNKNOWN = 0,
|
||||
BUFFER = 1,
|
||||
TEXTURE1D = 2,
|
||||
@@ -172,13 +175,48 @@ enum DdsCaps2 {
|
||||
}
|
||||
|
||||
export class DdsStream extends WindowsStream {
|
||||
dds() {
|
||||
const magicNumber = this.magicNumber();
|
||||
async dds() {
|
||||
this.magicNumber();
|
||||
const header = this.header();
|
||||
const headerDxt10 =
|
||||
header.pf.flags === DdpfFlags.FOURCC && header.pf.fourCC === 0x30315844
|
||||
? this.headerDxt10()
|
||||
: undefined;
|
||||
|
||||
if (
|
||||
header.pf.flags !== DdpfFlags.FOURCC ||
|
||||
header.pf.fourCC !== 0x30315844 // "DX10"
|
||||
) {
|
||||
throw new RangeError('No other formats other than DX10 are supported');
|
||||
}
|
||||
|
||||
const headerDxt10 = this.headerDxt10();
|
||||
|
||||
switch (headerDxt10.dxgiFormat) {
|
||||
case DxgiFormat.BC1_UNORM_SRGB: {
|
||||
const image = new Bc1Stream(
|
||||
this.consume((header.width / 4) * (header.height / 4) * 8),
|
||||
).bc1(header.width, header.height);
|
||||
|
||||
writeFileSync(
|
||||
'test.png',
|
||||
await sharp(image, {
|
||||
raw: {
|
||||
channels: 4,
|
||||
width: header.width,
|
||||
height: header.height,
|
||||
},
|
||||
})
|
||||
.png()
|
||||
.toBuffer(),
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new TypeError(
|
||||
`Unsupported DXGI format: ${DxgiFormat[headerDxt10.dxgiFormat]} (${
|
||||
headerDxt10.dxgiFormat
|
||||
})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
magicNumber() {
|
||||
@@ -201,7 +239,6 @@ export class DdsStream extends WindowsStream {
|
||||
caps3: this.dword(),
|
||||
caps4: this.dword(),
|
||||
reserved2: times(1, () => this.dword()),
|
||||
headerDxt10: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -221,7 +258,7 @@ export class DdsStream extends WindowsStream {
|
||||
headerDxt10() {
|
||||
return {
|
||||
dxgiFormat: this.uint() as DxgiFormat,
|
||||
resourceDimension: this.uint() as D3d10ResourceDimension,
|
||||
resourceDimension: this.uint() as DdsDimension,
|
||||
miscFlag: this.uint(),
|
||||
arraySize: this.uint(),
|
||||
miscFlags2: this.uint(),
|
||||
@@ -231,6 +268,6 @@ export class DdsStream extends WindowsStream {
|
||||
|
||||
new DdsStream(
|
||||
await readDVPLFile(
|
||||
'C:/Program Files (x86)/Steam/steamapps/common/World of Tanks Blitz/Data/3d/Tanks/German/images_pbr/E_100_BC.dx11.dds.dvpl',
|
||||
'C:/Program Files (x86)/Steam/steamapps/common/World of Tanks Blitz/Data/3d/Tanks/GB/images_pbr/Chieftain_Mk6_BC.dx11.dds.dvpl',
|
||||
),
|
||||
).dds();
|
||||
@@ -1,12 +1,12 @@
|
||||
export class PrimitiveStream {
|
||||
public index = 0;
|
||||
constructor(public buffer: Buffer) {}
|
||||
constructor(public stream: Buffer) {}
|
||||
|
||||
skip(size: number) {
|
||||
this.index += size;
|
||||
}
|
||||
read(size: number) {
|
||||
return this.buffer.subarray(this.index, this.index + size);
|
||||
return this.stream.subarray(this.index, this.index + size);
|
||||
}
|
||||
consume(size: number) {
|
||||
const subarray = this.read(size);
|
||||
@@ -19,7 +19,7 @@ export class PrimitiveStream {
|
||||
return this.read(Number.POSITIVE_INFINITY);
|
||||
}
|
||||
readRemainingLength() {
|
||||
return this.buffer.length - this.index;
|
||||
return this.stream.length - this.index;
|
||||
}
|
||||
consumeRemaining() {
|
||||
return this.consume(Number.POSITIVE_INFINITY);
|
||||
|
||||
@@ -84,9 +84,9 @@ export class ScgStream extends ScpgStream {
|
||||
ScgStream.vertexStride(polygonGroupRaw.vertexFormat) *
|
||||
polygonGroupRaw.vertexCount;
|
||||
|
||||
if (verticesStream.buffer.length !== stride) {
|
||||
if (verticesStream.stream.length !== stride) {
|
||||
console.warn(
|
||||
`Vertex stride mismatch; expected ${stride}, got ${verticesStream.buffer.length}`,
|
||||
`Vertex stride mismatch; expected ${stride}, got ${verticesStream.stream.length}`,
|
||||
);
|
||||
|
||||
return;
|
||||
|
||||
Reference in new issue
Block a user