mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 11:39:50 +02:00
Merge branch 'main' into ue
This commit is contained in:
commit
173c476fb4
12 files changed
+240
-110
No files matched your search
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user