Camera animation with AnimationMixer not working - should it?

Hi Community
I am using a FBX-animation to import several camera viewpoints from 3dsMax. In Max per each keyframe, also some of the meshes are animated.

In Threejs I’m using AnimationMixer and see that the mesh positions change, but neither mixer.update() nor mixer.setTime() functions affect the imported camera’s position/rotation.
When console-logging the imported AnimationClip I see the camera’s positoin&rotation change is imported though:

( Ps I dont think it makes a difference, but I want to be able to jump between the keyframes manually, ie not using the clock.getDelta(). )

I wonder if I’ve made a mistake somewhere or are cameras anyway discarded by the mixer?
Has anyone experience with this?

Thanks in advance!

			import { FBXLoader } from './libs/loaders/FBXLoader.js';

		var scene, camera, container;
		var renderer, mixer;
		var	cameraLoaded = false,
			target = new THREE.Vector3(),

			buildingMaterialTrans = new THREE.MeshLambertMaterial( { color: 0xe0e0e0, side: THREE.DoubleSide, depthWrite: false, transparent: true, opacity: 0.5,  name:"Building Material" } ),
			texture = new THREE.TextureLoader().load( "files/clara/VP_01_07.jpg" ),
			texMaterial = new THREE.MeshBasicMaterial( { map: texture, name:"Texture Material" } );

		var clock = new THREE.Clock();

		init();
		animate();

		function init() {
			container = document.getElementById("viewer");

			scene = new THREE.Scene();

			var directionalLight = new THREE.DirectionalLight( 0xffeedd );
			directionalLight.position.set( 0, 0, 2 );
			scene.add( directionalLight );


			var loader = new FBXLoader();
			
			loader.load( 'files/clara/test_03/Camera_Export_No_Hierarchy.fbx', function ( object ) {

				console.log(object);
				if (object.animations.length > 0) console.log("Animation duration: "+object.animations[0].duration);

				object.traverse( function ( child ) {

					if (child.type.includes("Camera") ){
						//scene.attach(child);
						scene.camera = child;
						child.name = "Camera: "+child.name;
						camera = child;
						camera.far = 6000000;
						if (scene.plane) {
							//console.log(scene.plane.position);
							camera.lookAt(scene.plane.position);
						}
						cameraLoaded = true;
						camera.aspect = window.innerWidth / window.innerHeight;
						camera.updateProjectionMatrix();

					}
					else if ( child.isMesh ) {
						if(child.name == "Placeholder_Background_Plane"){
							scene.plane = child;
							child.castShadow = false;
							child.receiveShadow = false;
							if (camera && camera.type.includes("Camera") ) camera.lookAt(child.position); 
							child.material = texMaterial;
						} else {
							scene.building = child;
							child.material = buildingMaterialTrans;
							child.castShadow = true;
							child.receiveShadow = true;
							child.renderOrder = 10;
						}
					} 
				} );	
				scene.add( object );

				mixer = new THREE.AnimationMixer( object );
				mixer.clipAction( object.animations[ 0 ] ).play();
				animate();

			} , undefined, function ( e ) {
				console.error( e );
			} );

			console.log(scene);

			renderer = new THREE.WebGLRenderer( {alpha: true} );
			renderer.setPixelRatio( window.devicePixelRatio );
			renderer.setSize( window.innerWidth, window.innerHeight );
			container.appendChild( renderer.domElement );

		    var ambientLight = new THREE.AmbientLight(0xeeeeee, 0.95);
		    ambientLight.name = "Scene ambient light";
		    scene.add(ambientLight);

			window.addEventListener( 'resize', resize, false );
			document.addEventListener( 'keydown', onDocumentKeyDown);
		}

		function onDocumentKeyDown(event) {
			var key = event.key;
			if (key == "c"){
				console.log(
					Math.floor(camera.position.x)+","+
					Math.floor(camera.position.y)+","+
					Math.floor(camera.position.z)+"	"+
					camera.rotation._x+","+
					camera.rotation._y+","+
					camera.rotation._z)
					;
				console.log(camera);
			} else if (key == "s"){
				console.log(scene);
			} else if (key == "m"){
				console.log(mixer);
			} else if (key == "1"){
				updateAnimationToKeyframe (0);
			} else if (key == "2"){
				updateAnimationToKeyframe (0.03999999910593033);
			} else if (key == "3"){
				updateAnimationToKeyframe (0.07999999821186066);
			} 
		}

		function updateAnimationToKeyframe (time){
			mixer.setTime(time);
			scene.camera.lookAt(scene.plane.position);
			//console.log(scene.plane.position);
			//console.log(camera.position);
			camera.updateProjectionMatrix();
			console.log(mixer.time);
		}

		function onDocumentMouseMove( event ) {
			mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
			mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
		}

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

		function animate() {
			requestAnimationFrame( animate );
			var delta = clock.getDelta();
			//mixer.update( delta );
			//console.log(camera.position);
			//console.log(delta);
			//console.log(mixer.time);
			if (cameraLoaded) renderer.render( scene, camera );
		}

Meanwhile I’ve got it to work and figured out the following:

  • one shouldn’t just jump to the exact time value which is saved under the VectorKeyframeTrack times:
    In my case the time value is “0.07999999821186066” but mixer.setTime(0.07999999821186066) already jumps to the next frame. So I chose to round the value down a bit and then it works as expected.
  • one shouldn’t rename the imported camera (duh!)

Hope this helps someone with a similar issue(s).

1 Like