Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
const split = function (container: GLTFContainer, meshes: Array): GLTFContainer {
const json = container.json;
const logger = GLTFUtil.createLogger('@gltf-transform/split', LoggerVerbosity.INFO);
const bufferViewMap = {};
const removedBufferViews = [];
// Group bufferviews by mesh.
json.meshes.forEach((mesh) => {
if (meshes.indexOf(mesh.name) === -1) return;
mesh.primitives.forEach((prim) => {
if (prim.indices) markAccessor(json.accessors[prim.indices]);
Object.keys(prim.attributes).forEach((attrName) => {
markAccessor(json.accessors[prim.attributes[attrName]]);
});
function markAccessor(accessor) {
if (bufferViewMap[accessor.bufferView] === undefined) {
bufferViewMap[accessor.bufferView] = mesh.name;
const prune = function (container: GLTFContainer): GLTFContainer {
const json = container.json;
const logger = GLTFUtil.createLogger('@gltf-transform/prune', LoggerVerbosity.INFO);
// Find all accessors used for mesh data.
let meshAccessorIndices = [];
json.meshes.forEach((mesh) => {
mesh.primitives.forEach((primitive) => {
for (const semantic in primitive.attributes) {
meshAccessorIndices.push(primitive.attributes[semantic]);
}
if (primitive.indices) {
meshAccessorIndices.push(primitive.indices);
}
})
});
meshAccessorIndices = Array.from(new Set(meshAccessorIndices)); // dedupe
meshAccessorIndices.sort((a, b) => a > b ? 1 : -1); // sort ascending
return Promise.all(pending).then(() => {
const buffer = (canvas as any).toBuffer() as Buffer;
const arrayBuffer = GLTFUtil.trimBuffer(buffer);
GLTFUtil.addImage(container, 'atlas', arrayBuffer, atlasIsPNG ? 'image/png': 'image/jpeg');
const atlasImageIndex = container.json.images.length - 1;
// Reassign textures to atlas.
const imageMap = new Map();
images.forEach((i) => imageMap.set(i.index, i));
container.json.materials.forEach((material) => {
if (!material.pbrMetallicRoughness) return;
if (!material.pbrMetallicRoughness.baseColorTexture) return;
const baseColorTexture = material.pbrMetallicRoughness.baseColorTexture;
const textureIndex = baseColorTexture.index;
const textureDef = container.json.textures[textureIndex];
if (!imageMap.has(textureDef.source)) return;
const image = imageMap.get(textureDef.source);
baseColorTexture['extensions'] = baseColorTexture['extensions'] || {};
baseColorTexture['extensions'][KHR_TEXTURE_TRANSFORM] = {
offset: [image.x / atlasWidth, image.y / atlasHeight],
const primitives = [];
const meshes = container.json.meshes || [];
meshes.forEach((mesh) => {
mesh.primitives.forEach((primitive) => {
const position = container.getAccessorArray(primitive.attributes['POSITION']);
const cells = primitive.indices !== undefined ? container.getAccessorArray(primitive.indices) : undefined;
primitives.push({position, cells, def: primitive});
})
});
if (primitives.length === 0) {
logger.warn('No primitives found.');
return;
}
GLTFUtil.addImage(container, 'occlusion', TEXTURE_DATA, TEXTURE_MIME_TYPE);
container.json.textures.push({source: container.json.images.length - 1});
const occlusionTextureIndex = container.json.textures.length - 1;
let regl;
if (options.gl) {
const gl = options.gl(resolution, resolution);
gl.getExtension('OES_texture_float');
gl.getExtension('OES_element_index_uint');
regl = REGL({gl, extensions: ['OES_texture_float', 'OES_element_index_uint']});
}
// TODO: Implement baking such that primitives affect other primitives, and respect
// world transforms.
primitives.forEach((primitive, index) => {
logger.info(`Baking primitive ${index} / ${primitives.length}.`);
}
}
if (primitive.indices && duplicateAccessors.has(primitive.indices)) {
primitive.indices = duplicateAccessors.get(primitive.indices);
}
});
});
// Clean up.
const removedAccessors = Array.from(duplicateAccessors).map(([dup, _]) => dup);
removedAccessors.sort((a, b) => a > b ? -1 : 1); // sort descending
removedAccessors.forEach((index) => GLTFUtil.removeAccessor(container, index));
for (let i = container.json.bufferViews.length - 1; i >= 0; i--) {
const bufferView = container.json.bufferViews[i];
if (bufferView.byteLength === 0) {
GLTFUtil.removeBufferView(container, i);
}
}
return container;
}
const aoSampler = geoao(position, {cells, resolution, regl});
for (let i = 0; i < samples; i++) aoSampler.sample();
const ao = aoSampler.report();
aoSampler.dispose();
// Write UV set and add AO map.
const numVertices = ao.length;
const uv2Data = new Float32Array(numVertices * 2);
for (let i = 0; i < numVertices; i++) {
uv2Data[i * 2] = uv2Data[i * 2 + 1] = 1 - ao[i];
}
GLTFUtil.addAccessor(
container,
uv2Data,
'VEC2' as GLTF.AccessorType.VEC2,
AccessorComponentType.FLOAT,
numVertices,
BufferViewTarget.ARRAY_BUFFER
);
const accessorIndex = container.json.accessors.length - 1;
primitiveDef.attributes['TEXCOORD_1'] = accessorIndex;
if (primitiveDef.attributes['TEXCOORD_0'] === undefined) {
primitiveDef.attributes['TEXCOORD_0'] = accessorIndex;
}
container.json.materials[primitiveDef.material].occlusionTexture = {index: occlusionTextureIndex};
});
const ao = aoSampler.report();
aoSampler.dispose();
// Write UV set and add AO map.
const numVertices = ao.length;
const uv2Data = new Float32Array(numVertices * 2);
for (let i = 0; i < numVertices; i++) {
uv2Data[i * 2] = uv2Data[i * 2 + 1] = 1 - ao[i];
}
GLTFUtil.addAccessor(
container,
uv2Data,
'VEC2' as GLTF.AccessorType.VEC2,
AccessorComponentType.FLOAT,
numVertices,
BufferViewTarget.ARRAY_BUFFER
);
const accessorIndex = container.json.accessors.length - 1;
primitiveDef.attributes['TEXCOORD_1'] = accessorIndex;
if (primitiveDef.attributes['TEXCOORD_0'] === undefined) {
primitiveDef.attributes['TEXCOORD_0'] = accessorIndex;
}
container.json.materials[primitiveDef.material].occlusionTexture = {index: occlusionTextureIndex};
});
}
// Bake vertex AO.
const {position, cells} = primitive;
const aoSampler = geoao(position, {cells, resolution, regl});
for (let i = 0; i < samples; i++) aoSampler.sample();
const ao = aoSampler.report();
aoSampler.dispose();
// Write UV set and add AO map.
const numVertices = ao.length;
const uv2Data = new Float32Array(numVertices * 2);
for (let i = 0; i < numVertices; i++) {
uv2Data[i * 2] = uv2Data[i * 2 + 1] = 1 - ao[i];
}
GLTFUtil.addAccessor(
container,
uv2Data,
'VEC2' as GLTF.AccessorType.VEC2,
AccessorComponentType.FLOAT,
numVertices,
BufferViewTarget.ARRAY_BUFFER
);
const accessorIndex = container.json.accessors.length - 1;
primitiveDef.attributes['TEXCOORD_1'] = accessorIndex;
if (primitiveDef.attributes['TEXCOORD_0'] === undefined) {
primitiveDef.attributes['TEXCOORD_0'] = accessorIndex;
}
container.json.materials[primitiveDef.material].occlusionTexture = {index: occlusionTextureIndex};
});
// Find duplicate mesh accessors.
const duplicateAccessors = new Map();
for (let i = 0; i < meshAccessorIndices.length; i++) {
if (duplicateAccessors.has(i)) continue;
const iAccessor = container.json.accessors[i];
const iAccessorData = container.getAccessorArray(i).slice().buffer;
for (let j = i + 1; j < meshAccessorIndices.length; j++) {
if (duplicateAccessors.has(j)) continue;
const jAccessor = container.json.accessors[j];
const jAccessorData = container.getAccessorArray(j).slice().buffer;
if (iAccessor.type !== jAccessor.type) continue;
if (iAccessor.componentType !== jAccessor.componentType) continue;
if (iAccessor.count !== jAccessor.count) continue;
if (iAccessor.normalized !== jAccessor.normalized) continue;
if (GLTFUtil.arrayBufferEquals(iAccessorData, jAccessorData)) {
duplicateAccessors.set(j, i);
}
}
}
logger.info(`Duplicates: ${Array.from(duplicateAccessors).length} of ${json.accessors.length}.`);
// Replace accessor references.
json.meshes.forEach((mesh) => {
mesh.primitives.forEach((primitive) => {
for (const semantic in primitive.attributes) {
const index = primitive.attributes[semantic];
if (duplicateAccessors.has(index)) {
primitive.attributes[semantic] = duplicateAccessors.get(index);
}
}
if (primitive.indices && duplicateAccessors.has(primitive.indices)) {
}
})
removedBufferViews.push(bufferViewIndex);
});
});
// Removed emptied bufferviews.
removedBufferViews.sort((a, b) => a > b ? -1 : 1);
removedBufferViews.forEach((index) => GLTFUtil.removeBufferView(container, index));
// Remove initial buffer, if empty.
const buffer = json.buffers[0];
if (buffer.byteLength === 0) {
GLTFUtil.removeBuffer(container, 0);
}
return container;
}