feat: lod distance based model loading

This commit is contained in:
TrèsAbhi committed 2023-12-24 18:38:13 -06:00
1 parent 9a8c0399be
commit 99fb44633e
3 files changed
+54 -31

No files matched your search

+2
View File
@@ -36,6 +36,8 @@
"krieg", "krieg",
"Leaderboard", "Leaderboard",
"leaderboards", "leaderboards",
"loddist",
"lods",
"metallicness", "metallicness",
"MURICA", "MURICA",
"nmatname", "nmatname",
+1 -1
View File
@@ -518,7 +518,7 @@ 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 !== 5137) continue; if (id !== 6225) 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>(
+51 -30
View File
@@ -1,4 +1,5 @@
import { Document, Material, Node, Scene } from '@gltf-transform/core'; import { Document, Material, Node, Scene } from '@gltf-transform/core';
import { writeFileSync } from 'fs';
import { range, times } from 'lodash'; import { range, times } from 'lodash';
import { dirname } from 'path'; import { dirname } from 'path';
import sharp from 'sharp'; import sharp from 'sharp';
@@ -8,6 +9,8 @@ import { readTexture } from '../blitzkrieg/readTexture';
import { readDVPL } from './readDVPL'; import { readDVPL } from './readDVPL';
import { readDVPLFile } from './readDVPLFile'; import { readDVPLFile } from './readDVPLFile';
const MAX_FLOAT32 = 2 ** 127 * (2 - 2 ** -23);
enum KAType { enum KAType {
NONE = 0, NONE = 0,
BOOLEAN = 1, BOOLEAN = 1,
@@ -36,7 +39,7 @@ enum KAType {
INT16 = 24, INT16 = 24,
UINT16 = 25, UINT16 = 25,
ARRAY = 27, ARRAY = 27,
UNCONFIRMED_TRANSFORM = 29, TRANSFORM = 29,
} }
export enum VertexAttribute { export enum VertexAttribute {
@@ -407,17 +410,15 @@ export class SCPGStream {
return stringTable[index]; return stringTable[index];
} }
case KAType.UNCONFIRMED_TRANSFORM: { case KAType.TRANSFORM: {
const position = this.consumeVector3(); const position = this.consumeVector3();
const scale = this.consumeVector3(); const scale = this.consumeVector3();
const rotation = this.consumeVector3(); const rotation = this.consumeVector4();
const UNCONFIRMED_scalar = this.consumeFloat();
return { return {
position, position,
scale, scale,
rotation, rotation,
UNCONFIRMED_scalar,
}; };
} }
@@ -511,6 +512,8 @@ export class SCPGStream {
const buffer = document.createBuffer(); const buffer = document.createBuffer();
const materials = new Map<bigint, Material | bigint>(); const materials = new Map<bigint, Material | bigint>();
writeFileSync('test.sc2.json', JSON.stringify(sc2, null, 2));
// create materials // create materials
await Promise.all( await Promise.all(
sc2['#dataNodes'].map(async (node) => { sc2['#dataNodes'].map(async (node) => {
@@ -546,6 +549,8 @@ export class SCPGStream {
), ),
); );
console.log(textures);
if (textures.baseRMMap) { if (textures.baseRMMap) {
material.setMetallicRoughnessTexture( material.setMetallicRoughnessTexture(
document document
@@ -562,8 +567,7 @@ export class SCPGStream {
.then(({ data, info }) => { .then(({ data, info }) => {
// fake it till you make it // fake it till you make it
for (let index = 0; index < data.length; index++) { for (let index = 0; index < data.length; index++) {
data[index] = data[index] = -data[index] + 255;
((-((data[index] / 255) * 2 - 1) + 1) / 2) * 255;
} }
return sharp(data, { raw: info }).jpeg().toBuffer(); return sharp(data, { raw: info }).jpeg().toBuffer();
@@ -588,8 +592,6 @@ export class SCPGStream {
.then(({ data, info }) => { .then(({ data, info }) => {
// https://www.youtube.com/watch?v=CGys6CnnYSg // https://www.youtube.com/watch?v=CGys6CnnYSg
for (let index = 0; index < data.length; index += 3) { for (let index = 0; index < data.length; index += 3) {
data[index] = data[index];
data[index + 1] = data[index + 1];
data[index + 2] = -data[index + 2] + 255; data[index + 2] = -data[index + 2] + 255;
} }
@@ -623,14 +625,17 @@ export class SCPGStream {
materials.set(id, resolvedMaterial); materials.set(id, resolvedMaterial);
}); });
writeFileSync('test.sc2.json', JSON.stringify(sc2, null, 2));
function parseHierarchies(hierarchies: Hierarchy[], parent: Scene | Node) { function parseHierarchies(hierarchies: Hierarchy[], parent: Scene | Node) {
hierarchies.forEach((hierarchy) => { hierarchies.forEach((hierarchy) => {
const node = document.createNode(hierarchy.name); const node = document.createNode(hierarchy.name);
const components = times(
times(
hierarchy.components.count, hierarchy.components.count,
(index) => hierarchy.components[index.toString().padStart(4, '0')], (index) => hierarchy.components[index.toString().padStart(4, '0')],
).forEach((component) => { );
components.forEach((component, componentIndex) => {
switch (component['comp.typename']) { switch (component['comp.typename']) {
case 'TransformComponent': { case 'TransformComponent': {
const translation = new Vector3(); const translation = new Vector3();
@@ -660,25 +665,45 @@ export class SCPGStream {
} }
case 'RenderComponent': { case 'RenderComponent': {
const batchIds = range( let minLODDistanceBatchId: undefined | number = undefined;
component['rc.renderObj']['ro.batchCount'],
);
let minLODIndex = Infinity;
let minLODBatchId = Infinity;
batchIds.forEach((batchId) => { if (component['rc.renderObj']['rb0.lodIndex'] === -1) {
const thisLODIndex = minLODDistanceBatchId = 0;
component['rc.renderObj'][`rb${batchId}.lodIndex`]; } else {
const lodList = components.find(
(component) => component['comp.typename'] === 'LodComponent',
);
if (thisLODIndex <= minLODIndex) { if (
minLODIndex = thisLODIndex; lodList === undefined ||
minLODBatchId = batchId; lodList['comp.typename'] !== 'LodComponent' // type annotation hack
) {
throw new SyntaxError('Missing LodComponent');
} }
});
let minLODDistance = Infinity;
range(component['rc.renderObj']['ro.batchCount']).forEach(
(id) => {
const lodIndex =
component['rc.renderObj'][`rb${id}.lodIndex`];
const lodDistance =
lodList['lc.loddist'][`distance${lodIndex}`];
if (
lodDistance <= minLODDistance ||
lodDistance === MAX_FLOAT32
) {
minLODDistance = lodDistance;
minLODDistanceBatchId = id;
}
},
);
}
const batch = const batch =
component['rc.renderObj']['ro.batches'][ component['rc.renderObj']['ro.batches'][
minLODBatchId.toString().padStart(4, '0') minLODDistanceBatchId!.toString().padStart(4, '0')
]; ];
const polygonGroup = scg.find( const polygonGroup = scg.find(
(group) => group.id === batch['rb.datasource'], (group) => group.id === batch['rb.datasource'],
@@ -725,11 +750,7 @@ export class SCPGStream {
.setMaterial(material); .setMaterial(material);
indexedVertexTypes.forEach((type) => { indexedVertexTypes.forEach((type) => {
if (!(type in vertexAttributeGLTFName)) { if (!(type in vertexAttributeGLTFName)) return;
throw new TypeError(
`Unhandled vertex type GLTF name: ${VertexAttribute[type]} (${type})`,
);
}
const vertexSize = vertexAttributeVectorSizes[type]; const vertexSize = vertexAttributeVectorSizes[type];