GLB Animations not playing

Hello,
I am a beginner in Threejs, I have created the WebAR viewer for my project.
For testing I am loading my glb model which has a basic rotation animation for testing purpose.

The model loads but I am not able to make the animation play. I have googled almost all the results on the glb animation but none of those are working.

The goal for me is after placing the model in AR the animation should start playing,

I even don’t know how to make the gltf scene as a global variable for accessing the animation later on in the project.

This is my code,

Web AR Test
<link rel="stylesheet" href="three.js/main/css/main.css" />
<script type="module">
  import * as THREE from "./three.js/build/three.module.js";
  import { OrbitControls } from "./three.js/examples/jsm/controls/OrbitControls.js";
  import { ARButton } from "./three.js/examples/jsm/webxr/ARButton.js";
  import { RGBELoader } from "./three.js/examples/jsm/loaders/RGBELoader.js";
  import { GLTFLoader } from "./three.js/examples/jsm/loaders/GLTFLoader.js";

  let container;
  let camera, scene, renderer, mixer;
  let controller;

  let reticle;

  let hitTestSource = null;
  let hitTestSourceRequested = false;

  Init();
  Animate();

  function Init() {
    container = document.createElement("div");
    document.body.appendChild(container);

    scene = new THREE.Scene();

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

    new RGBELoader()
      .setDataType(THREE.UnsignedByteType)
      .setPath("three.js/main/hdr/")
      .load("studio.hdr", function (texture) {
        const envMap = pmremGenerator.fromEquirectangular(texture).texture;

        //scene.background = envMap;
        scene.environment = envMap;

        texture.dispose();
        pmremGenerator.dispose();
      });

    renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.toneMapping = THREE.ACESFilmicToneMapping;
    renderer.toneMappingExposure = 1;
    renderer.outputEncoding = THREE.sRGBEncoding;
    renderer.xr.enabled = true;

    const pmremGenerator = new THREE.PMREMGenerator(renderer);
    pmremGenerator.compileEquirectangularShader();

    container.appendChild(renderer.domElement);
    document.body.appendChild(
      ARButton.createButton(renderer, { requiredFeatures: ["hit-test"] })
    );

    const gltfLoader = new GLTFLoader();
    var model = new THREE.Object3D();

    gltfLoader.load("three.js/main/glb/Ganesha.glb", (gltf, el) => {
      model = gltf.scene;
      model.name = "model";  
      console.log(gltf.animations);
      mixer = new THREE.AnimationMixer( model );
        gltf.animations.forEach(( clip ) => {
        mixer.clipAction(clip).play();
        });         
    });
   
    function OnSelect() {
      if (reticle.visible) {
        scene.add(model);
        model.position.setFromMatrixPosition(reticle.matrix);
        model.isvisible = true;                           
      }
    }

    controller = renderer.xr.getController(0);
    controller.addEventListener("select", OnSelect);
    scene.add(controller);

    reticle = new THREE.Mesh(
      new THREE.RingGeometry(0.15, 0.2, 32).rotateX(-Math.PI / 2),
      new THREE.MeshBasicMaterial()
    );
    reticle.matrixAutoUpdate = false;
    reticle.visible = false;
    scene.add(reticle);

    window.addEventListener("resize", OnWindowResize);
  }

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

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

  function Animate() {
    var mixerUpdateDelta = new THREE.Clock();        
    if(typeof mixer !== "undefined") mixer.update( mixerUpdateDelta.getDelta());
    
    renderer.setAnimationLoop(Render);
  }

  function Render(timestamp, frame) {
    if (frame) {
      const referenceSpace = renderer.xr.getReferenceSpace();
      const session = renderer.xr.getSession();

      if (hitTestSourceRequested === false) {
        session
          .requestReferenceSpace("viewer")
          .then(function (referenceSpace) {
            session
              .requestHitTestSource({ space: referenceSpace })
              .then(function (source) {
                hitTestSource = source;
              });
          });

        session.addEventListener("end", function () {
          hitTestSourceRequested = false;
          hitTestSource = null;
        });

        hitTestSourceRequested = true;
      }

      if (hitTestSource) {
        const hitTestResults = frame.getHitTestResults(hitTestSource);
        if (hitTestResults.length) {
          const hit = hitTestResults[0];
          reticle.visible = true;
          reticle.matrix.fromArray(
            hit.getPose(referenceSpace).transform.matrix
          );
        } else {
          reticle.visible = false;
        }
      }
    }

    renderer.render(scene, camera);
  }
</script>

Ganesha.glb (2.5 MB)

I’m afraid the way you are using THREE.Clock is not correct. Try it like so:

  1. Create an instance of THREE.Clock once similar to your scene or camera.
clock = new THREE.Clock();
  1. Reuse this object in your animation loop.
if ( mixer !== undefined ) mixer.update( clock.getDelta() );

Thanks for your help, I went through the tokyo example, I had to call the Animate method after the glb loader. The animation is working now…

Next in my question was, How to assign my gltf scene as a global variable to stop and start the animation using a button?

Do it like in the following example. It does not matter that ColladaLoader is used. It’s the same approach when using GLTFLoader.

https://threejs.org/examples/webgl_loader_collada

1 Like

Thanks for the info, I found out I can use gltf.animations and store it in a variable, That works for me too.

I just have one last issue, My model in AR appears to be in black and then sometimes during the session it lits up but doesn’t have any reflection…The model is looking good in the browser when tested with the XR plugin.