Animation in immersive-ar mode?

I’m new to three.js and trying to understand how to load animations properly in immersive-ar mode. I’m trying two different loaders… a static mesh using GLTF and an animated FBX file. You can see in the video that it works in the browser before entering AR. Thinking it has something to do with how I’m handling animation update? Any advice or solution? I’m sure I’ve over complicated this but any help I could get would be greatly appreciated!

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>WebXR Test</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
		<link type="text/css" rel="stylesheet" href="examples/main.css">
	</head>
	<body>

		<!-- Import maps polyfill -->
		<!-- Remove this when import maps will be widely supported -->
		<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>

		<script type="importmap">
			{
				"imports": {
					"three": "./build/three.module.js",
					"three/addons/": "./examples/jsm/"
				}
			}
		</script>

		<script type="module">

			import * as THREE from 'three';
			import { ARButton } from 'three/addons/webxr/ARButton.js';
			
			//Add this for stats animation
			import Stats from 'three/addons/libs/stats.module.js';
			import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';


			//
			let camera, scene, renderer, stats; 
			let controller;

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

			//Add Mixer
			let mixer;

			init();
			animate();

			function init() {

				const container = document.createElement( 'div' );
				document.body.appendChild( container );

				scene = new THREE.Scene();

				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 20 );

				const light = new THREE.HemisphereLight( 0xffffff, 0xbbbbff, 1 );
				light.position.set( 0.5, 1, 0.25 );
				scene.add( light );

				//Load GLTF Model

				new GLTFLoader()
					.setPath( 'examples/models/gltf/' )
					.load( 'SheenChair.glb', function ( gltf ) {

						gltf.scene.scale.set(1, 1, 1);
						gltf.scene.position.x = 0;
						gltf.scene.position.y = -1.1;
						gltf.scene.position.z = -1;
						scene.add( gltf.scene );

					} );


				// Load FBX Model and Animation

				const loader = new FBXLoader();
				loader.load( 'examples/models/fbx/Samba Dancing.fbx', function ( object ) {

					// Add scale and transform

					object.scale.set(.01, .01, .01);
					object.position.x = 1;
					object.position.y = -1.0;
					object.position.z = -1.0;

					mixer = new THREE.AnimationMixer( object );

					const action = mixer.clipAction( object.animations[ 0 ] );
					action.play();

					object.traverse( function ( child ) {

						if ( child.isMesh ) {

							child.castShadow = true;
							child.receiveShadow = true;

						}

					} );

					scene.add( object );

				} );

				//

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

				// ADD stats

				stats = new Stats();
				container.appendChild( stats.dom );

				//

				document.body.appendChild( ARButton.createButton( renderer ) );



				//

				window.addEventListener( 'resize', onWindowResize );

			}

			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

			}

			// 

			function animate() {

				renderer.setAnimationLoop( render );

				requestAnimationFrame( animate );

				const delta = clock.getDelta();

				if ( mixer ) mixer.update( delta );

				stats.update();

			}

			function render() {

				renderer.render( scene, camera );

			}

		</script>
	</body>
</html>

You’re setting both render into an animation loop and requesting animation frame in animate, I think you need to set animation loop once outside of animate, on animate, and call everything you need in there, remove request animation frame and set animation loop on render and just call render() while the loop goes round

OK! Thanks! I figured it out with your help. This is what I did and it works in both browser and headset. Appreciated! :slight_smile:

			function animate() {

				const delta = clock.getDelta();

				if ( mixer ) mixer.update( delta );

				stats.update();

				render();

			}

			renderer.setAnimationLoop( animate );

			function render() {

				renderer.render( scene, camera );

			}
1 Like