From 4819100ec69ee3b4f594e5f5333bfacfa1f0b339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Wed, 13 Mar 2024 17:00:40 -0500 Subject: [PATCH] feat: sharp-based texture manipulation --- scripts/buildAssets/tankModels.ts | 19 ---- src/core/blitz/extractModel/index.ts | 24 ++---- src/core/blitzkrieg/readTexture/constants.ts | 2 +- src/core/blitzkrieg/readTexture/index.ts | 91 +++++++------------- 4 files changed, 40 insertions(+), 96 deletions(-) diff --git a/scripts/buildAssets/tankModels.ts b/scripts/buildAssets/tankModels.ts index edf276fe9..f639dac88 100644 --- a/scripts/buildAssets/tankModels.ts +++ b/scripts/buildAssets/tankModels.ts @@ -1,21 +1,15 @@ import { NodeIO } from '@gltf-transform/core'; import { readdir } from 'fs/promises'; -import { Vector3Tuple } from 'three'; import { extractModel } from '../../src/core/blitz/extractModel'; import { readXMLDVPL } from '../../src/core/blitz/readXMLDVPL'; import { readYAMLDVPL } from '../../src/core/blitz/readYAMLDVPL'; import { toUniqueId } from '../../src/core/blitz/toUniqueId'; -// import { AssetCommit } from '../../src/core/github/assetCommit'; import { commitAssets } from '../../src/core/blitzkrieg/commitAssets'; import { FileChange } from '../../src/core/blitzkrieg/commitMultipleFiles'; import { DATA, POI } from './constants'; import { VehicleDefinitionList } from './definitions'; import { TankParameters } from './tankIcons'; -interface VehicleCustomization { - armorColor: string; -} - export async function tankModels(production: boolean) { console.log('Building tank models...'); @@ -27,18 +21,9 @@ export async function tankModels(production: boolean) { for (const nationIndex in nations) { const nation = nations[nationIndex]; const changes: FileChange[] = []; - // const assetCommit = new AssetCommit(`tank models ${nation}`, production); const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>( `${DATA}/${POI.vehicleDefinitions}/${nation}/list.xml.dvpl`, ); - const customization = await readXMLDVPL<{ root: VehicleCustomization }>( - `${DATA}/${POI.vehicleDefinitions}/${nation}/customization.xml.dvpl`, - ); - const baseColor = customization.root.armorColor - .split(' ') - .slice(0, 3) - .map(Number) - .map((channel) => channel / 255) as Vector3Tuple; console.log(`Building models for ${nation}`); @@ -56,11 +41,8 @@ export async function tankModels(production: boolean) { const model = await extractModel( DATA, parameters.resourcesPath.blitzModelPath.replace(/\.sc2$/, ''), - baseColor, ); - // writeFile(`test.${tankKey}.glb`, await nodeIO.writeBinary(model)); - changes.push({ path: `3d/tanks/models/${id}.glb`, content: Buffer.from(await nodeIO.writeBinary(model)).toString( @@ -71,7 +53,6 @@ export async function tankModels(production: boolean) { }), ); - // assetCommit.push(); await commitAssets(`tank models ${nation}`, changes, production); } } diff --git a/src/core/blitz/extractModel/index.ts b/src/core/blitz/extractModel/index.ts index d279257e8..68060ccc2 100644 --- a/src/core/blitz/extractModel/index.ts +++ b/src/core/blitz/extractModel/index.ts @@ -1,13 +1,7 @@ import { Document, Material, Node, Scene } from '@gltf-transform/core'; import { times } from 'lodash'; import { dirname } from 'path'; -import { - Matrix4, - Quaternion, - Vector3, - Vector3Tuple, - Vector4Tuple, -} from 'three'; +import { Matrix4, Quaternion, Vector3, Vector4Tuple } from 'three'; import { readTexture } from '../../blitzkrieg/readTexture'; import { TextureMutation } from '../../blitzkrieg/readTexture/constants'; import { Hierarchy, Sc2ReadStream, Textures } from '../../streams/sc2'; @@ -26,11 +20,7 @@ const omitMeshNames = { end: ['_POINT'], }; -export async function extractModel( - data: string, - path: string, - baseColor?: Vector3Tuple, -) { +export async function extractModel(data: string, path: string) { const sc2Path = `${data}/3d/${path}.sc2.dvpl`; const scgPath = `${data}/3d/${path}.scg.dvpl`; const sc2 = new Sc2ReadStream((await readDVPLFile(sc2Path)).buffer).sc2(); @@ -71,9 +61,7 @@ export async function extractModel( .setImage( await readTexture( `${data}/3d/${dirname(path)}/${textures.albedo}`, - baseColor - ? { mutation: TextureMutation.BaseColor, baseColor } - : undefined, + TextureMutation.Albedo, ), ), ); @@ -86,7 +74,7 @@ export async function extractModel( .setImage( await readTexture( `${data}/3d/${dirname(path)}/${textures.baseRMMap}`, - { mutation: TextureMutation.RoughnessMetallicness }, + TextureMutation.RoughnessMetallicness, ), ), ); @@ -104,7 +92,7 @@ export async function extractModel( `${data}/3d/${dirname(path)}/${ textures.baseNormalMap ?? textures.normalmap }`, - isBase ? { mutation: TextureMutation.Normal } : undefined, + isBase ? TextureMutation.Normal : undefined, ), ), ); @@ -118,7 +106,7 @@ export async function extractModel( .setImage( await readTexture( `${data}/3d/${dirname(path)}/${textures.miscMap}`, - { mutation: TextureMutation.Miscellaneous }, + TextureMutation.Miscellaneous, ), ), ); diff --git a/src/core/blitzkrieg/readTexture/constants.ts b/src/core/blitzkrieg/readTexture/constants.ts index eee6275bf..560a59f5f 100644 --- a/src/core/blitzkrieg/readTexture/constants.ts +++ b/src/core/blitzkrieg/readTexture/constants.ts @@ -1,6 +1,6 @@ export enum TextureMutation { Normal, RoughnessMetallicness, - BaseColor, + Albedo, Miscellaneous, } diff --git a/src/core/blitzkrieg/readTexture/index.ts b/src/core/blitzkrieg/readTexture/index.ts index c9d9a630b..9f55a3fea 100644 --- a/src/core/blitzkrieg/readTexture/index.ts +++ b/src/core/blitzkrieg/readTexture/index.ts @@ -1,27 +1,11 @@ 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'; import { TextureMutation } from './constants'; -type ReadTextureOptions = - | { - mutation: - | TextureMutation.Normal - | TextureMutation.RoughnessMetallicness - | TextureMutation.Miscellaneous; - } - | { - mutation: TextureMutation.BaseColor; - /** - * Normalized between 0 and 1 - */ - baseColor: Vector3Tuple; - }; - -export async function readTexture(path: string, options?: ReadTextureOptions) { +export async function readTexture(path: string, mutation?: TextureMutation) { const ddsTexturePath = path.replace('.tex', '.dx11.dds.dvpl'); const isDds = existsSync(ddsTexturePath); const resolvedTexturePath = isDds @@ -32,11 +16,11 @@ export async function readTexture(path: string, options?: ReadTextureOptions) { const raw = isDds ? await new DdsReadStream(decompressedDvpl.buffer).dds() : new PvrReadStream(decompressedDvpl.buffer).pvr(); - const channels = 4 * raw.width * raw.height; - switch (options?.mutation) { + switch (mutation) { case TextureMutation.Normal: { - for (let index = 0; index < channels; index += 4) { + const bytes = 4 * raw.width * raw.height; + for (let index = 0; index < bytes; index += 4) { /** * Red is always 255 and blue is always 0. Only alpha and green contain any * sort of information. @@ -52,53 +36,44 @@ export async function readTexture(path: string, options?: ReadTextureOptions) { raw.data[index + 3] = 255; } - break; + return await sharp(raw.data, { raw }).png().toBuffer(); } case TextureMutation.RoughnessMetallicness: { - for (let index = 0; index < channels; index += 4) { - /** - * Same channel situation as normal maps. - */ - const metallicness = raw.data[index + 1]; - const roughness = raw.data[index + 3]; + const newImage = sharp(Buffer.alloc(raw.width * raw.height), { + raw: { ...raw, channels: 1 }, + }); + const metallicness = sharp(raw.data, { raw }) + .extractChannel('green') + .png() + .toBuffer(); + const roughness = sharp(raw.data, { raw }) + .extractChannel('alpha') + .png() + .toBuffer(); - raw.data[index] = 0; - raw.data[index + 1] = roughness; - raw.data[index + 2] = metallicness; - raw.data[index + 3] = 255; - } - - break; + return await newImage + .joinChannel(await Promise.all([roughness, metallicness])) + .png() + .toBuffer(); } case TextureMutation.Miscellaneous: { - for (let index = 0; index < channels; index += 4) { - /** - * Alpha is ambient occlusion and green is emissive. But very few tanks - * use emissive so I am ignoring it for now. - */ - // const emissive = raw.data[index + 1]; - const occlusion = raw.data[index + 3]; - - raw.data[index] = occlusion; - raw.data[index + 1] = 0; - raw.data[index + 2] = 0; - raw.data[index + 3] = 255; - } - - break; + /** + * Alpha is ambient occlusion and green is emissive. But very few tanks + * use emissive so I am ignoring it for now. + */ + return await sharp(raw.data, { raw }) + .extractChannel('alpha') + .png() + .toBuffer(); } - case TextureMutation.BaseColor: { - for (let index = 0; index < channels; index += 4) { - // alpha map is specularity but I am ignoring it for now - raw.data[index + 3] = 255; - } - - break; + case TextureMutation.Albedo: { + return await sharp(raw.data, { raw }).removeAlpha().png().toBuffer(); } + + default: + return await sharp(raw.data, { raw }).png().toBuffer(); } - - return await sharp(raw.data, { raw: raw }).png().toBuffer(); }