Export 2 meshes with 2 animations using gltfExporter

Hello,
First post here, hope I do it correct.

I am trying to do a GLTFexport of two simple torusGeometries (pentagon shaped) that are spinning in two different speeds. When exported I am expected to get the same result as the rendering, but instead I get them to rotate “together” around 0,0.

The animations are in my op, correct, because I see them rotate like I want in my render. I am assuming the GLTFExporter does not understand what animation belongs to what object and just used the first animation to both objects, but I don’t know how to tell the exporter which animation belongs to which object.

Example code here

Hi, I’m having the same issue. It’s only exporting the first animation in the animations array:

<!DOCTYPE html>
<html lang="en">

<head>
	<title>Piece</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
	<link type="text/css" rel="stylesheet" href="main.css">
</head>

<body>
	<div id="info">
		<button id="export_scene">Export Scene</button>
	</div>

	<script type="module">

		import * as THREE from '../../build/three.module.js';
		import { GLTFExporter } from './jsm/exporters/GLTFExporter.js';

		function exportGLTF(input) {
			const gltfExporter = new GLTFExporter();

			const options = {
				binary: true,
				maxTextureSize: 4096,
				animations: [greenClip, redClip],
				includeCustomExtensions: true,
				forceIndices: true
			};

			gltfExporter.parse(input, function (result) {
				if (result instanceof ArrayBuffer) {
					saveArrayBuffer(result, 'scene.glb');
				} else {
					const output = JSON.stringify(result, null, 2);
					console.log(output);
					saveString(output, 'scene.gltf');
				}
			}, options);
		}

		document.getElementById('export_scene').addEventListener('click', function () {
			exportGLTF(scene);
		});

		const link = document.createElement('a');
		link.style.display = 'none';
		document.body.appendChild(link); // Firefox workaround, see #6594

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

		function saveString(text, filename) {
			save(new Blob([text], { type: 'text/plain' }), filename);
		}

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

		let clock;
		let redClip, greenClip, blueClip, yellowClip;
		let redMixer, greenMixer, blueMixer, yellowMixer;
		let camera, geometry, scene, renderer;

		init();
		animate();

		function init() {
			scene = new THREE.Scene();
			scene.name = 'scene';

			// Perspective Camera
			camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
			camera.position.set(10, 300, 0);
			camera.name = "PerspectiveCamera";
			scene.add(camera);

			// Ambient light
			const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
			ambientLight.name = 'AmbientLight';
			scene.add(ambientLight);

			// DirectLight
			const dirLight = new THREE.DirectionalLight(0xffffff, 1);
			dirLight.target.position.set(0, 0, - 1);
			dirLight.add(dirLight.target);
			dirLight.lookAt(- 1, - 1, 0);
			dirLight.name = 'DirectionalLight';
			scene.add(dirLight);

			//Axes 
			/*const axes = new THREE.AxesHelper(500);
			axes.name = "AxesHelper";
			scene.add(axes);*/

			// Colors 
			const red = new THREE.Color(.92, .17, .14);
			const green = new THREE.Color(.21, .63, .18);
			const blue = new THREE.Color(.2, .34, .78);
			const yellow = new THREE.Color(1., .74, .14);

			// Materials
			const redMaterial = new THREE.MeshBasicMaterial({ color: red });
			const greenMaterial = new THREE.MeshBasicMaterial({ color: green });
			const blueMaterial = new THREE.MeshBasicMaterial({ color: blue });
			const yellowMaterial = new THREE.MeshBasicMaterial({ color: yellow });

			// Geometries
			const cubeGeometry = new THREE.BoxGeometry(100, 100, 100);

			const redCube = new THREE.Mesh(cubeGeometry, redMaterial);
			const greenCube = new THREE.Mesh(cubeGeometry, greenMaterial);
			const blueCube = new THREE.Mesh(cubeGeometry, blueMaterial);
			const yellowCube = new THREE.Mesh(cubeGeometry, yellowMaterial);

			redCube.name = "redCube";
			greenCube.name = "greenCube";
			blueCube.name = "blueCube";
			yellowCube.name = "yellowCube";

			scene.add(redCube);
			scene.add(greenCube);
			scene.add(blueCube);
			//scene.add(yellowCube);

			// POSITION
			const redPosition = new THREE.VectorKeyframeTrack('redCube.position', [0, 1, 2], [0, 0, 0, 0, 100, 0, 0, 0, 0]);
			const greenPosition = new THREE.VectorKeyframeTrack('greenCube.position', [0, 1, 2], [100, 0, 0, 0, 0, 0, 100, 0, 0]);
			const bluePosition = new THREE.VectorKeyframeTrack('blueCube.position', [0], [0, 100, 100]);
			const yellowPosition = new THREE.VectorKeyframeTrack('yellowCube.position', [0], [100, 100, 100]);

			// Clip
			redClip = new THREE.AnimationClip('ActionA', 3, [redPosition]);
			greenClip = new THREE.AnimationClip('ActionB', 3, [greenPosition]);

			// Mixer
			redMixer = new THREE.AnimationMixer(redCube);
			greenMixer = new THREE.AnimationMixer(greenCube);

			const redClipAction = redMixer.clipAction(redClip);
			const greenClipAction = greenMixer.clipAction(greenClip);

			greenClipAction.play();
			redClipAction.play();

			// Clock
			clock = new THREE.Clock();

			// Renderer
			renderer = new THREE.WebGLRenderer({ antialias: true });
			renderer.setPixelRatio(window.devicePixelRatio);
			renderer.setSize(window.innerWidth, window.innerHeight);
			document.body.appendChild(renderer.domElement);

			window.addEventListener('resize', onWindowResize);
		}

		function onWindowResize() {
			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix();
			renderer.setSize(window.innerWidth, window.innerHeight);
		}

		function animate() {
			requestAnimationFrame(animate);
			render();
		}

		function render() {
			const timer = Date.now() * 0.0001;

			const delta = clock.getDelta();

			if (greenMixer) greenMixer.update(delta);
			if (redMixer) redMixer.update(delta);

			camera.position.x = Math.cos(timer) * 400;
			camera.position.z = Math.sin(timer) * 400;

			camera.lookAt(scene.position);
			renderer.render(scene, camera);
		}

	</script>

</body>

</html>

Thanks! That solved my first problem. I didn’t add my object names into each clip like you did:

new THREE.VectorKeyframeTrack('redCube.position' ...

By doing that the animations are saved together with each object. However it created a new problem, since the exported gltf now has 2 animations that does not necessary start at the same time. What I need is this to be exported “as one” animation. I started a new question about this: