Merge pull request #443 from pascalorg/fix/glb-export-missing-material

fix(editor): don't crash GLB export on a material-less renderable
This commit is contained in:
Wassim SAMAD
2026-06-25 16:51:19 -04:00
committed by GitHub
+24
View File
@@ -156,6 +156,11 @@ function pairClones(
// plain transform node instead of a primitive. // plain transform node instead of a primitive.
const EMPTY_GEOMETRY = new THREE.BufferGeometry() const EMPTY_GEOMETRY = new THREE.BufferGeometry()
// Hidden placeholder for a neutralised renderable that has no material: a valid
// material keeps GLTFExporter from crashing on `material.isShaderMaterial`, while
// EMPTY_GEOMETRY makes it emit a transform node instead of a primitive.
const PLACEHOLDER_MATERIAL = new THREE.MeshBasicMaterial({ visible: false })
/** /**
* Strip everything that must not bake into the model: * Strip everything that must not bake into the model:
* - Editor overlays on non-scene layers (gizmos, selection handles, ground * - Editor overlays on non-scene layers (gizmos, selection handles, ground
@@ -182,6 +187,25 @@ function pruneNonRenderableMeshes(root: THREE.Object3D, identityNodes: Set<THREE
toRemove.push(object) toRemove.push(object)
return return
} }
// A renderable (Mesh / Line / Points) with no material can't produce valid
// glTF and crashes GLTFExporter, which reads `material.isShaderMaterial`
// unconditionally — e.g. an imported sub-model that left a mesh material-less.
// Non-Mesh renderables also slip past the `isMesh` checks below and the
// material conversion. Neutralise it: keep the node (so children survive) but
// strip its geometry + give it the hidden placeholder, or drop it if a leaf.
const renderable = object as THREE.Mesh & { isLine?: boolean; isPoints?: boolean }
if (
(renderable.isMesh === true || renderable.isLine === true || renderable.isPoints === true) &&
renderable.material == null
) {
if (object.children.length > 0) {
renderable.geometry = EMPTY_GEOMETRY
renderable.material = PLACEHOLDER_MATERIAL
} else {
toRemove.push(object)
}
return
}
const mesh = object as THREE.Mesh const mesh = object as THREE.Mesh
if (!mesh.isMesh || isRenderableMesh(mesh)) return if (!mesh.isMesh || isRenderableMesh(mesh)) return
if (mesh.children.length > 0) { if (mesh.children.length > 0) {