mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: use PVRTexToolCLI
This commit is contained in:
1 parent
4ad10deb00
commit
0627f678ea
4 files changed
+99
-107
No files matched your search
Vendored
+1
@@ -38,6 +38,7 @@
|
|||||||
"leaderboards",
|
"leaderboards",
|
||||||
"MURICA",
|
"MURICA",
|
||||||
"nmatname",
|
"nmatname",
|
||||||
|
"noout",
|
||||||
"normalmap",
|
"normalmap",
|
||||||
"Oopsy",
|
"Oopsy",
|
||||||
"outdir",
|
"outdir",
|
||||||
|
|||||||
+60
-40
@@ -3,7 +3,7 @@ import { config } from 'dotenv';
|
|||||||
import { existsSync, writeFileSync } from 'fs';
|
import { existsSync, writeFileSync } from 'fs';
|
||||||
import { mkdir, readdir, rm, writeFile } from 'fs/promises';
|
import { mkdir, readdir, rm, writeFile } from 'fs/promises';
|
||||||
import { range, times } from 'lodash';
|
import { range, times } from 'lodash';
|
||||||
import { dirname, parse } from 'path';
|
import { dirname } from 'path';
|
||||||
import { argv } from 'process';
|
import { argv } from 'process';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import { Matrix4, Quaternion, Vector3, Vector4Tuple } from 'three';
|
import { Matrix4, Quaternion, Vector3, Vector4Tuple } from 'three';
|
||||||
@@ -23,7 +23,6 @@ import { toUniqueId } from '../src/core/blitz/toUniqueId';
|
|||||||
import commitMultipleFiles, {
|
import commitMultipleFiles, {
|
||||||
FileChange,
|
FileChange,
|
||||||
} from '../src/core/blitzkrieg/commitMultipleFiles';
|
} from '../src/core/blitzkrieg/commitMultipleFiles';
|
||||||
import { clearDdsToPng, ddsToPng } from '../src/core/blitzkrieg/ddsToPng';
|
|
||||||
import {
|
import {
|
||||||
GunDefinition,
|
GunDefinition,
|
||||||
ShellDefinition,
|
ShellDefinition,
|
||||||
@@ -33,6 +32,7 @@ import {
|
|||||||
Tier,
|
Tier,
|
||||||
TurretDefinition,
|
TurretDefinition,
|
||||||
} from '../src/core/blitzkrieg/tankDefinitions';
|
} from '../src/core/blitzkrieg/tankDefinitions';
|
||||||
|
import { toPng } from '../src/core/blitzkrieg/toPng';
|
||||||
import { Hierarchy } from '../src/types/sc2';
|
import { Hierarchy } from '../src/types/sc2';
|
||||||
|
|
||||||
const vertexAttributeGLTFName: Partial<Record<VertexAttribute, string>> = {
|
const vertexAttributeGLTFName: Partial<Record<VertexAttribute, string>> = {
|
||||||
@@ -505,8 +505,6 @@ if (allTargets || targets?.includes('circleFlags')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (allTargets || targets?.includes('tankModels')) {
|
if (allTargets || targets?.includes('tankModels')) {
|
||||||
await clearDdsToPng();
|
|
||||||
|
|
||||||
console.log('Building tank models...');
|
console.log('Building tank models...');
|
||||||
|
|
||||||
if (existsSync('dist/assets/models')) {
|
if (existsSync('dist/assets/models')) {
|
||||||
@@ -530,7 +528,8 @@ if (allTargets || targets?.includes('tankModels')) {
|
|||||||
const nationVehicleId = tank.id;
|
const nationVehicleId = tank.id;
|
||||||
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
|
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
|
||||||
|
|
||||||
if (id !== 7297) continue;
|
// if (id !== 6225) continue;
|
||||||
|
if (id !== 20257) continue;
|
||||||
console.log(`Building model ${id} @ ${nation}/${tankIndex}`);
|
console.log(`Building model ${id} @ ${nation}/${tankIndex}`);
|
||||||
|
|
||||||
const parameters = await readYAMLDVPL<TankParameters>(
|
const parameters = await readYAMLDVPL<TankParameters>(
|
||||||
@@ -554,54 +553,73 @@ if (allTargets || targets?.includes('tankModels')) {
|
|||||||
const document = new Document();
|
const document = new Document();
|
||||||
const scene = document.createScene();
|
const scene = document.createScene();
|
||||||
const buffer = document.createBuffer();
|
const buffer = document.createBuffer();
|
||||||
const materials = new Map<bigint, { material: Material }>();
|
const materials = new Map<bigint, Material | bigint>();
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
sc2['#dataNodes'].map(async (node) => {
|
sc2['#dataNodes'].map(async (node) => {
|
||||||
const material = document.createMaterial(node.materialName);
|
const id = node['#id'].readBigUInt64LE();
|
||||||
|
|
||||||
let albedoTexturePath: string | undefined = undefined;
|
if (node.parentMaterialKey !== undefined) {
|
||||||
let albedoTextureName: string | undefined = undefined;
|
materials.set(id, node.parentMaterialKey);
|
||||||
|
return;
|
||||||
if (node.textures) {
|
|
||||||
albedoTexturePath = `${DATA}/${DOI['3d']}/${dirname(
|
|
||||||
parameters.resourcesPath.blitzModelPath,
|
|
||||||
)}/${node.textures.albedo.replace('.tex', '.dx11.dds.dvpl')}`;
|
|
||||||
albedoTextureName = parse(albedoTexturePath).name;
|
|
||||||
} else if (typeof node.configCount === 'number') {
|
|
||||||
const config = node.configArchive_0;
|
|
||||||
albedoTexturePath = `${DATA}/${DOI['3d']}/${dirname(
|
|
||||||
parameters.resourcesPath.blitzModelPath,
|
|
||||||
)}/${config.textures.albedo.replace('.tex', '.dx11.dds.dvpl')}`;
|
|
||||||
albedoTextureName = config.configName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (albedoTexturePath) {
|
const material = document.createMaterial(node.materialName);
|
||||||
const albedoBuffer = await ddsToPng(
|
const albedoTexture = document
|
||||||
await readDVPLFile(albedoTexturePath),
|
.createTexture(node.materialName)
|
||||||
);
|
.setMimeType('image/png');
|
||||||
const albedoTexture = document
|
let albedoPathRaw: string | undefined = undefined;
|
||||||
.createTexture(albedoTextureName)
|
|
||||||
.setMimeType('image/png')
|
|
||||||
.setImage(albedoBuffer);
|
|
||||||
|
|
||||||
|
if (node.textures) {
|
||||||
|
albedoPathRaw = node.textures.albedo;
|
||||||
|
} else if (typeof node.configCount === 'number') {
|
||||||
|
albedoPathRaw = node.configArchive_0.textures.albedo;
|
||||||
|
}
|
||||||
|
|
||||||
|
const albedoTexturePath = albedoPathRaw
|
||||||
|
? `${DATA}/${DOI['3d']}/${dirname(
|
||||||
|
parameters.resourcesPath.blitzModelPath,
|
||||||
|
)}/${albedoPathRaw.replace('.tex', '.dx11.dds.dvpl')}`
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (albedoTexturePath) {
|
||||||
|
const isDds = existsSync(albedoTexturePath);
|
||||||
|
const resolvedAlbedoTexturePath = isDds
|
||||||
|
? albedoTexturePath
|
||||||
|
: albedoTexturePath.replace('.dds', '.pvr');
|
||||||
|
const decompressedAlbedo = await readDVPLFile(
|
||||||
|
resolvedAlbedoTexturePath,
|
||||||
|
);
|
||||||
|
const albedoPng = await toPng(decompressedAlbedo, {
|
||||||
|
format: isDds ? 'dds' : 'pvr',
|
||||||
|
alpha: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
albedoTexture.setImage(albedoPng);
|
||||||
material.setBaseColorTexture(albedoTexture);
|
material.setBaseColorTexture(albedoTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
materials.set(node['#id'].readBigUInt64LE(), { material });
|
materials.set(node['#id'].readBigUInt64LE(), material);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
sc2['#dataNodes'].forEach((node) => {
|
// replace children with parents
|
||||||
if (node.parentMaterialKey === undefined) return;
|
materials.forEach((material, id) => {
|
||||||
|
if (typeof material !== 'bigint') return;
|
||||||
|
|
||||||
const parentMaterial = materials.get(node.parentMaterialKey);
|
let resolvedMaterial: Material | bigint = material;
|
||||||
|
|
||||||
if (parentMaterial === undefined) return;
|
while (typeof resolvedMaterial === 'bigint') {
|
||||||
|
const linkedParentMaterial = materials.get(resolvedMaterial);
|
||||||
|
|
||||||
materials.set(node['#id'].readBigUInt64LE(), {
|
if (linkedParentMaterial === undefined) {
|
||||||
material: parentMaterial.material,
|
throw new Error('Could not resolve material');
|
||||||
});
|
}
|
||||||
|
|
||||||
|
resolvedMaterial = linkedParentMaterial;
|
||||||
|
}
|
||||||
|
|
||||||
|
materials.set(id, resolvedMaterial);
|
||||||
});
|
});
|
||||||
|
|
||||||
function parseHierarchies(
|
function parseHierarchies(
|
||||||
@@ -723,14 +741,16 @@ if (allTargets || targets?.includes('tankModels')) {
|
|||||||
.setBuffer(buffer);
|
.setBuffer(buffer);
|
||||||
const material = materials.get(batch['rb.nmatname']);
|
const material = materials.get(batch['rb.nmatname']);
|
||||||
|
|
||||||
if (!material) {
|
if (!(material instanceof Material)) {
|
||||||
throw new Error(`Missing material ${batch['rb.nmatname']}`);
|
throw new Error(
|
||||||
|
`Material ${batch['rb.nmatname']} is unresolved`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const primitive = document
|
const primitive = document
|
||||||
.createPrimitive()
|
.createPrimitive()
|
||||||
.setIndices(indexAccessor)
|
.setIndices(indexAccessor)
|
||||||
.setMaterial(material.material);
|
.setMaterial(material);
|
||||||
|
|
||||||
indexedVertexTypes.forEach((type) => {
|
indexedVertexTypes.forEach((type) => {
|
||||||
if (!(type in vertexAttributeGLTFName)) {
|
if (!(type in vertexAttributeGLTFName)) {
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
import { execFile } from 'child_process';
|
|
||||||
import { existsSync } from 'fs';
|
|
||||||
import { mkdir, readFile, rm, writeFile } from 'fs/promises';
|
|
||||||
import { uniqueId } from 'lodash';
|
|
||||||
import { promisify } from 'util';
|
|
||||||
|
|
||||||
export async function clearDdsToPng() {
|
|
||||||
await rm('temp/ddsToPng/images', { force: true, recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const execFilePromise = promisify(execFile);
|
|
||||||
let downloadStatus: Promise<void> | undefined = undefined;
|
|
||||||
|
|
||||||
export async function ddsToPng(ddsBuffer: Buffer) {
|
|
||||||
const id = uniqueId();
|
|
||||||
await mkdir('temp/ddsToPng/images', { recursive: true });
|
|
||||||
|
|
||||||
if (
|
|
||||||
!existsSync('temp/ddsToPng/texconv.exe') &&
|
|
||||||
downloadStatus === undefined
|
|
||||||
) {
|
|
||||||
console.log('texconv.exe not found, downloading...');
|
|
||||||
|
|
||||||
downloadStatus = new Promise<void>(async (resolve, reject) => {
|
|
||||||
const releaseResponse = await fetch(
|
|
||||||
'https://api.github.com/repos/microsoft/DirectXTex/releases/latest',
|
|
||||||
);
|
|
||||||
const releaseData = (await releaseResponse.json()) as {
|
|
||||||
assets: { browser_download_url: string; name: string }[];
|
|
||||||
};
|
|
||||||
const asset = releaseData.assets.find(
|
|
||||||
(asset) => asset.name === 'texconv.exe',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!asset) throw new Error('Could not find texconv.exe');
|
|
||||||
|
|
||||||
const downloadResponse = await fetch(asset.browser_download_url);
|
|
||||||
const downloadData = await downloadResponse.arrayBuffer();
|
|
||||||
|
|
||||||
await writeFile(
|
|
||||||
'temp/ddsToPng/texconv.exe',
|
|
||||||
new Uint8Array(downloadData),
|
|
||||||
);
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await downloadStatus;
|
|
||||||
|
|
||||||
return new Promise<Buffer>(async (resolve, reject) => {
|
|
||||||
await writeFile(`temp/ddsToPng/images/${id}.dds`, ddsBuffer);
|
|
||||||
await execFilePromise('temp/ddsToPng/texconv.exe', [
|
|
||||||
'-ft',
|
|
||||||
'png',
|
|
||||||
'-o',
|
|
||||||
'temp/ddsToPng/images',
|
|
||||||
`temp/ddsToPng/images/${id}.dds`,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const pngBuffer = await readFile(`temp/ddsToPng/images/${id}.png`);
|
|
||||||
|
|
||||||
resolve(pngBuffer);
|
|
||||||
rm(`temp/ddsToPng/images/${id}.dds`);
|
|
||||||
// rm(`temp/ddsToPng/images/${id}.png`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { exec } from 'child_process';
|
||||||
|
import { mkdir, readFile, rm, writeFile } from 'fs/promises';
|
||||||
|
import { generateUUID } from 'three/src/math/MathUtils';
|
||||||
|
import { promisify } from 'util';
|
||||||
|
|
||||||
|
const execPromise = promisify(exec);
|
||||||
|
|
||||||
|
interface ToPngOptions {
|
||||||
|
format: string;
|
||||||
|
alpha: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toPngDefaultOptions: ToPngOptions = {
|
||||||
|
format: 'dds',
|
||||||
|
alpha: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function toPng(
|
||||||
|
inputBuffer: Buffer,
|
||||||
|
options?: Partial<ToPngOptions>,
|
||||||
|
) {
|
||||||
|
const mergedOptions = { ...toPngDefaultOptions, ...options };
|
||||||
|
const id = generateUUID();
|
||||||
|
await mkdir('temp/toPng', { recursive: true });
|
||||||
|
await writeFile(`temp/toPng/${id}.${mergedOptions.format}`, inputBuffer);
|
||||||
|
await execPromise(
|
||||||
|
`PVRTexToolCLI -noout -i temp/toPng/${id}.${mergedOptions.format} -f ${
|
||||||
|
mergedOptions.alpha ? 'r8g8b8a8' : 'r8g8b8'
|
||||||
|
},UB,sRGB -d temp/toPng/${id}.png`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const pngBuffer = await readFile(`temp/toPng/${id}.png`);
|
||||||
|
|
||||||
|
rm(`temp/toPng/${id}.${mergedOptions.format}`);
|
||||||
|
rm(`temp/toPng/${id}.png`);
|
||||||
|
|
||||||
|
return pngBuffer;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user