Hi, I’m trying to export an animated scene with GLTFExporter, but the exported file is not working (it does without the animation). I’m following the documentation, and I wonder what I’m doing wrong. This is an example with basic components and animations:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - exporter - gltf</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: [clip],
includeCustomExtensions: true
};
console.log(options);
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 camera, geometry, scene, renderer, mixer, clip;
let gridHelper, sphere, smallSphere;
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);*/
// Sphere
const material = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true });
sphere = new THREE.Mesh(new THREE.SphereGeometry(70, 10, 10), material);
sphere.position.set(0, 0, 0);
sphere.name = "Sphere";
scene.add(sphere);
// Small sphere
smallSphere = new THREE.Mesh(new THREE.SphereGeometry(20, 10, 10), material);
smallSphere.position.set(80, 0, 0);
smallSphere.name = "SmallSphere";
scene.add(smallSphere);
// POSITION
const positionKF = new THREE.VectorKeyframeTrack('.position', [0, 1, 2], [0, 90, 60, 30, 0, 60, 70, 40, 50]);
// SCALE
const scaleKF = new THREE.VectorKeyframeTrack('.scale', [0, 1, 2], [1, 1, 1, 2, 2, 2, 1, 1, 1]);
// ROTATION
// Rotation should be performed using quaternions, using a THREE.QuaternionKeyframeTrack
// Interpolating Euler angles (.rotation property) can be problematic and is currently not supported
// set up rotation about x axis
const xAxis = new THREE.Vector3(1, 0, 0);
const qInitial = new THREE.Quaternion().setFromAxisAngle(xAxis, 0);
const qFinal = new THREE.Quaternion().setFromAxisAngle(xAxis, Math.PI);
const quaternionKF = new THREE.QuaternionKeyframeTrack('.quaternion', [0, 1, 2], [qInitial.x, qInitial.y, qInitial.z, qInitial.w, qFinal.x, qFinal.y, qFinal.z, qFinal.w, qInitial.x, qInitial.y, qInitial.z, qInitial.w]);
// COLOR
const colorKF = new THREE.ColorKeyframeTrack('.material.color', [0, 1, 2], [1, 0, 0, 0, 1, 0, 0, 0, 1], THREE.InterpolateDiscrete);
// OPACITY
const opacityKF = new THREE.NumberKeyframeTrack('.material.opacity', [0, 1, 2], [1, 0, 1]);
// Clip
clip = new THREE.AnimationClip('Action', 3, [positionKF]);
// Mixer
mixer = new THREE.AnimationMixer(smallSphere);
const clipAction = mixer.clipAction(clip);
clipAction.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 (mixer) {
mixer.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>