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>