fix(editor): guard GLB export against undefined array-material slots (Sentry MONOREPO-EDITOR-79) (#459)
Co-authored-by: Anton Pascal <anton-pascal@users.noreply.github.com>
This commit is contained in:
@@ -107,6 +107,26 @@ describe('prepareSceneForExport', () => {
|
||||
expect(visibleChildren).toHaveLength(1)
|
||||
})
|
||||
|
||||
test('fills undefined slots in an array material so no undefined survives the prune', () => {
|
||||
// Multi-material / group geometry where one slot was never assigned:
|
||||
// `mesh.material = [validMat, undefined]`. The scalar-null guard doesn't
|
||||
// catch this (an array is never `== null`), so the undefined slot used to
|
||||
// reach GLTFExporter and crash on `material.isShaderMaterial`.
|
||||
const root = new THREE.Group()
|
||||
const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1))
|
||||
mesh.material = [nodeMaterial(), undefined as unknown as THREE.Material]
|
||||
root.add(mesh)
|
||||
|
||||
const { scene } = prepareSceneForExport(root, {})
|
||||
|
||||
const exported = scene.children[0] as THREE.Mesh
|
||||
const materials = exported.material as THREE.Material[]
|
||||
expect(Array.isArray(materials)).toBe(true)
|
||||
expect(materials).toHaveLength(2)
|
||||
// No undefined/null slot survives; every slot is a real material.
|
||||
expect(materials.every((m) => m != null)).toBe(true)
|
||||
})
|
||||
|
||||
test('stamps identity from the scene registry and strips other userData', () => {
|
||||
const root = new THREE.Group()
|
||||
const doorGroup = new THREE.Group()
|
||||
|
||||
@@ -228,6 +228,20 @@ function pruneNonRenderableMeshes(root: THREE.Object3D, identityNodes: Set<THREE
|
||||
}
|
||||
return
|
||||
}
|
||||
// The scalar branch above only catches `material == null`; an ARRAY material
|
||||
// (multi-material / group geometry) is never `== null`, so a slot that was
|
||||
// never assigned — e.g. `[validMat, undefined]` — slips through and reaches
|
||||
// GLTFExporter, which reads `material.isShaderMaterial` on every group's
|
||||
// material and crashes on the undefined slot. Don't drop the mesh (the other
|
||||
// slots may be real geometry): fill any null/undefined holes with the hidden
|
||||
// placeholder so the exporter never sees an undefined material.
|
||||
if (
|
||||
(renderable.isMesh === true || renderable.isLine === true || renderable.isPoints === true) &&
|
||||
Array.isArray(renderable.material) &&
|
||||
renderable.material.some((m) => m == null)
|
||||
) {
|
||||
renderable.material = renderable.material.map((m) => m ?? PLACEHOLDER_MATERIAL)
|
||||
}
|
||||
const mesh = object as THREE.Mesh
|
||||
if (!mesh.isMesh || isRenderableMesh(mesh)) return
|
||||
if (mesh.children.length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user