Merge branch 'main' into ue

This commit is contained in:
tresabhi committed 2026-06-10 13:11:59 -05:00
commit 173c476fb4
12 files changed
+240 -110

No files matched your search

+48 -6
View File
@@ -1,6 +1,7 @@
import { NATION_IDS } from "@blitzkit/core";
import sharp from "sharp";
import type { Vector3Tuple } from "three";
import { parsePackedSpriteRect } from "../core/blitz/parsePackedSpriteRect";
import { AssetUploader } from "../core/github/assetUploader";
import { vfs } from "./constants";
import type { VehicleDefinitionList } from "./definitions";
@@ -37,6 +38,45 @@ export interface TankParameters {
};
}
async function extractPackedTankIcon(packedBuffer: Buffer, context: string) {
const texture = sharp(packedBuffer);
const spriteRect = parsePackedSpriteRect(packedBuffer);
if (!spriteRect) {
throw new Error(`Missing RIFF sprite bounds for ${context}`);
}
const [left, top, width, height] = spriteRect;
const bounds = [left, top, width, height];
if (!bounds.every(Number.isInteger)) {
throw new Error(
`Invalid RIFF sprite bounds for ${context}: ${bounds.join(" ")}`,
);
}
const metadata = await texture.metadata();
if (metadata.width === undefined || metadata.height === undefined) {
throw new Error(`Failed to read image dimensions for ${context}`);
}
if (
left < 0 ||
top < 0 ||
width <= 0 ||
height <= 0 ||
left + width > metadata.width ||
top + height > metadata.height
) {
throw new Error(
`Out-of-bounds RIFF sprite for ${context}: ${bounds.join(" ")} in ${metadata.width}x${metadata.height}`,
);
}
return await texture.extract({ left, top, width, height }).toBuffer();
}
export async function tankIcons() {
console.log("Building tank icons...");
@@ -67,12 +107,14 @@ export async function tankIcons() {
const bigPath = `Data/${parameters.resourcesPath.bigIconPath
.replace(/~res:\//, "")
.replace(/\..+/, "")}.packed.webp`;
const big = await sharp(await vfs.file(bigPath))
.trim()
.toBuffer();
const small = await sharp(await vfs.file(smallPath))
.trim()
.toBuffer();
const big = await extractPackedTankIcon(
await vfs.file(bigPath),
`tank ${tankKey} big`,
);
const small = await extractPackedTankIcon(
await vfs.file(smallPath),
`tank ${tankKey} small`,
);
if (big) {
await uploader.add({
@@ -1,5 +1,6 @@
import {
bufferToBigInt,
ConfigArchive,
Hierarchy,
Sc2ReadStream,
ScgReadStream,
@@ -75,6 +76,42 @@ export async function extractModel(vfs: AbstractVFS, path: string) {
.setImage(baseColor),
);
let defaultConfigArchive: ConfigArchive | undefined = undefined;
if (node.configCount) {
const names = times(
node.configCount,
(index) => node[`configArchive_${index}`],
);
defaultConfigArchive = names.find(
(archive) => archive.configName === "Default",
);
defaultConfigArchive ??= node.configArchive_0;
}
const customCullMode =
node.customCullMode ?? defaultConfigArchive?.customCullMode;
if (customCullMode === 0) {
material.setDoubleSided(true);
} else if (customCullMode !== undefined) {
material.setDoubleSided(false);
}
if (
node.enabledPresets?.AlphaTest &&
node.properties?.alphatestThreshold
) {
const view = new DataView(node.properties.alphatestThreshold);
const alphaCutoff =
view.byteLength < 4
? 0.5
: view.getFloat32(view.byteLength - 4, true);
material.setAlphaMode("MASK").setAlphaCutoff(alphaCutoff);
}
if (node.configCount) {
for (
let configIndex = 0;
@@ -85,14 +122,17 @@ export async function extractModel(vfs: AbstractVFS, path: string) {
if (
archive.configName !== "Default" ||
!archive.enabledPresets?.AlphaTest
!archive.enabledPresets?.AlphaTest ||
!archive.properties?.alphatestThreshold
) {
continue;
}
const view = new DataView(archive.properties.alphatestThreshold);
const _ = view.getUint8(0); // only god knows what these flags are
const alphaCutoff = view.getFloat32(1, true);
const alphaCutoff =
view.byteLength < 4
? 0.5
: view.getFloat32(view.byteLength - 4, true);
material.setAlphaMode("MASK").setAlphaCutoff(alphaCutoff);
}
@@ -171,8 +211,8 @@ export async function extractModel(vfs: AbstractVFS, path: string) {
case "TransformComponent": {
const localTranslation = component["tc.localTranslation"];
// The game resets top-level node translation to [0, 0, 0] on load.
// Child nodes must keep their authored local offsets.
// The game resets top-level node translation to [0, 0, 0] on load.
// Child nodes must keep their authored local offsets.
node.setTranslation(
parent instanceof Scene ? [0, 0, 0] : localTranslation,
);
@@ -284,4 +324,4 @@ export async function extractModel(vfs: AbstractVFS, path: string) {
await document.transform(prune({ keepAttributes: true }), dedup());
return document;
}
}