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)
|
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', () => {
|
test('stamps identity from the scene registry and strips other userData', () => {
|
||||||
const root = new THREE.Group()
|
const root = new THREE.Group()
|
||||||
const doorGroup = new THREE.Group()
|
const doorGroup = new THREE.Group()
|
||||||
|
|||||||
@@ -228,6 +228,20 @@ function pruneNonRenderableMeshes(root: THREE.Object3D, identityNodes: Set<THREE
|
|||||||
}
|
}
|
||||||
return
|
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
|
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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user