Some models are broken after export to .glb

Im trying to export an a-frame scene with some 3D models on it, the export works but the resulting glb file is not correct

this is my a-frame scene

    <a-scene id="scene" embedded renderer="logarithmicDepthBuffer: true; antialias: true;" vr-mode-ui="enabled: false" gltf-model="dracoDecoderPath: https://www.gstatic.com/draco/v1/decoders/;" inspector keyboard-shortcuts screenshot device-orientation-permission-ui>
        <a-node class="no-export" id="sceneLoader"></a-node>
        <a-entity class="no-export" id="mainPlane">
            <a-entity class="no-export" id="cr_animation"></a-entity>
            <a-entity class="no-export clickable" cursor-listener geometry="primitive: plane" position="1 0 0" scale="0.9 0.9 0.9" rotation="-90 0 0" material="color: #FFFFFFB5; opacity: 0.8; transparent: true"></a-entity>
            <a-entity class="no-export clickable" cursor-listener geometry="primitive: plane" position="0 0 0" scale="0.9 0.9 0.9" rotation="-90 0 0" material="color: #FFFFFFB5; opacity: 0.8; transparent: true"></a-entity>
            <a-entity class="no-export clickable" cursor-listener geometry="primitive: plane" position="-1 0 0" scale="0.9 0.9 0.9" rotation="-90 0 0" material="color: #FFFFFFB5; opacity: 0.8; transparent: true"></a-entity>
            <a-entity class="no-export clickable" cursor-listener geometry="primitive: plane" position="1 0 -1" scale="0.9 0.9 0.9" rotation="-90 0 0" material="color: #FFFFFFB5; opacity: 0.8; transparent: true"></a-entity>
            <a-entity class="no-export clickable" cursor-listener geometry="primitive: plane" position="0 0 -1" scale="0.9 0.9 0.9" rotation="-90 0 0" material="color: #FFFFFFB5; opacity: 0.8; transparent: true"></a-entity>
            <a-entity class="no-export clickable" cursor-listener geometry="primitive: plane" position="-1 0 -1" scale="0.9 0.9 0.9" rotation="-90 0 0" material="color: #FFFFFFB5; opacity: 0.8; transparent: true"></a-entity>
            <a-entity class="no-export clickable" cursor-listener geometry="primitive: plane" position="1 0 -2" scale="0.9 0.9 0.9" rotation="-90 0 0" material="color: #FFFFFFB5; opacity: 0.8; transparent: true"></a-entity>
            <a-entity class="no-export clickable" cursor-listener geometry="primitive: plane" position="0 0 -2" scale="0.9 0.9 0.9" rotation="-90 0 0" material="color: #FFFFFFB5; opacity: 0.8; transparent: true"></a-entity>
            <a-entity class="no-export clickable" cursor-listener geometry="primitive: plane" position="-1 0 -2" scale="0.9 0.9 0.9" rotation="-90 0 0" material="color: #FFFFFFB5; opacity: 0.8; transparent: true"></a-entity>
            <a-entity class="no-export" id="capsulemodel4365">
                <a-entity class="export" id="model4365" gltf-model="https://s3.us-east-1.amazonaws.com/dev-cityar-storage/assets/web-app-object/GingerBread_Man_new2_2023-12-02-001020_jfen.glb" shadow="cast: true;" animation-mixer=""></a-entity>
            </a-entity>
            <a-entity class="no-export" id="capsulemodel4377">
                <a-entity class="export" id="model4377" gltf-model="https://s3.us-east-1.amazonaws.com/dev-cityar-storage/assets/web-app-object/cr_meerkat_model_2023-06-15-141803_iqyy.glb" shadow="cast: true;" animation-mixer=""></a-entity>
            </a-entity>
        </a-entity>
        <a-light class="no-export" id="dirLight" light="castShadow: true; shadowRadius: 3" type="directional" intensity="0.25" position="0.15 6 -1"></a-light>
        <a-light class="no-export" light="castShadow: false;" type="directional" intensity="0.75" position="-3 3 5"></a-light>
        <a-light class="no-export" light="castShadow: false;" type="directional" intensity="0.75" position="3 3 5"></a-light>
        <a-light class="no-export" light="castShadow: false;" type="directional" intensity="0.25" position="-3 3 -5"></a-light>
        <a-light class="no-export" light="castShadow: false;" type="directional" intensity="0.25" position="3 3 -5"></a-light>
        <a-entity class="no-export" camera="fov: 85" position="0 2.25 2" rotation="-15 0 0" look-controls="enabled: false">
            <a-entity class="no-export" cursor="rayOrigin: mouse;" raycaster="objects: .clickable;"></a-entity>
        </a-entity>
    </a-scene>

this is how the scene looks

this is the export code

<script type="module">

import { GLTFExporter } from 'https://threejs.org/examples/jsm/exporters/GLTFExporter.js';

document.addEventListener("DOMContentLoaded", () => {
	const btn = document.createElement('button');
	btn.textContent = 'Download .glb';
	document.body.appendChild(btn);
	btn.onclick = download;

	function download() {
		const scene = document.getElementById("scene").object3D;
		console.log("Scene before export:", scene);

		const objectsToExport = [];
		scene.traverse(child => {
			console.log("child name:", child.name, child.type);
			if (child.el && child.el.classList && !child.el.classList.contains('no-export') ) {
				console.log("child name:", child.name);
				console.log("child:", child);
				const mesh = child.el.object3DMap.mesh;
				let capsuleModel = child;
				const capsule = document.getElementById('capsule' + child.el.id);
				if(capsule){
					capsuleModel = capsule.object3D;
				}
				if (mesh) {
					mesh.position.copy(capsuleModel.position);
					mesh.rotation.copy(child.rotation);
					mesh.scale.copy(child.scale);
					mesh.updateMatrix();
					objectsToExport.push(mesh);
				} else {
					console.warn("Mesh not found for child:", child);
				}
			} else {
				console.log("Skipping non-exportable child:", child);
			}
		});

		console.log("Objects to export:", objectsToExport);
		const originalChildren = scene.children.slice();
		scene.children = objectsToExport;
		console.log("Scene after setting export objects:", scene.children);

		const exporter = new GLTFExporter();
		exporter.parse(scene, result => {
			console.log("Export successful. Restoring original scene children.");
			saveArrayBuffer(result, 'scene.glb');
			scene.children = originalChildren;
			console.log("Scene restored to original children:", scene.children);
		}, error => {
			console.error('An error happened during export:', error);
			scene.children = originalChildren;
			console.log("Scene restored to original children after error:", scene.children);
		}, { binary: true });
	}

	function saveArrayBuffer(buffer, filename) {
		save(new Blob([buffer], { type: 'application/octet-stream' }), filename);
	}

	const link = document.createElement('a');
	link.style.display = 'none';
	document.body.appendChild(link);

	function save(blob, filename) {
		link.href = URL.createObjectURL(blob);
		link.download = filename;
		link.click();
	}
});
</script>

This is the result after the export

scene

So it kinda works but theres those problems that i cant find, any ideas?

Does the exported GLB look the same (and incorrect) in a few different glTF viewers? If so, it might be a bug in A-Frame, and could be reported there, or try updating to the newest A-Frame version if you haven’t yet. I would also strongly recommend using a version of THREE.GLTFExporter that matches the version of three.js you have in A-Frame. Mixing and matching pieces of three.js from different versions will cause problems.

Linking the latest exporter from https://threejs.org/examples/jsm/exporters/GLTFExporter.js could break any time three.js makes a release, better to have a version that you can update manually when you are there to test it. If you’re not using a bundler, then I’d used a versioned URL from a CDN as shown on three.js docs.

1 Like

yes it looks broken on multiple viewers, there are some models that work, simple models like geometrical shapes and basketballs, i did try using updated versions of aframe too but same problem

I think the problem is caused by iterating over the scene, applying local transforms to meshes, and then pushing those meshes into a flat array. This would be fine if there were no skinned meshes, or no inherited transforms, but it looks like this scene has skinned meshes, and perhaps also nested node transforms. Pushing skinned meshes into a new scene, without their bones, will break the character.

Would it be an option for to clone the scene instead, and then remove or mark as non-visible the objects you don’t want to export? GLTFExporter skips non-visible objects by default.

If cloning the scene, note that you’ll want to use SkeletonUtils.clone rather than object.clone() to keep the relationships between meshes and bones connected properly.