Skip to content

Instantly share code, notes, and snippets.

@donmccurdy
Last active June 18, 2024 04:45
Show Gist options
  • Save donmccurdy/2226332bb58980caebcd21fe7cbca029 to your computer and use it in GitHub Desktop.
Save donmccurdy/2226332bb58980caebcd21fe7cbca029 to your computer and use it in GitHub Desktop.
Example implementation of MSFT_lod in glTF-Transform.
import { Extension, ExtensionProperty, Node, NodeIO, PropertyType } from '@gltf-transform/core';
import { weld, dedup, simplifyPrimitive } from '@gltf-transform/functions';
import { MeshoptSimplifier } from 'meshoptimizer';
/******************************************************************************
* Example implementation of MSFT_lod for glTF-Transform.
*
* ⚠️ NOTICE: This code is provided for the sake of example, and is not tested or
* maintained. For a full implementation, refer to:
* https://github.com/takahirox/glTF-Transform-lod-script
*/
const MSFT_LOD = 'MSFT_lod';
class LODExtension extends Extension {
extensionName = MSFT_LOD;
static EXTENSION_NAME = MSFT_LOD;
/** Creates a new LOD property for use on a {@link Node}. */
createLOD(name = '') {
return new LOD(this.document.getGraph(), name);
}
read() {
throw new Error('MSFT_lod: read() not implemented');
}
write(context) {
const jsonDoc = context.jsonDoc;
for (const lod of this.properties) {
const ids = lod.listLODs().map((node) => context.nodeIndexMap.get(node));
lod.listParents().forEach((parent) => {
if (parent instanceof Node) {
const nodeIndex = context.nodeIndexMap.get(parent);
const nodeDef = jsonDoc.json.nodes[nodeIndex];
nodeDef.extensions = nodeDef.extensions || {};
nodeDef.extensions[MSFT_LOD] = { ids };
}
});
}
return this;
}
}
class LOD extends ExtensionProperty {
static EXTENSION_NAME = MSFT_LOD;
init() {
this.extensionName = MSFT_LOD;
this.propertyType = 'LOD';
this.parentTypes = [PropertyType.NODE];
}
getDefaults() {
return Object.assign(super.getDefaults(), { lods: [] });
}
listLODs() {
return this.listRefs('lods');
}
addLOD(node) {
return this.addRef('lods', node);
}
removeLOD(node) {
return this.removeRef('lods', node);
}
}
/******************************************************************************
* Script for adding LODs to a given GLB scene.
*/
await MeshoptSimplifier.ready;
const io = new NodeIO().registerExtensions([LODExtension]);
const simplifier = MeshoptSimplifier;
const document = await io.read('./in.glb');
const lodExtension = document.createExtension(LODExtension);
await document.transform(weld());
for (const mesh of document.getRoot().listMeshes()) {
// Generate LOD Primitives.
const lod1 = document.createMesh(mesh.getName() + '_LOD1');
const lod2 = document.createMesh(mesh.getName() + '_LOD2');
for (const prim of mesh.listPrimitives()) {
lod1.addPrimitive(simplifyPrimitive(document, prim.clone(), { ratio: 0.5, error: 0.001, simplifier }));
lod2.addPrimitive(simplifyPrimitive(document, prim.clone(), { ratio: 0.25, error: 0.01, simplifier }));
}
// Attach LODs to all Nodes referencing this Mesh.
mesh.listParents().forEach((parent) => {
if (parent instanceof Node) {
const lod = lodExtension.createLOD()
.addLOD(document.createNode().setMesh(lod1))
.addLOD(document.createNode().setMesh(lod2));
parent.setExtension(MSFT_LOD, lod);
}
});
}
await document.transform(dedup());
await io.write('./out.gltf', document);
@donmccurdy
Copy link
Author

Thanks @takahirox! I've tried to fix the mistake above (this script is still just an untested draft, though), and added a link to your full implementation at the top.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment