mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: armor chunking
This commit is contained in:
1 parent
1f378137f1
commit
f9ccb06d1f
5 files changed
+122
-186
No files matched your search
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
import { NodeIO } from '@gltf-transform/core';
|
import { NodeIO } from '@gltf-transform/core';
|
||||||
import { readdir } from 'fs/promises';
|
import { readdir, writeFile } from 'fs/promises';
|
||||||
import { extractArmor } from '../../src/core/blitz/extractArmor';
|
import { extractArmor } from '../../src/core/blitz/extractArmor';
|
||||||
import { readXMLDVPL } from '../../src/core/blitz/readXMLDVPL';
|
import { readXMLDVPL } from '../../src/core/blitz/readXMLDVPL';
|
||||||
import { toUniqueId } from '../../src/core/blitz/toUniqueId';
|
import { toUniqueId } from '../../src/core/blitz/toUniqueId';
|
||||||
@@ -28,15 +28,15 @@ export async function buildTankArmors() {
|
|||||||
const tank = tanks.root[tankKey];
|
const tank = tanks.root[tankKey];
|
||||||
const id = toUniqueId(nation, tank.id);
|
const id = toUniqueId(nation, tank.id);
|
||||||
|
|
||||||
// if (id !== 7297) continue; // 60tp
|
if (id !== 7297) continue; // 60tp
|
||||||
if (id !== 5137) continue; // tiger ii
|
// if (id !== 5137) continue; // tiger ii
|
||||||
// if (id !== 24609) continue; // concept 1b
|
// if (id !== 24609) continue; // concept 1b
|
||||||
|
|
||||||
console.log(`Building armor ${id} @ ${nation}/${tankKey}`);
|
console.log(`Building armor ${id} @ ${nation}/${tankKey}`);
|
||||||
|
|
||||||
const armor = await extractArmor(DATA, `${nation}-${tankKey}`);
|
const model = await extractArmor(DATA, `${nation}-${tankKey}`);
|
||||||
|
|
||||||
await nodeIO.write('temp/armor/index.gltf', armor);
|
writeFile(`temp/armor/${id}.glb`, await nodeIO.writeBinary(model));
|
||||||
|
|
||||||
// writeFile('test.sc2.json', JSON.stringify(sc2, null, 2));
|
// writeFile('test.sc2.json', JSON.stringify(sc2, null, 2));
|
||||||
}
|
}
|
||||||
|
|||||||
+73
-118
@@ -1,47 +1,20 @@
|
|||||||
import { Document, Node, Scene } from '@gltf-transform/core';
|
import { Accessor, Document, Node, Scene } from '@gltf-transform/core';
|
||||||
import { range, times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import {
|
import { Matrix4, Quaternion, Vector3, Vector4Tuple } from 'three';
|
||||||
Matrix4,
|
|
||||||
Quaternion,
|
|
||||||
Vector3,
|
|
||||||
Vector3Tuple,
|
|
||||||
Vector4Tuple,
|
|
||||||
} from 'three';
|
|
||||||
import { Hierarchy, Sc2Stream } from '../streams/sc2';
|
import { Hierarchy, Sc2Stream } from '../streams/sc2';
|
||||||
import { ScgStream, vertexAttributeVectorSizes } from '../streams/scg';
|
import { ScgStream } from '../streams/scg';
|
||||||
import { VertexAttribute } from '../streams/scpg';
|
import { VertexAttribute } from '../streams/scpg';
|
||||||
|
import {
|
||||||
|
vertexAttributeGLTFName,
|
||||||
|
vertexAttributeGltfVectorSizes,
|
||||||
|
} from './extractModel';
|
||||||
import { readDVPLFile } from './readDVPLFile';
|
import { readDVPLFile } from './readDVPLFile';
|
||||||
|
|
||||||
Buffer.prototype.toJSON = function () {
|
Buffer.prototype.toJSON = function () {
|
||||||
return [...(this as Buffer)].map((int) => int.toString(16)).join('');
|
return [...(this as Buffer)].map((int) => int.toString(16)).join('');
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAX_FLOAT32 = 2 ** 127 * (2 - 2 ** -23);
|
export async function extractArmor(data: string, fileName: string) {
|
||||||
|
|
||||||
const vertexAttributeGLTFName: 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',
|
|
||||||
};
|
|
||||||
|
|
||||||
const vertexAttributeGltfVectorSizes = {
|
|
||||||
...vertexAttributeVectorSizes,
|
|
||||||
|
|
||||||
[VertexAttribute.TANGENT]: 4,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export async function extractArmor(
|
|
||||||
data: string,
|
|
||||||
fileName: string,
|
|
||||||
baseColor?: Vector3Tuple,
|
|
||||||
) {
|
|
||||||
const sc2Path = `${data}/3d/Tanks/CollisionMeshes/${fileName}.sc2.dvpl`;
|
const sc2Path = `${data}/3d/Tanks/CollisionMeshes/${fileName}.sc2.dvpl`;
|
||||||
const scgPath = `${data}/3d/Tanks/CollisionMeshes/${fileName}.scg.dvpl`;
|
const scgPath = `${data}/3d/Tanks/CollisionMeshes/${fileName}.scg.dvpl`;
|
||||||
const sc2 = new Sc2Stream(await readDVPLFile(sc2Path)).sc2();
|
const sc2 = new Sc2Stream(await readDVPLFile(sc2Path)).sc2();
|
||||||
@@ -88,58 +61,7 @@ export async function extractArmor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'RenderComponent': {
|
case 'RenderComponent': {
|
||||||
let minLODDistanceBatchId: undefined | number;
|
const batch = component['rc.renderObj']['ro.batches']['0000'];
|
||||||
|
|
||||||
if (component['rc.renderObj']['rb0.lodIndex'] === -1) {
|
|
||||||
minLODDistanceBatchId = 0;
|
|
||||||
} else {
|
|
||||||
const lodList = components.find(
|
|
||||||
(component) => component['comp.typename'] === 'LodComponent',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
lodList === undefined ||
|
|
||||||
lodList['comp.typename'] !== 'LodComponent' // type annotation hack
|
|
||||||
) {
|
|
||||||
throw new SyntaxError('Missing LodComponent');
|
|
||||||
}
|
|
||||||
|
|
||||||
let minLODDistance = Infinity;
|
|
||||||
let isFirstMaxFloat = true;
|
|
||||||
let lodIndexStreak = 0;
|
|
||||||
let lodIndexStreakIndex: undefined | number = undefined;
|
|
||||||
|
|
||||||
range(component['rc.renderObj']['ro.batchCount']).forEach(
|
|
||||||
(id) => {
|
|
||||||
const lodIndex =
|
|
||||||
component['rc.renderObj'][`rb${id}.lodIndex`];
|
|
||||||
const lodDistance =
|
|
||||||
lodList['lc.loddist'][`distance${lodIndex}`];
|
|
||||||
|
|
||||||
if (lodIndexStreakIndex === lodIndex) {
|
|
||||||
lodIndexStreak++;
|
|
||||||
} else {
|
|
||||||
lodIndexStreak = 0;
|
|
||||||
lodIndexStreakIndex = lodIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
lodDistance < minLODDistance ||
|
|
||||||
(lodDistance === minLODDistance && lodIndexStreak > 0) ||
|
|
||||||
(lodDistance === MAX_FLOAT32 && isFirstMaxFloat)
|
|
||||||
) {
|
|
||||||
if (lodDistance === MAX_FLOAT32) isFirstMaxFloat = false;
|
|
||||||
minLODDistance = lodDistance;
|
|
||||||
minLODDistanceBatchId = id;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const batch =
|
|
||||||
component['rc.renderObj']['ro.batches'][
|
|
||||||
minLODDistanceBatchId!.toString().padStart(4, '0')
|
|
||||||
];
|
|
||||||
const polygonGroup = scg.get(batch['rb.datasource']);
|
const polygonGroup = scg.get(batch['rb.datasource']);
|
||||||
|
|
||||||
if (!polygonGroup) {
|
if (!polygonGroup) {
|
||||||
@@ -150,52 +72,85 @@ export async function extractArmor(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const indicesAccessor = document
|
const hardJointIndices = new Set<number>();
|
||||||
.createAccessor()
|
const vertexHardJointIndices = new Map<number, number>();
|
||||||
.setType('SCALAR')
|
const attributes = new Map<VertexAttribute, number[][]>();
|
||||||
.setArray(new Uint16Array(polygonGroup.indices))
|
|
||||||
.setBuffer(buffer);
|
|
||||||
const primitive = document
|
|
||||||
.createPrimitive()
|
|
||||||
.setIndices(indicesAccessor);
|
|
||||||
const attributes: Partial<Record<VertexAttribute, number[][]>> = {};
|
|
||||||
const indexedAttributes: VertexAttribute[] = [];
|
|
||||||
|
|
||||||
polygonGroup.vertices.forEach((vertex) => {
|
polygonGroup.vertices.forEach((vertex, index) => {
|
||||||
vertex.forEach(({ attribute, value }) => {
|
vertex.forEach(({ attribute, value }) => {
|
||||||
if (attributes[attribute] === undefined) {
|
if (!attributes.has(attribute)) {
|
||||||
attributes[attribute] = [];
|
attributes.set(attribute, []);
|
||||||
indexedAttributes.push(attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes[attribute]!.push(value);
|
attributes.get(attribute)!.push(value);
|
||||||
|
|
||||||
|
if (attribute === VertexAttribute.HARD_JOINTINDEX) {
|
||||||
|
hardJointIndices.add(value[0]);
|
||||||
|
vertexHardJointIndices.set(index, value[0]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (indexedAttributes.includes(VertexAttribute.TANGENT)) {
|
if (attributes.has(VertexAttribute.TANGENT)) {
|
||||||
attributes[VertexAttribute.TANGENT] = attributes[
|
attributes.set(
|
||||||
VertexAttribute.TANGENT
|
VertexAttribute.TANGENT,
|
||||||
]!.map((tangent) => [...tangent, 1]);
|
attributes
|
||||||
|
.get(VertexAttribute.TANGENT)!
|
||||||
|
.map((tangent) => [...tangent, 1]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
indexedAttributes.forEach((attribute) => {
|
const accessors = new Map<string, Accessor>();
|
||||||
const name = vertexAttributeGLTFName[attribute];
|
|
||||||
if (!name || primitive.getAttribute(name)) return;
|
|
||||||
const vertexSize = vertexAttributeGltfVectorSizes[attribute];
|
|
||||||
|
|
||||||
const attributeAccessor = document
|
attributes.forEach((value, attribute) => {
|
||||||
|
const name = vertexAttributeGLTFName[attribute];
|
||||||
|
if (!name || accessors.has(name)) return;
|
||||||
|
const vertexSize = vertexAttributeGltfVectorSizes[attribute];
|
||||||
|
const accessor = document
|
||||||
.createAccessor(name)
|
.createAccessor(name)
|
||||||
.setType(vertexSize === 1 ? 'SCALAR' : `VEC${vertexSize}`)
|
.setType(vertexSize === 1 ? 'SCALAR' : `VEC${vertexSize}`)
|
||||||
.setArray(new Float32Array(attributes[attribute]!.flat()))
|
.setArray(new Float32Array(value.flat()))
|
||||||
.setBuffer(buffer);
|
.setBuffer(buffer);
|
||||||
primitive.setAttribute(name, attributeAccessor);
|
|
||||||
|
accessors.set(name, accessor);
|
||||||
});
|
});
|
||||||
|
|
||||||
const mesh = document
|
hardJointIndices.forEach((hardJointIndex) => {
|
||||||
.createMesh(batch['##name'])
|
const hardJointNode = document.createNode(
|
||||||
.addPrimitive(primitive);
|
`armor_${hardJointIndex}`,
|
||||||
|
);
|
||||||
|
const mesh = document.createMesh(batch['##name']);
|
||||||
|
const indicesAccessor = document
|
||||||
|
.createAccessor()
|
||||||
|
.setType('SCALAR')
|
||||||
|
.setArray(
|
||||||
|
new Uint16Array(
|
||||||
|
polygonGroup.indices.filter((index) => {
|
||||||
|
if (!vertexHardJointIndices.has(index)) {
|
||||||
|
throw new Error(
|
||||||
|
`Missing vertex hard joint index for index ${index}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
node.setMesh(mesh);
|
return (
|
||||||
|
vertexHardJointIndices.get(index)! === hardJointIndex
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.setBuffer(buffer);
|
||||||
|
const primitive = document
|
||||||
|
.createPrimitive()
|
||||||
|
.setIndices(indicesAccessor);
|
||||||
|
|
||||||
|
accessors.forEach((accessor, name) => {
|
||||||
|
primitive.setAttribute(name, accessor);
|
||||||
|
});
|
||||||
|
|
||||||
|
mesh.addPrimitive(primitive);
|
||||||
|
hardJointNode.setMesh(mesh);
|
||||||
|
node.addChild(hardJointNode);
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,28 +16,28 @@ import { readDVPLFile } from './readDVPLFile';
|
|||||||
|
|
||||||
const MAX_FLOAT32 = 2 ** 127 * (2 - 2 ** -23);
|
const MAX_FLOAT32 = 2 ** 127 * (2 - 2 ** -23);
|
||||||
|
|
||||||
const vertexAttributeGLTFName: Partial<Record<VertexAttribute, string>> = {
|
export const vertexAttributeGLTFName: Partial<Record<VertexAttribute, string>> =
|
||||||
[VertexAttribute.VERTEX]: 'POSITION',
|
{
|
||||||
[VertexAttribute.NORMAL]: 'NORMAL',
|
[VertexAttribute.VERTEX]: 'POSITION',
|
||||||
[VertexAttribute.COLOR]: 'COLOR_0',
|
[VertexAttribute.NORMAL]: 'NORMAL',
|
||||||
[VertexAttribute.TEXCOORD0]: 'TEXCOORD_0',
|
[VertexAttribute.TEXCOORD0]: 'TEXCOORD_0',
|
||||||
[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.TANGENT]: 'TANGENT',
|
||||||
[VertexAttribute.JOINTINDEX]: 'JOINT_0',
|
[VertexAttribute.JOINTINDEX]: 'JOINT_0',
|
||||||
[VertexAttribute.JOINTWEIGHT]: 'WEIGHT_0',
|
[VertexAttribute.JOINTWEIGHT]: 'WEIGHT_0',
|
||||||
};
|
};
|
||||||
|
|
||||||
const vertexAttributeGltfVectorSizes = {
|
export const vertexAttributeGltfVectorSizes = {
|
||||||
...vertexAttributeVectorSizes,
|
...vertexAttributeVectorSizes,
|
||||||
|
|
||||||
[VertexAttribute.TANGENT]: 4,
|
[VertexAttribute.TANGENT]: 4,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
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(
|
export async function extractModel(
|
||||||
@@ -283,27 +283,28 @@ export async function extractModel(
|
|||||||
.createPrimitive()
|
.createPrimitive()
|
||||||
.setIndices(indicesAccessor)
|
.setIndices(indicesAccessor)
|
||||||
.setMaterial(material);
|
.setMaterial(material);
|
||||||
const attributes: Partial<Record<VertexAttribute, number[][]>> = {};
|
const attributes = new Map<VertexAttribute, number[][]>();
|
||||||
const indexedAttributes: VertexAttribute[] = [];
|
|
||||||
|
|
||||||
polygonGroup.vertices.forEach((vertex) => {
|
polygonGroup.vertices.forEach((vertex) => {
|
||||||
vertex.forEach(({ attribute, value }) => {
|
vertex.forEach(({ attribute, value }) => {
|
||||||
if (attributes[attribute] === undefined) {
|
if (!attributes.has(attribute)) {
|
||||||
attributes[attribute] = [];
|
attributes.set(attribute, []);
|
||||||
indexedAttributes.push(attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes[attribute]!.push(value);
|
attributes.get(attribute)!.push(value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (indexedAttributes.includes(VertexAttribute.TANGENT)) {
|
if (attributes.has(VertexAttribute.TANGENT)) {
|
||||||
attributes[VertexAttribute.TANGENT] = attributes[
|
attributes.set(
|
||||||
VertexAttribute.TANGENT
|
VertexAttribute.TANGENT,
|
||||||
]!.map((tangent) => [...tangent, 1]);
|
attributes
|
||||||
|
.get(VertexAttribute.TANGENT)!
|
||||||
|
.map((tangent) => [...tangent, 1]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
indexedAttributes.forEach((attribute) => {
|
attributes.forEach((value, attribute) => {
|
||||||
const name = vertexAttributeGLTFName[attribute];
|
const name = vertexAttributeGLTFName[attribute];
|
||||||
if (!name || primitive.getAttribute(name)) return;
|
if (!name || primitive.getAttribute(name)) return;
|
||||||
const vertexSize = vertexAttributeGltfVectorSizes[attribute];
|
const vertexSize = vertexAttributeGltfVectorSizes[attribute];
|
||||||
@@ -311,7 +312,7 @@ export async function extractModel(
|
|||||||
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(attributes[attribute]!.flat()))
|
.setArray(new Float32Array(value.flat()))
|
||||||
.setBuffer(buffer);
|
.setBuffer(buffer);
|
||||||
primitive.setAttribute(name, attributeAccessor);
|
primitive.setAttribute(name, attributeAccessor);
|
||||||
});
|
});
|
||||||
|
|||||||
+16
-36
@@ -23,6 +23,10 @@ interface BlitzkriegPolygonGroup {
|
|||||||
indices: number[];
|
indices: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const vertexAttributesArray = Object.values(VertexAttribute).filter(
|
||||||
|
(value) => typeof value === 'number',
|
||||||
|
);
|
||||||
|
|
||||||
export const vertexAttributeVectorSizes = {
|
export const vertexAttributeVectorSizes = {
|
||||||
[VertexAttribute.VERTEX]: 3,
|
[VertexAttribute.VERTEX]: 3,
|
||||||
[VertexAttribute.NORMAL]: 3,
|
[VertexAttribute.NORMAL]: 3,
|
||||||
@@ -66,24 +70,6 @@ export class ScgStream extends ScpgStream {
|
|||||||
polygonGroupsRaw.push(polygonGroupRaw);
|
polygonGroupsRaw.push(polygonGroupRaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeFile(
|
|
||||||
// 'test.scg.txt',
|
|
||||||
// polygonGroupsRaw
|
|
||||||
// .map((polygonGroupRaw) => {
|
|
||||||
// const floats: number[] = [];
|
|
||||||
// const stream = new BufferStream(polygonGroupRaw.vertices);
|
|
||||||
|
|
||||||
// while (stream.readRemainingLength()) {
|
|
||||||
// floats.push(stream.float());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return `format: ${polygonGroupRaw.vertexFormat.toString(
|
|
||||||
// 2,
|
|
||||||
// )}\n\n${floats.join('\n')}`;
|
|
||||||
// })
|
|
||||||
// .join('\n\n#####\n\n'),
|
|
||||||
// );
|
|
||||||
|
|
||||||
polygonGroupsRaw.forEach((polygonGroupRaw) => {
|
polygonGroupsRaw.forEach((polygonGroupRaw) => {
|
||||||
const indicesStream = new ScpgStream(polygonGroupRaw.indices);
|
const indicesStream = new ScpgStream(polygonGroupRaw.indices);
|
||||||
const indices: number[] = [];
|
const indices: number[] = [];
|
||||||
@@ -112,14 +98,10 @@ export class ScgStream extends ScpgStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let index = 0; index < polygonGroupRaw.vertexCount; index++) {
|
for (let index = 0; index < polygonGroupRaw.vertexCount; index++) {
|
||||||
vertices[index] = format.map((attribute) => {
|
vertices[index] = format.map((attribute) => ({
|
||||||
return {
|
attribute,
|
||||||
attribute,
|
value: verticesStream.vectorN(vertexAttributeVectorSizes[attribute]),
|
||||||
value: verticesStream.vectorN(
|
}));
|
||||||
vertexAttributeVectorSizes[attribute],
|
|
||||||
),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verticesStream.readRemainingLength() !== 0) {
|
if (verticesStream.readRemainingLength() !== 0) {
|
||||||
@@ -139,17 +121,15 @@ export class ScgStream extends ScpgStream {
|
|||||||
const format: VertexAttribute[] = [];
|
const format: VertexAttribute[] = [];
|
||||||
let stride = 0;
|
let stride = 0;
|
||||||
|
|
||||||
Object.values(VertexAttribute)
|
vertexAttributesArray.forEach((attributeUntyped) => {
|
||||||
.filter((value) => typeof value === 'number')
|
const attribute = attributeUntyped as VertexAttribute;
|
||||||
.forEach((attributeUntyped) => {
|
const mask = 1 << attribute;
|
||||||
const attribute = attributeUntyped as VertexAttribute;
|
|
||||||
const mask = 1 << attribute;
|
|
||||||
|
|
||||||
if (formatInt & mask) {
|
if (formatInt & mask) {
|
||||||
format.push(attribute);
|
format.push(attribute);
|
||||||
stride += vertexAttributeVectorSizes[attribute] * 4;
|
stride += vertexAttributeVectorSizes[attribute] * 4;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { format, stride };
|
return { format, stride };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user