mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 12:09:51 +02:00
fix: use base color map
This commit is contained in:
1 parent
1598ae973f
commit
95fa5bc1a5
2 files changed
+84
-81
No files matched your search
@@ -1,44 +1,48 @@
|
|||||||
import { toUniqueId } from '@blitzkit/core';
|
import { toUniqueId } from "@blitzkit/core";
|
||||||
import { NodeIO } from '@gltf-transform/core';
|
import { NodeIO } from "@gltf-transform/core";
|
||||||
import { ALL_EXTENSIONS } from '@gltf-transform/extensions';
|
import { ALL_EXTENSIONS } from "@gltf-transform/extensions";
|
||||||
import { readdir } from 'fs/promises';
|
import { readdir } from "fs/promises";
|
||||||
import { extractModel } from '../core/blitz/extractModel';
|
import { extractModel } from "../core/blitz/extractModel";
|
||||||
import { readXMLDVPL } from '../core/blitz/readXMLDVPL';
|
import { readXMLDVPL } from "../core/blitz/readXMLDVPL";
|
||||||
import { readYAMLDVPL } from '../core/blitz/readYAMLDVPL';
|
import { readYAMLDVPL } from "../core/blitz/readYAMLDVPL";
|
||||||
import { commitAssets } from '../core/github/commitAssets';
|
import { commitAssets } from "../core/github/commitAssets";
|
||||||
import { FileChange } from '../core/github/commitMultipleFiles';
|
import { FileChange } from "../core/github/commitMultipleFiles";
|
||||||
import { DATA } from './constants';
|
import { DATA } from "./constants";
|
||||||
import { VehicleDefinitionList } from './definitions';
|
import { VehicleDefinitionList } from "./definitions";
|
||||||
import { TankParameters } from './tankIcons';
|
import { TankParameters } from "./tankIcons";
|
||||||
|
|
||||||
export async function tankModels() {
|
export async function tankModels() {
|
||||||
console.log('Building tank models...');
|
console.log("Building tank models...");
|
||||||
|
|
||||||
const nodeIO = new NodeIO().registerExtensions(ALL_EXTENSIONS);
|
const nodeIO = new NodeIO().registerExtensions(ALL_EXTENSIONS);
|
||||||
const nations = await readdir(`${DATA}/XML/item_defs/vehicles`).then(
|
const nations = await readdir(`${DATA}/XML/item_defs/vehicles`).then(
|
||||||
(nations) => nations.filter((nation) => nation !== 'common'),
|
(nations) => nations.filter((nation) => nation !== "common")
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const nationIndex in nations) {
|
for (const nationIndex in nations) {
|
||||||
const nation = nations[nationIndex];
|
const nation = nations[nationIndex];
|
||||||
const changes: FileChange[] = [];
|
const changes: FileChange[] = [];
|
||||||
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
const tanks = await readXMLDVPL<{ root: VehicleDefinitionList }>(
|
||||||
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`,
|
`${DATA}/XML/item_defs/vehicles/${nation}/list.xml`
|
||||||
);
|
);
|
||||||
|
const entries = Object.entries(tanks.root);
|
||||||
|
|
||||||
console.log(`Building models for ${nation}`);
|
console.log(`Building models for ${nation}`);
|
||||||
|
|
||||||
for (const [tankKey, tank] of Object.entries(tanks.root)) {
|
let index = 0;
|
||||||
if (tankKey.includes('tutorial_bot')) continue;
|
for (const [tankKey, tank] of entries) {
|
||||||
|
console.log(` ${++index} / ${entries.length} : ${tankKey}`);
|
||||||
|
|
||||||
|
if (tankKey.includes("tutorial_bot")) continue;
|
||||||
|
|
||||||
const id = toUniqueId(nation, tank.id);
|
const id = toUniqueId(nation, tank.id);
|
||||||
|
|
||||||
const parameters = await readYAMLDVPL<TankParameters>(
|
const parameters = await readYAMLDVPL<TankParameters>(
|
||||||
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`,
|
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`
|
||||||
);
|
);
|
||||||
const model = await extractModel(
|
const model = await extractModel(
|
||||||
DATA,
|
DATA,
|
||||||
parameters.resourcesPath.blitzModelPath.replace(/\.sc2$/, ''),
|
parameters.resourcesPath.blitzModelPath.replace(/\.sc2$/, "")
|
||||||
);
|
);
|
||||||
|
|
||||||
changes.push({
|
changes.push({
|
||||||
|
|||||||
@@ -4,35 +4,34 @@ import {
|
|||||||
ScgReadStream,
|
ScgReadStream,
|
||||||
Textures,
|
Textures,
|
||||||
VertexAttribute,
|
VertexAttribute,
|
||||||
} from '@blitzkit/core';
|
} from "@blitzkit/core";
|
||||||
import { Document, Material, Node, Scene } from '@gltf-transform/core';
|
import { Document, Material, Node, Scene } from "@gltf-transform/core";
|
||||||
import { dedup, prune } from '@gltf-transform/functions';
|
import { dedup, prune } from "@gltf-transform/functions";
|
||||||
import { times } from 'lodash-es';
|
import { times } from "lodash-es";
|
||||||
import { dirname } from 'path';
|
import { dirname } from "path";
|
||||||
import { Matrix4, Quaternion, Vector3, Vector4Tuple } from 'three';
|
import { readDVPLFile } from "../readDVPLFile";
|
||||||
import { readDVPLFile } from '../readDVPLFile';
|
import { readTexture } from "../readTexture";
|
||||||
import { readTexture } from '../readTexture';
|
import { TextureMutation } from "../readTexture/constants";
|
||||||
import { TextureMutation } from '../readTexture/constants';
|
|
||||||
import {
|
import {
|
||||||
vertexAttributeGLTFName,
|
vertexAttributeGLTFName,
|
||||||
vertexAttributeGltfVectorSizes,
|
vertexAttributeGltfVectorSizes,
|
||||||
} from './constants';
|
} from "./constants";
|
||||||
|
|
||||||
const ERROR_ON_UNKNOWN_COMPONENT = false;
|
const ERROR_ON_UNKNOWN_COMPONENT = false;
|
||||||
|
|
||||||
const omitMeshNames = {
|
const omitMeshNames = {
|
||||||
start: ['chassis_chassis_', 'chassis_track_crash_', 'HP_'],
|
start: ["chassis_chassis_", "chassis_track_crash_", "HP_"],
|
||||||
end: ['_POINT'],
|
end: ["_POINT"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function extractModel(data: string, path: string) {
|
export async function extractModel(data: string, path: string) {
|
||||||
const sc2Path = `${data}/3d/${path}.sc2`;
|
const sc2Path = `${data}/3d/${path}.sc2`;
|
||||||
const scgPath = `${data}/3d/${path}.scg`;
|
const scgPath = `${data}/3d/${path}.scg`;
|
||||||
const sc2 = new Sc2ReadStream(
|
const sc2 = new Sc2ReadStream(
|
||||||
(await readDVPLFile(sc2Path)).buffer as ArrayBuffer,
|
(await readDVPLFile(sc2Path)).buffer as ArrayBuffer
|
||||||
).sc2();
|
).sc2();
|
||||||
const scg = new ScgReadStream(
|
const scg = new ScgReadStream(
|
||||||
(await readDVPLFile(scgPath)).buffer as ArrayBuffer,
|
(await readDVPLFile(scgPath)).buffer as ArrayBuffer
|
||||||
).scg();
|
).scg();
|
||||||
const document = new Document();
|
const document = new Document();
|
||||||
const scene = document.createScene();
|
const scene = document.createScene();
|
||||||
@@ -41,8 +40,8 @@ export async function extractModel(data: string, path: string) {
|
|||||||
|
|
||||||
// create materials
|
// create materials
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
sc2['#dataNodes'].map(async (node) => {
|
sc2["#dataNodes"].map(async (node) => {
|
||||||
const id = new DataView(node['#id']).getBigUint64(0, true);
|
const id = new DataView(node["#id"]).getBigUint64(0, true);
|
||||||
|
|
||||||
if (node.parentMaterialKey !== undefined) {
|
if (node.parentMaterialKey !== undefined) {
|
||||||
/**
|
/**
|
||||||
@@ -58,7 +57,7 @@ export async function extractModel(data: string, path: string) {
|
|||||||
|
|
||||||
if (node.textures) {
|
if (node.textures) {
|
||||||
textures = node.textures;
|
textures = node.textures;
|
||||||
} else if (typeof node.configCount === 'number') {
|
} else if (typeof node.configCount === "number") {
|
||||||
textures = node.configArchive_0.textures;
|
textures = node.configArchive_0.textures;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,26 +65,26 @@ export async function extractModel(data: string, path: string) {
|
|||||||
material.setBaseColorTexture(
|
material.setBaseColorTexture(
|
||||||
document
|
document
|
||||||
.createTexture(node.materialName)
|
.createTexture(node.materialName)
|
||||||
.setMimeType('image/jpeg')
|
.setMimeType("image/jpeg")
|
||||||
.setImage(
|
.setImage(
|
||||||
await readTexture(
|
await readTexture(
|
||||||
`${data}/3d/${dirname(path)}/${textures.albedo}`,
|
`${data}/3d/${dirname(path)}/${textures.baseColorMap ?? textures.albedo}`,
|
||||||
TextureMutation.Albedo,
|
TextureMutation.Albedo
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (textures.baseRMMap) {
|
if (textures.baseRMMap) {
|
||||||
material.setMetallicRoughnessTexture(
|
material.setMetallicRoughnessTexture(
|
||||||
document
|
document
|
||||||
.createTexture(node.materialName)
|
.createTexture(node.materialName)
|
||||||
.setMimeType('image/jpeg')
|
.setMimeType("image/jpeg")
|
||||||
.setImage(
|
.setImage(
|
||||||
await readTexture(
|
await readTexture(
|
||||||
`${data}/3d/${dirname(path)}/${textures.baseRMMap}`,
|
`${data}/3d/${dirname(path)}/${textures.baseRMMap}`,
|
||||||
TextureMutation.RoughnessMetallicness,
|
TextureMutation.RoughnessMetallicness
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,15 +94,15 @@ export async function extractModel(data: string, path: string) {
|
|||||||
material.setNormalTexture(
|
material.setNormalTexture(
|
||||||
document
|
document
|
||||||
.createTexture(node.materialName)
|
.createTexture(node.materialName)
|
||||||
.setMimeType('image/jpeg')
|
.setMimeType("image/jpeg")
|
||||||
.setImage(
|
.setImage(
|
||||||
await readTexture(
|
await readTexture(
|
||||||
`${data}/3d/${dirname(path)}/${
|
`${data}/3d/${dirname(path)}/${
|
||||||
textures.baseNormalMap ?? textures.normalmap
|
textures.baseNormalMap ?? textures.normalmap
|
||||||
}`,
|
}`,
|
||||||
isBase ? TextureMutation.Normal : undefined,
|
isBase ? TextureMutation.Normal : undefined
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,31 +110,31 @@ export async function extractModel(data: string, path: string) {
|
|||||||
material.setOcclusionTexture(
|
material.setOcclusionTexture(
|
||||||
document
|
document
|
||||||
.createTexture(node.materialName)
|
.createTexture(node.materialName)
|
||||||
.setMimeType('image/jpeg')
|
.setMimeType("image/jpeg")
|
||||||
.setImage(
|
.setImage(
|
||||||
await readTexture(
|
await readTexture(
|
||||||
`${data}/3d/${dirname(path)}/${textures.miscMap}`,
|
`${data}/3d/${dirname(path)}/${textures.miscMap}`,
|
||||||
TextureMutation.Miscellaneous,
|
TextureMutation.Miscellaneous
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
materials.set(
|
materials.set(
|
||||||
new DataView(node['#id']).getBigUint64(0, true),
|
new DataView(node["#id"]).getBigUint64(0, true),
|
||||||
material,
|
material
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// replace children materials with parents
|
// replace children materials with parents
|
||||||
materials.forEach((material, id) => {
|
materials.forEach((material, id) => {
|
||||||
if (typeof material !== 'bigint') return;
|
if (typeof material !== "bigint") return;
|
||||||
|
|
||||||
let resolvedMaterial: Material | bigint | undefined = material;
|
let resolvedMaterial: Material | bigint | undefined = material;
|
||||||
|
|
||||||
while (typeof resolvedMaterial === 'bigint') {
|
while (typeof resolvedMaterial === "bigint") {
|
||||||
const linkedParentMaterial = materials.get(resolvedMaterial);
|
const linkedParentMaterial = materials.get(resolvedMaterial);
|
||||||
|
|
||||||
resolvedMaterial = linkedParentMaterial;
|
resolvedMaterial = linkedParentMaterial;
|
||||||
@@ -155,36 +154,36 @@ export async function extractModel(data: string, path: string) {
|
|||||||
const node = document.createNode(hierarchy.name);
|
const node = document.createNode(hierarchy.name);
|
||||||
const components = times(
|
const components = times(
|
||||||
hierarchy.components.count,
|
hierarchy.components.count,
|
||||||
(index) => hierarchy.components[index.toString().padStart(4, '0')],
|
(index) => hierarchy.components[index.toString().padStart(4, "0")]
|
||||||
);
|
);
|
||||||
|
|
||||||
components.forEach((component) => {
|
components.forEach((component) => {
|
||||||
switch (component['comp.typename']) {
|
switch (component["comp.typename"]) {
|
||||||
case 'LodComponent':
|
case "LodComponent":
|
||||||
// found and used later by transform component
|
// found and used later by transform component
|
||||||
// TODO: lod component always shows up before transform component; cache it before parsing transform
|
// TODO: lod component always shows up before transform component; cache it before parsing transform
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'TransformComponent': {
|
case "TransformComponent": {
|
||||||
node.setTranslation(component['tc.localTranslation']);
|
node.setTranslation(component["tc.localTranslation"]);
|
||||||
node.setRotation(component['tc.localRotation']);
|
node.setRotation(component["tc.localRotation"]);
|
||||||
node.setScale(component['tc.localScale']);
|
node.setScale(component["tc.localScale"]);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'RenderComponent': {
|
case "RenderComponent": {
|
||||||
const renderObject = component['rc.renderObj'];
|
const renderObject = component["rc.renderObj"];
|
||||||
|
|
||||||
times(renderObject['ro.batchCount'], (batchIndex): void => {
|
times(renderObject["ro.batchCount"], (batchIndex): void => {
|
||||||
const lodIndex = renderObject[`rb${batchIndex}.lodIndex`];
|
const lodIndex = renderObject[`rb${batchIndex}.lodIndex`];
|
||||||
|
|
||||||
if (lodIndex !== 0) return;
|
if (lodIndex !== 0) return;
|
||||||
|
|
||||||
const batchKey = batchIndex.toString().padStart(4, '0');
|
const batchKey = batchIndex.toString().padStart(4, "0");
|
||||||
const batch = renderObject['ro.batches'][batchKey];
|
const batch = renderObject["ro.batches"][batchKey];
|
||||||
const material = materials.get(batch['rb.nmatname']);
|
const material = materials.get(batch["rb.nmatname"]);
|
||||||
const polygonGroup = scg.get(batch['rb.datasource']);
|
const polygonGroup = scg.get(batch["rb.datasource"]);
|
||||||
|
|
||||||
if (!(material instanceof Material)) {
|
if (!(material instanceof Material)) {
|
||||||
// probably shadow material
|
// probably shadow material
|
||||||
@@ -192,14 +191,14 @@ export async function extractModel(data: string, path: string) {
|
|||||||
}
|
}
|
||||||
if (polygonGroup === undefined) {
|
if (polygonGroup === undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Missing polygon group ${batch['rb.datasource']}`,
|
`Missing polygon group ${batch["rb.datasource"]}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const lodNode = document.createNode(batchKey);
|
const lodNode = document.createNode(batchKey);
|
||||||
const indicesAccessor = document
|
const indicesAccessor = document
|
||||||
.createAccessor()
|
.createAccessor()
|
||||||
.setType('SCALAR')
|
.setType("SCALAR")
|
||||||
.setArray(new Uint16Array(polygonGroup.indices))
|
.setArray(new Uint16Array(polygonGroup.indices))
|
||||||
.setBuffer(buffer);
|
.setBuffer(buffer);
|
||||||
const primitive = document
|
const primitive = document
|
||||||
@@ -233,7 +232,7 @@ export async function extractModel(data: string, path: string) {
|
|||||||
const vertexSize = vertexAttributeGltfVectorSizes[attribute];
|
const vertexSize = vertexAttributeGltfVectorSizes[attribute];
|
||||||
const attributeAccessor = document
|
const attributeAccessor = document
|
||||||
.createAccessor(name)
|
.createAccessor(name)
|
||||||
.setType(vertexSize === 1 ? 'SCALAR' : `VEC${vertexSize}`)
|
.setType(vertexSize === 1 ? "SCALAR" : `VEC${vertexSize}`)
|
||||||
.setArray(new Float32Array(value.flat()))
|
.setArray(new Float32Array(value.flat()))
|
||||||
.setBuffer(buffer);
|
.setBuffer(buffer);
|
||||||
|
|
||||||
@@ -241,7 +240,7 @@ export async function extractModel(data: string, path: string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mesh = document
|
const mesh = document
|
||||||
.createMesh(batch['##name'])
|
.createMesh(batch["##name"])
|
||||||
.addPrimitive(primitive);
|
.addPrimitive(primitive);
|
||||||
lodNode.setMesh(mesh);
|
lodNode.setMesh(mesh);
|
||||||
node.addChild(lodNode);
|
node.addChild(lodNode);
|
||||||
@@ -253,24 +252,24 @@ export async function extractModel(data: string, path: string) {
|
|||||||
default: {
|
default: {
|
||||||
if (ERROR_ON_UNKNOWN_COMPONENT) {
|
if (ERROR_ON_UNKNOWN_COMPONENT) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unhandled component type: ${component['comp.typename']}`,
|
`Unhandled component type: ${component["comp.typename"]}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hierarchy['#hierarchy']) {
|
if (hierarchy["#hierarchy"]) {
|
||||||
parseHierarchies(hierarchy['#hierarchy'], node);
|
parseHierarchies(hierarchy["#hierarchy"], node);
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.addChild(node);
|
parent.addChild(node);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
parseHierarchies(sc2['#hierarchy'], scene);
|
parseHierarchies(sc2["#hierarchy"], scene);
|
||||||
|
|
||||||
scene.addChild(document.createNode('test'));
|
scene.addChild(document.createNode("test"));
|
||||||
|
|
||||||
await document.transform(prune({ keepAttributes: true }), dedup());
|
await document.transform(prune({ keepAttributes: true }), dedup());
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user