3

I can’t seem to be able to clone an FBX model (FBX downloaded from Mixamo) while retaining animation keyframes.

Have attempted a number of approaches including using the cloneFbx gist (included in the example below); all to no avail. Even placing the entire FBXLoader() function inside a loop does not work as expected since only one of the models will animate at a time.

This issue has been partially addressed here, but I cannot seem to ‘copy’ the animation sequence as answer suggests.

Can anyone point out where I’m going wrong?

Here's a rough example of one of my tests:

Load fbx model and store animation:

var loader = new THREE.FBXLoader();
  loader.load( 'models/Walking.fbx', function ( fbx ) {
    clip = fbx.animations[ 0 ];

    // createVehicle(fbx); // Works! Creates one animated model via FBX

    // cloneFbx via: https://gist.github.com/kevincharm/bf12a2c673b43a3988f0f171a05794c1
    for (var i = 0; i < 2; i++) {
      const model = cloneFbx(fbx);
      createVehicle(model);
    }
  });

Add mixers and actions based on stored clip, add model to scene:

function createVehicle(model){
  model.mixer = new THREE.AnimationMixer( model );
  mixers.push( model.mixer );

  var action = model.mixer.clipAction( clip );
  action.play();

  model.traverse( function ( child ) {
    if ( child.isMesh ) {
      child.castShadow = true;
      child.receiveShadow = true;
    }
  });

  const x = Math.random() * groundSize - groundSize/2;
  const z = Math.random() * groundSize - groundSize/2;
  model.position.set(x, 0, z);

  const vehicle = new Vehicle(model, x, z);
  vehicles.push(vehicle);

  scene.add( model );
}

Animation cycle:

if ( mixers.length > 0 ) {
        for ( var i = 0; i < mixers.length; i ++ ) {
      mixers[ 0 ].update( clock.getDelta() );
        }
    }
Ryan Achten
  • 495
  • 4
  • 21
  • Execution seems to halt at `var action = model.mixer.clipAction( clip );`. I have no idea why this is, since the animation clip seems to contain the appropriate information: `{name: "mixamo.com", tracks: Array(53), duration: 2.4666666984558105, uuid: "5380AF34-4DB3-4BA1-AB0C-7BF2CE95EB7B"}` – Ryan Achten Jun 11 '18 at 20:59

2 Answers2

1

Couldn’t figure out an elegant solution to this. Best I could come up with is creating a loop with the loading sequence inside of it; this is very slow (since the FBX has to be parsed each time).

The key here was having an animation mixer controlling the animated objects as a group as opposed to creating a mixer per animated object.

If anyone can figure out a better solution, I would be super keen to hear it (perhaps using the cloneFbx script properly).

Create mixer, load FBX:

  // Create mixer to run animations
  mixer = new THREE.AnimationMixer( scene );

  // Load fbx
  var loader = new THREE.FBXLoader();
  for (var i = 0; i < 5; i++) {
    loader.load( 'models/Walking.fbx', function ( fbx ) {
      mixer.clipAction( fbx.animations[ 0 ], fbx )
          .startAt( - Math.random() )
          .play();

      createVehicle(fbx);
    });
  }

Create class instances, add to scene:

function createVehicle(model){

  const x = Math.random() * groundSize - groundSize/2;
  const z = Math.random() * groundSize - groundSize/2;
  model.position.set(x, 0, z);

  const vehicle = new Vehicle(model, x, z);
  vehicles.push(vehicle);

  scene.add( model );
}

Draw cycle:

mixer.update( clock.getDelta() );
Ryan Achten
  • 495
  • 4
  • 21
1

I found out that SkeletonUtils.clone() works good for me.

https://threejs.org/docs/index.html#examples/en/utils/SkeletonUtils.clone

Damjan Pavlica
  • 31,277
  • 10
  • 71
  • 76