SKILL.md
Three.js Scene Graph + Export
Quick start
- Load the module, call
createScene(), thenupdateMatrixWorld(true). - Treat named
THREE.Groupnodes as parts/links. - Assign each mesh to its nearest named ancestor.
- Bake world transforms into geometry before export.
- Export per-part OBJ (and optionally URDF) using deterministic ordering.
Scene graph essentials
Object3D (root)
├── Group (part)
│ ├── Mesh
│ └── Group (child part)
│ └── Mesh
└── Group (another part)
└── Mesh
THREE.Scene/THREE.Object3D: containersTHREE.Group: logical part (no geometry)THREE.Mesh: geometry + material
Part partitioning rules
- Parts are named groups at any depth.
- Each mesh belongs to the nearest named ancestor.
- Nested named groups are separate parts; do not merge their meshes into parents.
- Skip empty parts; sort part names for determinism.
function buildPartMap(root) {
const parts = new Map();
root.traverse((obj) => {
if (obj.isGroup && obj.name) parts.set(obj.name, { group: obj, meshes: [] });
});
root.traverse((obj) => {
if (!obj.isMesh) return;
let parent = obj.parent;
while (parent && !(parent.isGroup && parent.name)) parent = parent.parent;
if (parent && parts.has(parent.name)) parts.get(parent.name).meshes.push(obj);
});
return Array.from(parts.values()).filter((p) => p.meshes.length > 0);
}
Mesh baking (world transforms)
Always bake transforms before export:
root.updateMatrixWorld(true);
let geom = mesh.geometry.clone();
geom.applyMatrix4(mesh.matrixWorld);
if (geom.index) geom = geom.toNonIndexed();
if (!geom.attributes.normal) geom.computeVertexNormals();
