mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: faster models
This commit is contained in:
1 parent
0f29145ab0
commit
e05f02099d
8 files changed
+77
-34
No files matched your search
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:assets": "bun --filter=@blitzkit/scripts run build:assets",
|
"build:assets": "cd packages/scripts && bun run build:assets",
|
||||||
"build:averages": "bun --filter=@blitzkit/scripts run build:averages",
|
"build:averages": "bun --filter=@blitzkit/scripts run build:averages",
|
||||||
"build:bot": "rm -rf dist/bot && mkdir -p dist/bot/prisma && bun build src/index.ts src/workers/bot.ts src/workers/render.ts --outdir dist/bot --target=bun --splitting --sourcemap=inline --external sharp && cp -R src/lang dist/bot/lang && prisma generate && cp prisma/schema.prisma dist/bot/prisma/schema.prisma && cp -R node_modules/prisma/*.node dist/bot/prisma && cd dist/bot && bun init -y && bun add sharp",
|
"build:bot": "rm -rf dist/bot && mkdir -p dist/bot/prisma && bun build src/index.ts src/workers/bot.ts src/workers/render.ts --outdir dist/bot --target=bun --splitting --sourcemap=inline --external sharp && cp -R src/lang dist/bot/lang && prisma generate && cp prisma/schema.prisma dist/bot/prisma/schema.prisma && cp -R node_modules/prisma/*.node dist/bot/prisma && cd dist/bot && bun init -y && bun add sharp",
|
||||||
"build:website": "bun build:prisma && bun --filter=@blitzkit/website run build",
|
"build:website": "bun build:prisma && bun --filter=@blitzkit/website run build",
|
||||||
|
|||||||
@@ -9,17 +9,21 @@
|
|||||||
"setup:patches": "bun src/applyPatches.ts"
|
"setup:patches": "bun src/applyPatches.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"progress": "^2.0.3",
|
|
||||||
"@gltf-transform/core": "^3.10.1",
|
|
||||||
"decompress": "^4.2.1",
|
|
||||||
"csv-parse": "^5.5.6",
|
|
||||||
"googleapis": "^134.0.0",
|
|
||||||
"@blitzkit/core": "workspace:*",
|
"@blitzkit/core": "workspace:*",
|
||||||
"fast-xml-parser": "^4.4.1"
|
"@gltf-transform/core": "^4.0.10",
|
||||||
|
"@gltf-transform/extensions": "^4.0.10",
|
||||||
|
"@gltf-transform/functions": "^4.0.10",
|
||||||
|
"csv-parse": "^5.5.6",
|
||||||
|
"decompress": "^4.2.1",
|
||||||
|
"draco3dgltf": "^1.5.7",
|
||||||
|
"fast-xml-parser": "^4.4.1",
|
||||||
|
"googleapis": "^134.0.0",
|
||||||
|
"progress": "^2.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/decompress": "^4.2.7",
|
"@types/decompress": "^4.2.7",
|
||||||
"@types/progress": "^2.0.7",
|
"@types/draco3dgltf": "^1.4.3",
|
||||||
"@types/lodash": "^4.17.7"
|
"@types/lodash": "^4.17.7",
|
||||||
|
"@types/progress": "^2.0.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
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 draco3d from 'draco3dgltf';
|
||||||
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';
|
||||||
@@ -13,7 +15,12 @@ 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();
|
const nodeIO = new NodeIO()
|
||||||
|
.registerExtensions(ALL_EXTENSIONS)
|
||||||
|
.registerDependencies({
|
||||||
|
'draco3d.decoder': await draco3d.createDecoderModule(),
|
||||||
|
'draco3d.encoder': await draco3d.createEncoderModule(),
|
||||||
|
});
|
||||||
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'),
|
||||||
);
|
);
|
||||||
@@ -32,6 +39,7 @@ export async function tankModels() {
|
|||||||
if (tankKey.includes('tutorial_bot')) return;
|
if (tankKey.includes('tutorial_bot')) return;
|
||||||
|
|
||||||
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.dvpl`,
|
`${DATA}/3d/Tanks/Parameters/${nation}/${tankKey}.yaml.dvpl`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ export const vertexAttributeGLTFName: Partial<Record<VertexAttribute, string>> =
|
|||||||
[VertexAttribute.TEXCOORD1]: 'TEXCOORD_0',
|
[VertexAttribute.TEXCOORD1]: 'TEXCOORD_0',
|
||||||
[VertexAttribute.TEXCOORD2]: 'TEXCOORD_0',
|
[VertexAttribute.TEXCOORD2]: 'TEXCOORD_0',
|
||||||
[VertexAttribute.TEXCOORD3]: 'TEXCOORD_0',
|
[VertexAttribute.TEXCOORD3]: 'TEXCOORD_0',
|
||||||
[VertexAttribute.TANGENT]: 'TANGENT',
|
|
||||||
[VertexAttribute.JOINTINDEX]: 'JOINT_0',
|
|
||||||
[VertexAttribute.JOINTWEIGHT]: 'WEIGHT_0',
|
|
||||||
};
|
};
|
||||||
export const vertexAttributeGltfVectorSizes = {
|
export const vertexAttributeGltfVectorSizes = {
|
||||||
...vertexAttributeVectorSizes,
|
...vertexAttributeVectorSizes,
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import {
|
|||||||
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 { KHRDracoMeshCompression } from '@gltf-transform/extensions';
|
||||||
|
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 { Matrix4, Quaternion, Vector3, Vector4Tuple } from 'three';
|
||||||
@@ -223,7 +225,9 @@ export async function extractModel(data: string, path: string) {
|
|||||||
const primitive = document
|
const primitive = document
|
||||||
.createPrimitive()
|
.createPrimitive()
|
||||||
.setIndices(indicesAccessor)
|
.setIndices(indicesAccessor)
|
||||||
.setMaterial(material);
|
.setMaterial(material)
|
||||||
|
.setName(batchKey);
|
||||||
|
|
||||||
const attributes = new Map<VertexAttribute, number[][]>();
|
const attributes = new Map<VertexAttribute, number[][]>();
|
||||||
|
|
||||||
polygonGroup.vertices.forEach((vertex) => {
|
polygonGroup.vertices.forEach((vertex) => {
|
||||||
@@ -236,25 +240,23 @@ export async function extractModel(data: string, path: string) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (attributes.has(VertexAttribute.TANGENT)) {
|
|
||||||
attributes.set(
|
|
||||||
VertexAttribute.TANGENT,
|
|
||||||
attributes
|
|
||||||
.get(VertexAttribute.TANGENT)!
|
|
||||||
.map((tangent) => [...tangent, 1]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes.forEach((value, attribute) => {
|
attributes.forEach((value, attribute) => {
|
||||||
const name = vertexAttributeGLTFName[attribute];
|
const name = vertexAttributeGLTFName[attribute];
|
||||||
if (!name || primitive.getAttribute(name)) return;
|
|
||||||
const vertexSize = vertexAttributeGltfVectorSizes[attribute];
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
name === undefined ||
|
||||||
|
primitive.getAttribute(name) !== null
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
primitive.setAttribute(name, attributeAccessor);
|
primitive.setAttribute(name, attributeAccessor);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -288,5 +290,19 @@ export async function extractModel(data: string, path: string) {
|
|||||||
|
|
||||||
parseHierarchies(sc2['#hierarchy'], scene);
|
parseHierarchies(sc2['#hierarchy'], scene);
|
||||||
|
|
||||||
|
scene.addChild(document.createNode('test'));
|
||||||
|
|
||||||
|
await document.transform(prune({ keepAttributes: true }), dedup());
|
||||||
|
|
||||||
|
document
|
||||||
|
.createExtension(KHRDracoMeshCompression)
|
||||||
|
.setRequired(true)
|
||||||
|
.setEncoderOptions({
|
||||||
|
method: KHRDracoMeshCompression.EncoderMethod.EDGEBREAKER,
|
||||||
|
encodeSpeed: 0,
|
||||||
|
decodeSpeed: 0,
|
||||||
|
quantizationVolume: 'scene',
|
||||||
|
});
|
||||||
|
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
import { DdsReadStream, PvrReadStream } from '@blitzkit/core';
|
import { DdsReadStream, PvrReadStream } from '@blitzkit/core';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
import sharp from 'sharp';
|
import sharp, { JpegOptions } from 'sharp';
|
||||||
import { readDVPLFile } from '../readDVPLFile';
|
import { readDVPLFile } from '../readDVPLFile';
|
||||||
import { TextureMutation } from './constants';
|
import { TextureMutation } from './constants';
|
||||||
|
|
||||||
|
const jpegOptions: JpegOptions = {
|
||||||
|
quality: 20,
|
||||||
|
mozjpeg: true,
|
||||||
|
};
|
||||||
|
|
||||||
export async function readTexture(path: string, mutation?: TextureMutation) {
|
export async function readTexture(path: string, mutation?: TextureMutation) {
|
||||||
const ddsTexturePath = path.replace('.tex', '.dx11.dds.dvpl');
|
const ddsTexturePath = path.replace('.tex', '.dx11.dds.dvpl');
|
||||||
const isDds = existsSync(ddsTexturePath);
|
const isDds = existsSync(ddsTexturePath);
|
||||||
@@ -35,7 +40,7 @@ export async function readTexture(path: string, mutation?: TextureMutation) {
|
|||||||
raw.data[index + 3] = 255;
|
raw.data[index + 3] = 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await sharp(raw.data, { raw }).jpeg().toBuffer();
|
return await sharp(raw.data, { raw }).jpeg(jpegOptions).toBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
case TextureMutation.RoughnessMetallicness: {
|
case TextureMutation.RoughnessMetallicness: {
|
||||||
@@ -44,16 +49,16 @@ export async function readTexture(path: string, mutation?: TextureMutation) {
|
|||||||
});
|
});
|
||||||
const metallicness = sharp(raw.data, { raw })
|
const metallicness = sharp(raw.data, { raw })
|
||||||
.extractChannel('green')
|
.extractChannel('green')
|
||||||
.jpeg()
|
.jpeg(jpegOptions)
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
const roughness = sharp(raw.data, { raw })
|
const roughness = sharp(raw.data, { raw })
|
||||||
.extractChannel('alpha')
|
.extractChannel('alpha')
|
||||||
.jpeg()
|
.jpeg(jpegOptions)
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|
||||||
return await newImage
|
return await newImage
|
||||||
.joinChannel(await Promise.all([roughness, metallicness]))
|
.joinChannel(await Promise.all([roughness, metallicness]))
|
||||||
.jpeg()
|
.jpeg(jpegOptions)
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,15 +69,18 @@ export async function readTexture(path: string, mutation?: TextureMutation) {
|
|||||||
*/
|
*/
|
||||||
return await sharp(raw.data, { raw })
|
return await sharp(raw.data, { raw })
|
||||||
.extractChannel('alpha')
|
.extractChannel('alpha')
|
||||||
.jpeg()
|
.jpeg(jpegOptions)
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
case TextureMutation.Albedo: {
|
case TextureMutation.Albedo: {
|
||||||
return await sharp(raw.data, { raw }).removeAlpha().jpeg().toBuffer();
|
return await sharp(raw.data, { raw })
|
||||||
|
.removeAlpha()
|
||||||
|
.jpeg(jpegOptions)
|
||||||
|
.toBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return await sharp(raw.data, { raw }).jpeg().toBuffer();
|
return await sharp(raw.data, { raw }).jpeg(jpegOptions).toBuffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import { asset } from '@blitzkit/core';
|
|||||||
import { type ObjectMap, useLoader } from '@react-three/fiber';
|
import { type ObjectMap, useLoader } from '@react-three/fiber';
|
||||||
import { Mesh, MeshStandardMaterial } from 'three';
|
import { Mesh, MeshStandardMaterial } from 'three';
|
||||||
import { type GLTF, GLTFLoader } from 'three-stdlib';
|
import { type GLTF, GLTFLoader } from 'three-stdlib';
|
||||||
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||||
|
|
||||||
const cache: Record<
|
const cache: Record<
|
||||||
number,
|
number,
|
||||||
@@ -12,7 +13,16 @@ const cache: Record<
|
|||||||
> = {};
|
> = {};
|
||||||
|
|
||||||
export function useModel(id: number) {
|
export function useModel(id: number) {
|
||||||
const gltf = useLoader(GLTFLoader, asset(`3d/tanks/models/${id}.glb`));
|
const gltf = useLoader(
|
||||||
|
GLTFLoader,
|
||||||
|
asset(`3d/tanks/models/${id}.glb`),
|
||||||
|
(loader) => {
|
||||||
|
const dracoLoader = new DRACOLoader();
|
||||||
|
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');
|
||||||
|
loader.setDRACOLoader(dracoLoader);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (!cache[id]) {
|
if (!cache[id]) {
|
||||||
cache[id] = {
|
cache[id] = {
|
||||||
gltf,
|
gltf,
|
||||||
|
|||||||
Reference in new issue
Block a user