2
votes

Using Three.js r68+ I have been needing to very slightly modify the source to get my animations working correctly. Unmodified only one of each type of model are animated (there are multiple spawns of each type of model).

This is the modified source at line 29407 (posted code beginning at 29389):

THREE.AnimationHandler = {

LINEAR: 0,
CATMULLROM: 1,
CATMULLROM_FORWARD: 2,

//

add: function () { console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); },
get: function () { console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); },
remove: function () { console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); },

//

animations: [],

init: function ( data ) {

    // original -> if ( data.initialized === true ) return;
    if ( data.initialized === true ) return data; //<-- modified

The function now returns the animation data if initialized. I'm assuming it doesn't do so because of caching. My question is what is the best practice for animating multiple models that have the same animation names? I tried naming them uniquely per model name ie: "Death_MaleWarrior" but this had no effect.

Currently my models and animations are handled like so:

var modelArray = [];
var geoCache   = [];
var loader     = new THREE.JSONLoader(true);

var _MODEL = function(data){

  this.data       = data;  
  this.mesh       = null;
  this.animations = {};
  this.canAnimate = false;

  this.parseAnimations = function () {

    var len,i,anim;

    if (this.mesh) {

          if( this.mesh.geometry.animations ){
              this.canAnimate = true;
              len = this.mesh.geometry.animations.length;
              if( len ){
                  for(i=0;i<len;i++){
                      anim = this.mesh.geometry.animations[i];
                      if( anim ){
                        this.animations[anim.name] = new THREE.Animation( this.mesh, anim );
                      }
                  }
              }
          }
      }
  };

  this.playAnimation = function(label){

    if (this.canAnimate) {
      if( this.animations[label] ){
        //if( this.animations[label].data ){
          this.animations[label].play(0,1);
        //}
      }
    }
    return false;

  };

  this.load = function(geo){

    var mat;

    mat = new THREE.MeshPhongMaterial({color:somecolor, skinning:true})

    this.mesh = new THREE.SkinnedMesh(geo,mat);

    this.mesh.position.x = this.data.position[0];
    this.mesh.position.y = this.data.position[1];
    this.mesh.position.z = this.data.position[2];

    this.parseAnimations();

    scene.add(this.mesh);

    this.playAnimation('Idle');

  };

  this.init = function(){

    var geo;

    if( geoCache[this.data.name] ){
      geo = geoCache[this.data.name];
      this.load(geo); 
    }else{
       geo = loader.parse(JSON.parse(this.data.json)).geometry;
       geoCache[this.data.name] = geo;
       this.load(geo);            
    }        

  };

  this.init();

};

var dataArray = [{name:'MaleWarrior',json:'json_data',position:[x,y,z]},{name:'FemaleWarrior',json:'json_data',position:[x,y,z]},{name:'MaleWarrior',json:'json_data',position:[x,y,z]}];

for(var i=0, len=objectArray.length; i<len; i++){
  modelArray.push(new _MODEL(dataArray[i]) );
}

In this example the first MaleWarrior will animate but the second will not. If there was a second female she would not be animated either as the animation (even though it is a new THREE.Animation() ) will be considered initialized and will not return any data. If I do not check for the existence of animations.data in playAnimation I get the error ""Uncaught TypeError: Cannot read property 'name' of undefined " on line 29665".

Is what I'm doing bypassing animation caching and hurting performance? I feel like I'm missing something. How will an animation play without data?

All animation names are the same for every model "Idle", "Run", "Attack" etc.

Any help would be appreciated. If I'm not being clear enough please let me know. I have added additional detail.

1
There is no necessity of modifying the code. Perhaps you should check the way you are exporting your models if you are using the JSON loader? Also, for each THREE.SkinnedMesh you create and for which you want the animation to play, you must create a THREE.Animation object, and call its .play method. Perhaps you thought you had to create only one THREE.Animation for all THREE.SkinnedMesh? Most likely you must check your JSON and its loading, since geometry.animations may not be loading. Otherwise, I see no reason your code shouldn't work. Perhaps also that Animationhandler modificated line. - kdrnic
Also, usually animation.data IS defined. It is set when you call THREE.Animation constructor. We could help you more if you posted links to the rest of your code and the JSON file you are loading. - kdrnic
Each skinned mesh gets in own array of THREE.Animation objects. The parseAnimations function is ran for each individual mesh. The AnimationHandler (pre r68) line is commented out. There is no issue with the JSONLoader. Everything worked fine pre r68. - Hobbes
Also, you are creating a global called "animation" in that code, since you forgot to specify local scope by using "var", should be "var animation = new THREE.Animation...". - kdrnic
I believe you have a larger object, that has those .animations and .mesh properties, it would be useful to see that, as your problem can be there, and not in this piece of code you quoted here. - kdrnic

1 Answers

0
votes

This turned out to be an official three.js bug.

https://github.com/mrdoob/three.js/issues/5516