diff --git a/.vscode/settings.json b/.vscode/settings.json index 523603036..2d6a976a9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,11 +9,14 @@ "bepaint", "BINORMAL", "blitzstars", + "classname", "cleandist", "cockroackdb", "corsproxy", "CUBETEXCOORD", "customperiod", + "datasource", + "debugflags", "deburr", "decalmask", "diacriticless", @@ -32,6 +35,7 @@ "Leaderboard", "leaderboards", "MURICA", + "nmatname", "normalmap", "Oopsy", "outdir", diff --git a/package.json b/package.json index e628bff29..348fd9998 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ }, "license": "MIT", "dependencies": { + "@gltf-transform/core": "^3.9.0", "@octokit/plugin-throttling": "^8.1.3", "@octokit/rest": "^20.0.1", "@prisma/client": "^5.4.1", diff --git a/scripts/buildAssets.ts b/scripts/buildAssets.ts index b34247ce3..80b5ce6fd 100644 --- a/scripts/buildAssets.ts +++ b/scripts/buildAssets.ts @@ -1,6 +1,7 @@ import { config } from 'dotenv'; import { existsSync, writeFileSync } from 'fs'; import { mkdir, readdir, rm } from 'fs/promises'; +import { times } from 'lodash'; import { argv } from 'process'; import sharp from 'sharp'; import { TankType } from '../src/components/Tanks'; @@ -25,6 +26,9 @@ import { TurretDefinition, } from '../src/core/blitzkrieg/tankDefinitions'; +(BigInt.prototype as any).toJSON = function () { + return this.toString(); +}; config(); // WARNING! MOST OF THESE TYPES ARE NOT EXHAUSTIVE! @@ -502,7 +506,7 @@ if (allTargets || targets?.includes('tankModels')) { const nationVehicleId = tank.id; const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1; - // if (id !== 897) continue; + if (id !== 6753) continue; console.log(`Building model ${id} @ ${nation}/${tankIndex}`); const parameters = await readYAMLDVPL( @@ -520,8 +524,35 @@ if (allTargets || targets?.includes('tankModels')) { `${DATA}/${DOI['3d']}/${scgPath}.dvpl`, ); - const sc2 = sc2Stream.consumeSC2(); const scg = scgStream.consumeSCG(); + const sc2 = sc2Stream.consumeSC2(); + + sc2['#hierarchy'][0]['#hierarchy']!.forEach((hierarchy) => { + times( + hierarchy.components.count, + (index) => hierarchy.components[index.toString().padStart(4, '0')], + ).forEach((component) => { + if (component['comp.typename'] !== 'RenderComponent') return; + + hierarchy.name, + times( + component['rc.renderObj']['ro.batchCount'], + (index) => + component['rc.renderObj']['ro.batches'][ + index.toString().padStart(4, '0') + ], + ).map((batch) => { + const polygonGroup = scg.find( + (group) => group.id === batch['rb.datasource'], + ); + + console.log(batch['rb.datasource'], polygonGroup !== undefined); + }); + }); + }); + + // writeFile('test.sc2.json', JSON.stringify(sc2, null, 2)); + // writeFile('test.scg.json', JSON.stringify(scg, null, 2)); } }), ); diff --git a/src/core/blitz/SCPGStream.ts b/src/core/blitz/SCPGStream.ts index 5cb39fe60..70d333cde 100644 --- a/src/core/blitz/SCPGStream.ts +++ b/src/core/blitz/SCPGStream.ts @@ -1,4 +1,5 @@ import { times } from 'lodash'; +import { SC2 } from '../../types/sc2'; import { readDVPL } from './readDVPL'; import { readDVPLFile } from './readDVPLFile'; @@ -70,6 +71,12 @@ interface PolygonGroupRaw { vertexFormat: number; vertices: Buffer; } +type BlitzkriegVertex = { type: VertexType; value: number[] }[]; +interface BlitzkriegPolygonGroup { + id: bigint; + vertices: BlitzkriegVertex[]; + indices: number[]; +} const VECTOR_SIZES = [ [VertexType.COLOR, VertexType.FLEXIBILITY, VertexType.HARD_JOINTINDEX], @@ -216,7 +223,7 @@ export class SCPGStream { } const verticesStream = new SCPGStream(polygonGroupRaw.vertices); - const vertices: Partial>[] = []; + const vertices: BlitzkriegVertex[] = []; const vertexFormatBuffer = Buffer.alloc(4); vertexFormatBuffer.writeUInt32LE(polygonGroupRaw.vertexFormat, 0); const vertexFormat = [...vertexFormatBuffer] @@ -230,9 +237,11 @@ export class SCPGStream { vertexFormat.forEach((type) => { const resolved = VECTOR_SIZES.some((types, size) => { if (types.includes(type)) { - if (!(index in vertices)) vertices[index] = {}; - vertices[index][type] = verticesStream.consumeVectorN(size + 1); - + if (!(index in vertices)) vertices[index] = []; + vertices[index].push({ + type, + value: verticesStream.consumeVectorN(size + 1), + }); return true; } }); @@ -243,7 +252,11 @@ export class SCPGStream { }); } - return { vertices, indices }; + return { + id: polygonGroupRaw['#id'].readBigUInt64LE(), + vertices, + indices, + } satisfies BlitzkriegPolygonGroup; }); return polygonGroups; @@ -271,7 +284,7 @@ export class SCPGStream { } consumeSC2() { this.consumeSC2Header(); - return this.consumeKA(); + return this.consumeKA() as SC2; } consumeKAValue( diff --git a/src/types/sc2.ts b/src/types/sc2.ts new file mode 100644 index 000000000..29bc9dfea --- /dev/null +++ b/src/types/sc2.ts @@ -0,0 +1,144 @@ +export interface SC2 { + '#dataNodes': DataNode[]; + '#hierarchy': Hierarchy[]; + '#sceneComponents': SceneComponents; +} + +type DataNode = { + '##name': 'NMaterial'; + '#id': Buffer; + materialName: string; + parentMaterialKey?: string; + configCount?: number; + qualityGroup?: string; + fxName?: string; +} & Record<`configArchive_${number}`, ConfigArchive>; + +interface ConfigArchive { + configName: string; + fxName: string; + properties: Properties; + textureSampleStates: TextureSampleStates; + textures: Textures; + customCullMode?: number; + enabledPresets?: EnabledPresets; + flags?: Flags; +} + +interface EnabledPresets { + AlphaTest: boolean; +} + +interface Flags { + AMBIENT_ATTENUATION_BOX: number; +} + +interface Properties { + alphatestThreshold: Buffer; + attenuationBoxHalfSize: Buffer; + attenuationBoxSmoothness: Buffer; + baseColorFactor?: Buffer; + decalTileCoordScale?: Buffer; + inGlossiness: Buffer; + inSpecularity: Buffer; + metalFresnelReflectance: Buffer; + metallicRoughnessFactor?: Buffer; +} + +interface TextureSampleStates { + albedo: number; + baseColorMap: number; + baseNormalMap: number; + baseRMMap: number; + decalmask?: number; + maskMap: number; + miscMap: number; + normalmap: number; +} + +interface Textures { + albedo: string; + baseColorMap: string; + baseNormalMap: string; + baseRMMap: string; + decalmask?: string; + maskMap: string; + miscMap: string; + normalmap: string; +} + +interface Hierarchy { + '##name': 'Entity'; + '#hierarchy'?: Hierarchy[]; + components: Components; + flags: number; + id: number; + name: string; +} + +// 0001, 0002, 0003...: Component +type Components = { + count: number; +} & Record; + +type LODDistance = Record<`distance${number}`, number>; + +// 0001, 0002, 0003... +type RoBatches = Record< + string, + { + '##name': 'RenderBatch'; + 'rb.aabbox': RbAabbox; + 'rb.classname': 'RenderBatch'; + 'rb.datasource': bigint; + 'rb.nmatname': string; + 'rb.sortingKey': number; + } +>; + +interface RbAabbox { + minimum: number[]; + maximum: number[]; +} + +type Component = + | { + 'comp.typename': 'TransformComponent'; + 'tc.localRotation': number[]; + 'tc.localScale': number[]; + 'tc.localTranslation': number[]; + 'tc.worldRotation': number[]; + 'tc.worldScale': number[]; + 'tc.worldTranslation': number[]; + } + | { + 'comp.typename': 'RenderComponent'; + 'rc.renderObj': { + '##name': 'Mesh'; + 'ro.batchCount': number; + 'ro.batches': RoBatches; + 'ro.debugflags': number; + 'ro.flags': number; + 'ro.notShadowOnly': boolean; + 'ro.sOclCull': boolean; + 'ro.sOclIndex': number; + } & Record<`rb${number}.${'lodIndex' | 'switchIndex'}`, number>; + } + | { + 'comp.typename': 'SlotComponent'; + 'sc.attachmentRotation': number[]; + 'sc.attachmentScale': number[]; + 'sc.attachmentTranslation': number[]; + 'sc.configFilePath': string; + 'sc.slotName': string; + 'sc.template': string; + 'sc.typeFiltersCount': number; + } + | { + 'comp.typename': 'LodComponent'; + 'lc.loddist': LODDistance; + }; + +interface SceneComponents { + count: number; +} diff --git a/yarn.lock b/yarn.lock index 80612cbd4..9725b6fe6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -826,6 +826,13 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9" integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A== +"@gltf-transform/core@^3.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@gltf-transform/core/-/core-3.9.0.tgz#c63d5d0a2cf2d00f2a5f8faa5c5c6c6187db06c2" + integrity sha512-sLIUFqPIx8ATPDRwC9G/TXh4C7Q+w9z7DzXVMiNNReSVsAemTmZACqwCYpDW4jkUm3pbmz5/h9I9HXokBHbJ2w== + dependencies: + property-graph "^1.3.1" + "@img/sharp-darwin-arm64@0.33.1": version "0.33.1" resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.1.tgz#9d3cb0e4899b10b003608a018877b45b6db02861" @@ -5109,6 +5116,11 @@ prop-types@^15.6.0: object-assign "^4.1.1" react-is "^16.13.1" +property-graph@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/property-graph/-/property-graph-1.3.1.tgz#25fb2c8040ba9b15f04cda90a443f3f844505293" + integrity sha512-gei3N/bHWJdCItJ4blnlGWd9iauEZI+JZYj/A0D177XSI01+QhiJGAVscYBhe3Yywow3A2QJzVtsO2P+UgrRRQ== + property-information@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.4.0.tgz#6bc4c618b0c2d68b3bb8b552cbb97f8e300a0f82"