2024-02-04 17:55:48 -06:00
|
|
|
import { existsSync } from 'fs';
|
|
|
|
|
import sharp from 'sharp';
|
|
|
|
|
import { Vector3Tuple } from 'three';
|
|
|
|
|
import { readDVPLFile } from '../blitz/readDVPLFile';
|
|
|
|
|
import { DdsReadStream } from '../streams/dds';
|
|
|
|
|
import { PvrReadStream } from '../streams/pvr';
|
2023-12-22 15:59:06 -06:00
|
|
|
|
2023-12-28 20:58:18 -06:00
|
|
|
export enum TextureMutation {
|
|
|
|
|
Normal,
|
|
|
|
|
RoughnessMetallicness,
|
|
|
|
|
BaseColor,
|
2024-01-29 15:27:21 -06:00
|
|
|
Miscellaneous,
|
2023-12-28 20:58:18 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ReadTextureOptions =
|
|
|
|
|
| {
|
2024-01-29 15:27:21 -06:00
|
|
|
mutation:
|
|
|
|
|
| TextureMutation.Normal
|
|
|
|
|
| TextureMutation.RoughnessMetallicness
|
|
|
|
|
| TextureMutation.Miscellaneous;
|
2023-12-28 20:58:18 -06:00
|
|
|
}
|
|
|
|
|
| {
|
|
|
|
|
mutation: TextureMutation.BaseColor;
|
|
|
|
|
/**
|
|
|
|
|
* Normalized between 0 and 1
|
|
|
|
|
*/
|
|
|
|
|
baseColor: Vector3Tuple;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export async function readTexture(path: string, options?: ReadTextureOptions) {
|
2024-02-04 17:55:48 -06:00
|
|
|
const ddsTexturePath = path.replace('.tex', '.dx11.dds.dvpl');
|
2023-12-29 19:56:50 -06:00
|
|
|
const isDds = existsSync(ddsTexturePath);
|
|
|
|
|
const resolvedTexturePath = isDds
|
|
|
|
|
? ddsTexturePath
|
2024-02-04 17:55:48 -06:00
|
|
|
: ddsTexturePath.replace('.dds', '.pvr');
|
2023-12-29 19:56:50 -06:00
|
|
|
const decompressedDvpl = await readDVPLFile(resolvedTexturePath);
|
2023-12-30 11:44:42 -06:00
|
|
|
|
2023-12-29 19:56:50 -06:00
|
|
|
const raw = isDds
|
2024-02-04 17:55:48 -06:00
|
|
|
? await new DdsReadStream(decompressedDvpl).dds()
|
|
|
|
|
: new PvrReadStream(decompressedDvpl).pvr();
|
2023-12-29 19:56:50 -06:00
|
|
|
const channels = 4 * raw.width * raw.height;
|
2023-12-28 13:11:52 -06:00
|
|
|
|
2023-12-28 20:58:18 -06:00
|
|
|
switch (options?.mutation) {
|
|
|
|
|
case TextureMutation.Normal: {
|
|
|
|
|
for (let index = 0; index < channels; index += 4) {
|
2023-12-29 19:56:50 -06:00
|
|
|
/**
|
|
|
|
|
* Red is always 255 and blue is always 0. Only alpha and green contain any
|
|
|
|
|
* sort of information.
|
|
|
|
|
*/
|
|
|
|
|
let x = raw.data[index + 3] * (2 / 255) - 1;
|
|
|
|
|
let y = raw.data[index + 1] * (2 / 255) - 1;
|
2023-12-29 12:41:21 -06:00
|
|
|
const underSqrt = 1 - x ** 2 - y ** 2;
|
|
|
|
|
let z = underSqrt > 0 ? Math.sqrt(underSqrt) : -Math.sqrt(-underSqrt);
|
2023-12-28 20:58:18 -06:00
|
|
|
|
2023-12-29 19:56:50 -06:00
|
|
|
raw.data[index] = Math.round((x + 1) * (255 / 2));
|
|
|
|
|
raw.data[index + 1] = Math.round((y + 1) * (255 / 2));
|
|
|
|
|
raw.data[index + 2] = Math.round((z + 1) * (255 / 2));
|
|
|
|
|
raw.data[index + 3] = 255;
|
2023-12-28 20:58:18 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TextureMutation.RoughnessMetallicness: {
|
|
|
|
|
for (let index = 0; index < channels; index += 4) {
|
2023-12-29 19:56:50 -06:00
|
|
|
/**
|
|
|
|
|
* Same channel situation as normal maps.
|
|
|
|
|
*/
|
|
|
|
|
const metallicness = raw.data[index + 1];
|
|
|
|
|
const roughness = raw.data[index + 3];
|
2023-12-28 20:58:18 -06:00
|
|
|
|
2023-12-29 19:56:50 -06:00
|
|
|
raw.data[index] = 0;
|
|
|
|
|
raw.data[index + 1] = roughness;
|
|
|
|
|
raw.data[index + 2] = metallicness;
|
|
|
|
|
raw.data[index + 3] = 255;
|
2023-12-28 20:58:18 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-02 09:46:24 -06:00
|
|
|
case TextureMutation.Miscellaneous: {
|
2024-01-29 15:27:21 -06:00
|
|
|
for (let index = 0; index < channels; index += 4) {
|
|
|
|
|
/**
|
|
|
|
|
* Green is ambient occlusion and alpha is emissive. But very few tanks
|
|
|
|
|
* use emissive so I am ignoring it for now.
|
|
|
|
|
*/
|
|
|
|
|
const occlusion = raw.data[index + 1];
|
|
|
|
|
// const emissive = raw.data[index + 3];
|
|
|
|
|
|
|
|
|
|
raw.data[index] = occlusion;
|
|
|
|
|
raw.data[index + 1] = 0;
|
|
|
|
|
raw.data[index + 2] = 0;
|
|
|
|
|
raw.data[index + 3] = 255;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-28 20:58:18 -06:00
|
|
|
case TextureMutation.BaseColor: {
|
|
|
|
|
for (let index = 0; index < channels; index += 4) {
|
2024-01-29 15:27:21 -06:00
|
|
|
// alpha map is specularity but I am ignoring it for now
|
2023-12-29 19:56:50 -06:00
|
|
|
raw.data[index + 3] = 255;
|
2023-12-28 20:58:18 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-29 19:56:50 -06:00
|
|
|
return await sharp(raw.data, { raw: raw }).png().toBuffer();
|
2023-12-22 15:59:06 -06:00
|
|
|
}
|