feat: attempting textures

This commit is contained in:
TrèsAbhi committed 2023-12-19 14:32:08 -06:00
1 parent 6d2bb7b131
commit f61725a021
6 files changed
+281 -145

No files matched your search

+3
View File
@@ -8,6 +8,7 @@
"atgm",
"bepaint",
"BINORMAL",
"Binormals",
"blitzstars",
"classname",
"cleandist",
@@ -27,6 +28,7 @@
"fullstats",
"fuzzysort",
"gamemode",
"GLTF",
"immer",
"JOINTINDEX",
"JOINTWEIGHT",
@@ -43,6 +45,7 @@
"ownedtanks",
"playerachievements",
"playerinfo",
"pngjs",
"ratingsleaderboard",
"ratingsplot",
"researchable",
+4
View File
@@ -53,6 +53,8 @@
"lodash": "^4.17.21",
"markdown-escape": "^2.0.0",
"next": "^14.0.3",
"parse-dds": "^1.2.1",
"pngjs": "^7.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-fuzzysort": "^1.0.1",
@@ -75,6 +77,8 @@
"@types/lz4": "^0.6.4",
"@types/markdown-escape": "^1.1.0",
"@types/node": "^20.6.5",
"@types/parse-dds": "^1.0.3",
"@types/pngjs": "^6.0.4",
"@types/react": "^18.2.22",
"@types/three": "^0.159.0",
"@vanilla-extract/next-plugin": "^2.3.1",
+187 -88
View File
@@ -1,14 +1,18 @@
import { Document, Node, NodeIO, Scene } from '@gltf-transform/core';
import { Document, Material, Node, NodeIO, Scene } from '@gltf-transform/core';
import { config } from 'dotenv';
import { existsSync, writeFileSync } from 'fs';
import { mkdir, readdir, rm } from 'fs/promises';
import { times } from 'lodash';
import { mkdir, readFile, readdir, rm } from 'fs/promises';
import { range, times } from 'lodash';
import { argv } from 'process';
import sharp from 'sharp';
import { Matrix4, Quaternion, Vector3, Vector4Tuple } from 'three';
import { TankType } from '../src/components/Tanks';
import { NATION_IDS } from '../src/constants/nations';
import { SCPGStream, VertexType } from '../src/core/blitz/SCPGStream';
import {
SCPGStream,
VertexAttribute,
vertexAttributeVectorSizes,
} from '../src/core/blitz/SCPGStream';
import { readBase64DVPL } from '../src/core/blitz/readBase64DVPL';
import { readDVPLFile } from '../src/core/blitz/readDVPLFile';
import { readStringDVPL } from '../src/core/blitz/readStringDVPL';
@@ -29,6 +33,19 @@ import {
} from '../src/core/blitzkrieg/tankDefinitions';
import { Hierarchy } from '../src/types/sc2';
const vertexTypeGLTFName: Partial<Record<VertexAttribute, string>> = {
[VertexAttribute.VERTEX]: 'POSITION',
[VertexAttribute.NORMAL]: 'NORMAL',
[VertexAttribute.COLOR]: 'COLOR_0',
[VertexAttribute.TEXCOORD0]: 'TEXCOORD_0',
[VertexAttribute.TEXCOORD1]: 'TEXCOORD_0',
[VertexAttribute.TEXCOORD2]: 'TEXCOORD_0',
[VertexAttribute.TEXCOORD3]: 'TEXCOORD_0',
[VertexAttribute.TANGENT]: 'TANGENT',
[VertexAttribute.JOINTINDEX]: 'JOINT_0',
[VertexAttribute.JOINTWEIGHT]: 'WEIGHT_0',
};
(BigInt.prototype as any).toJSON = function () {
return this.toString();
};
@@ -533,6 +550,30 @@ if (allTargets || targets?.includes('tankModels')) {
const document = new Document();
const scene = document.createScene();
const buffer = document.createBuffer();
const materials: Record<string, Material> = {};
// writeFileSync(
// 'test.txt',
// scg
// .map((group) =>
// group.vertices
// .map((vertex) =>
// vertex
// .map(
// (vertexProperty) =>
// `${VertexAttribute[vertexProperty.type].padEnd(
// 14,
// ' ',
// )} ${vertexProperty.value
// .map((value) => value.toString().padEnd(25, ' '))
// .join(' ')}`,
// )
// .join('\n'),
// )
// .join('\n\n'),
// )
// .join('\n\n#######\n\n'),
// );
function parseHierarchies(
hierarchies: Hierarchy[],
@@ -598,68 +639,95 @@ if (allTargets || targets?.includes('tankModels')) {
}
case 'RenderComponent': {
times(
const batchIds = range(
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'],
);
const unpackedVertices: number[] = [];
);
let minLODIndex = Infinity;
let minLODBatchId = Infinity;
if (!polygonGroup)
throw new Error(
`Missing polygon group ${batch['rb.datasource']}`,
);
batchIds.forEach((batchId) => {
const thisLODIndex =
component['rc.renderObj'][`rb${batchId}.lodIndex`];
polygonGroup.vertices.forEach((vertex) => {
vertex.map((vertexItem) => {
switch (vertexItem.type) {
case VertexType.VERTEX: {
unpackedVertices.push(...vertexItem.value);
break;
}
// default:
// throw new TypeError(
// `Unhandled vertex type: ${
// VertexType[vertexItem.type]
// } (${vertexItem.type})`,
// );
}
});
});
const vertexAccessor = document
.createAccessor()
.setType('VEC3')
.setArray(new Float32Array(unpackedVertices))
.setBuffer(buffer);
const indexAccessor = document
.createAccessor()
.setType('SCALAR')
.setArray(new Uint16Array(polygonGroup.indices))
.setBuffer(buffer);
const primitive = document
.createPrimitive()
.setIndices(indexAccessor)
.setAttribute('POSITION', vertexAccessor);
const mesh = document
.createMesh(batch['##name'])
.addPrimitive(primitive);
const polygons = document
.createNode(batch['##name'])
.setMesh(mesh);
node.addChild(polygons);
if (thisLODIndex <= minLODIndex) {
minLODIndex = thisLODIndex;
minLODBatchId = batchId;
}
});
const batch =
component['rc.renderObj']['ro.batches'][
minLODBatchId.toString().padStart(4, '0')
];
const polygonGroup = scg.find(
(group) => group.id === batch['rb.datasource'],
);
const material = materials[batch['rb.nmatname']];
if (!polygonGroup) {
throw new Error(
`Missing polygon group ${batch['rb.datasource']}`,
);
}
const indexedVertexTypes: VertexAttribute[] = [];
const unpackedVertices: Partial<
Record<VertexAttribute, number[]>
> = {};
polygonGroup.vertices.forEach((vertex) => {
vertex.map((vertexItem) => {
if (!(vertexItem.type in unpackedVertices)) {
unpackedVertices[vertexItem.type] = [];
indexedVertexTypes.push(vertexItem.type);
}
unpackedVertices[vertexItem.type]!.push(
...vertexItem.value,
);
});
});
const indexAccessor = document
.createAccessor()
.setType('SCALAR')
.setArray(new Uint16Array(polygonGroup.indices))
.setBuffer(buffer);
const primitive = document
.createPrimitive()
.setIndices(indexAccessor)
.setMaterial(material);
indexedVertexTypes.forEach((type) => {
if (!(type in vertexTypeGLTFName)) {
return;
// throw new TypeError(
// `Unhandled vertex type GLTF name: ${VertexType[type]} (${type})`,
// );
}
const vertexSize = vertexAttributeVectorSizes[type];
if (!primitive.getAttribute(vertexTypeGLTFName[type]!)) {
primitive.setAttribute(
vertexTypeGLTFName[type]!,
document
.createAccessor()
.setType(
vertexSize === 1 ? 'SCALAR' : `VEC${vertexSize}`,
)
.setArray(new Float32Array(unpackedVertices[type]!))
.setBuffer(buffer),
);
}
});
const mesh = document
.createMesh(batch['##name'])
.addPrimitive(primitive);
node.setMesh(mesh);
break;
}
@@ -678,35 +746,66 @@ if (allTargets || targets?.includes('tankModels')) {
});
}
await Promise.all(
sc2['#dataNodes'].map(async (node) => {
const material = document.createMaterial(node.materialName);
// if (typeof node.configCount === 'number') {
// try {
// const config = node.configArchive_0;
// const texturePath = `${DATA}/${DOI['3d']}/${dirname(
// parameters.resourcesPath.blitzModelPath,
// )}/${config.textures.albedo.replace(
// /\.tex$/,
// '.dx11.dds.dvpl',
// )}`;
// console.log(texturePath);
// const dds = await readDVPLFile(texturePath);
// const image = parseDDS(dds).images[0];
// const textureArray = new Uint8Array(
// dds,
// image.offset,
// image.length,
// );
// const png = new PNG({
// width: image.shape[0],
// height: image.shape[1],
// });
// for (var index = 0; index < textureArray.length; index += 4) {
// png.data[index] = textureArray[index];
// png.data[index + 1] = textureArray[index + 1];
// png.data[index + 2] = textureArray[index + 2];
// png.data[index + 3] = textureArray[index + 3];
// }
// const pngBuffer = PNG.sync.write(png);
// const texture = document
// .createTexture(config.configName)
// .setImage(await readFile('C:/Users/coola/Downloads/33e.jpg'));
// material.setBaseColorTexture(texture);
// } catch (error) {
// // console.log(error);
// }
// }
if (node.configCount) {
console.log('uhhh');
const config = node.configArchive_0;
const texturePath = 'C:/Users/coola/Downloads/33e.jpg';
const textureBuffer = await readFile(texturePath);
const texture = document.createTexture(config?.configName);
texture.setImage(textureBuffer);
material.setBaseColorTexture(texture);
}
materials[node['#id'].readBigUInt64LE().toString()] = material;
}),
);
parseHierarchies(sc2['#hierarchy'], scene);
writeFileSync('test.glb', await new NodeIO().writeBinary(document));
// 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));
}
}),
);
+56 -54
View File
@@ -34,7 +34,7 @@ enum KAType {
UNCONFIRMED_TRANSFORM = 29,
}
export enum VertexType {
export enum VertexAttribute {
VERTEX,
NORMAL,
COLOR,
@@ -46,6 +46,7 @@ export enum VertexType {
BINORMAL,
HARD_JOINTINDEX,
PIVOT4,
PIVOT_DEPRECATED,
FLEXIBILITY,
ANGLE_SIN_COS,
JOINTINDEX,
@@ -56,6 +57,29 @@ export enum VertexType {
CUBETEXCOORD3,
}
export const vertexAttributeVectorSizes = {
[VertexAttribute.VERTEX]: 3,
[VertexAttribute.NORMAL]: 3,
[VertexAttribute.COLOR]: 1,
[VertexAttribute.TEXCOORD0]: 2,
[VertexAttribute.TEXCOORD1]: 2,
[VertexAttribute.TEXCOORD2]: 2,
[VertexAttribute.TEXCOORD3]: 2,
[VertexAttribute.TANGENT]: 3,
[VertexAttribute.BINORMAL]: 3,
[VertexAttribute.HARD_JOINTINDEX]: 1,
[VertexAttribute.CUBETEXCOORD0]: 3,
[VertexAttribute.CUBETEXCOORD1]: 3,
[VertexAttribute.CUBETEXCOORD2]: 3,
[VertexAttribute.CUBETEXCOORD3]: 3,
[VertexAttribute.PIVOT4]: 4,
[VertexAttribute.PIVOT_DEPRECATED]: 3,
[VertexAttribute.FLEXIBILITY]: 1,
[VertexAttribute.ANGLE_SIN_COS]: 2,
[VertexAttribute.JOINTINDEX]: 4,
[VertexAttribute.JOINTWEIGHT]: 4,
} as const;
interface PolygonGroupRaw {
'##name': 'PolygonGroup';
'#id': Buffer;
@@ -71,39 +95,13 @@ interface PolygonGroupRaw {
vertexFormat: number;
vertices: Buffer;
}
type BlitzkriegVertex = { type: VertexType; value: number[] }[];
type BlitzkriegVertex = { type: VertexAttribute; value: number[] }[];
interface BlitzkriegPolygonGroup {
id: bigint;
vertices: BlitzkriegVertex[];
indices: number[];
}
const VECTOR_SIZES = [
[VertexType.COLOR, VertexType.FLEXIBILITY, VertexType.HARD_JOINTINDEX],
[
VertexType.TEXCOORD0,
VertexType.TEXCOORD1,
VertexType.TEXCOORD2,
VertexType.TEXCOORD3,
VertexType.ANGLE_SIN_COS,
],
[
VertexType.VERTEX,
VertexType.NORMAL,
VertexType.BINORMAL,
VertexType.CUBETEXCOORD0,
VertexType.CUBETEXCOORD1,
VertexType.CUBETEXCOORD2,
VertexType.CUBETEXCOORD3,
],
[
VertexType.PIVOT4,
VertexType.JOINTINDEX,
VertexType.JOINTWEIGHT,
VertexType.TANGENT,
],
];
export class SCPGStream {
public index = 0;
constructor(public buffer: Buffer) {}
@@ -197,6 +195,21 @@ export class SCPGStream {
return this.consumeMatrixN(4);
}
getVertexStride(vertexFormat: number) {
const flags = vertexFormat.toString(2);
return Object.values(VertexAttribute)
.filter((value) => typeof value === 'number')
.reduce(
(accumulator, attribute) =>
flags[attribute as VertexAttribute] === '1'
? accumulator +
vertexAttributeVectorSizes[attribute as VertexAttribute] * 4
: accumulator,
0,
);
}
consumeSCGHeader() {
return {
name: this.consumeAscii(4),
@@ -228,36 +241,25 @@ export class SCPGStream {
const verticesStream = new SCPGStream(polygonGroupRaw.vertices);
const vertices: BlitzkriegVertex[] = [];
const vertexFormatBuffer = Buffer.alloc(4);
vertexFormatBuffer.writeUInt32LE(polygonGroupRaw.vertexFormat, 0);
const vertexFormat = [...vertexFormatBuffer]
.map((number) => number.toString(2))
.join('')
.split('')
.map((bitString, index) => (bitString === '1' ? index : null))
.filter((type) => type !== null) as VertexType[];
const vertexFormat = (
polygonGroupRaw.vertexFormat
.toString(2)
.split('')
.map((bitString, index) =>
bitString === '1' ? index : null,
) as VertexAttribute[]
).filter((type) => type !== null);
for (let index = 0; index < polygonGroupRaw.vertexCount; index++) {
vertexFormat.forEach((type) => {
const resolved = VECTOR_SIZES.some((types, size) => {
if (types.includes(type)) {
if (!(index in vertices)) vertices[index] = [];
vertices[index].push({
type,
value: verticesStream.consumeVectorN(size + 1),
});
return true;
}
});
if (!resolved) {
throw new TypeError(`Unknown vertex type: ${type}`);
}
});
vertices[index] = vertexFormat.map((type) => ({
type,
value: verticesStream.consumeVectorN(
vertexAttributeVectorSizes[type],
),
}));
}
console.log(verticesStream.readRemaining().length);
return {
id: polygonGroupRaw['#id'].readBigUInt64LE(),
vertices,
+9 -3
View File
@@ -11,10 +11,16 @@ type DataNode = {
'#id': Buffer;
materialName: string;
parentMaterialKey?: string;
configCount?: number;
qualityGroup?: string;
fxName?: string;
} & Record<`configArchive_${number}`, ConfigArchive>;
} & (
| ({
configCount: number;
} & Record<`configArchive_${number}`, ConfigArchive>)
| {
configCount: undefined;
}
);
interface ConfigArchive {
configName: string;
@@ -103,7 +109,7 @@ interface RbAabbox {
maximum: number[];
}
type Component =
export type Component =
| {
'comp.typename': 'TransformComponent';
'tc.localRotation': Vector4Tuple;
+22
View File
@@ -2387,6 +2387,18 @@
resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz#90267db13f64d6e9ccb5ae3eac92786a7c77a516"
integrity sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==
"@types/parse-dds@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@types/parse-dds/-/parse-dds-1.0.3.tgz#a1bb00421906094510d88c64af978564d3c35c9b"
integrity sha512-LD9BruIpfP6Z9aAJ488h+PwtSwLzM0NO+Rs1l/zjcPLBTqR31OnIGmAvJYhGiHhbOn7R0YXw1gL5bImNiK6HNw==
"@types/pngjs@^6.0.4":
version "6.0.4"
resolved "https://registry.yarnpkg.com/@types/pngjs/-/pngjs-6.0.4.tgz#9a457aebabd944efde1a773a0fa1d74933e8021b"
integrity sha512-atAK9xLKOnxiuArxcHovmnOUUGBZOQ3f0vCf43FnoKs6XnqiambT1kkJWmdo71IR+BoXSh+CueeFR0GfH3dTlQ==
dependencies:
"@types/node" "*"
"@types/prop-types@*":
version "15.7.11"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563"
@@ -4907,6 +4919,11 @@ parse-css-color@^0.2.1:
color-name "^1.1.4"
hex-rgb "^4.1.0"
parse-dds@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/parse-dds/-/parse-dds-1.2.1.tgz#b16533e00c4db91178b3460271361df4cb328164"
integrity sha512-oE88N8mzUi9d26tAaKVc0clxi6AjeTSWpLwgtcEVjXTYY1MW58w8FctmM9gGgvFd6YGu1/eFFYVeDVnlMsKBtA==
parse-json@^5.0.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
@@ -5032,6 +5049,11 @@ pkg-types@^1.0.3:
mlly "^1.2.0"
pathe "^1.1.0"
pngjs@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-7.0.0.tgz#a8b7446020ebbc6ac739db6c5415a65d17090e26"
integrity sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==
postcss-value-parser@^4.0.2, postcss-value-parser@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"