Обновление скелетных анимаций Three.js к новой системе на основе микшера

Система микшера была введена в r73, и с тех пор я пытаюсь обновить свою игру в этой новой системе.

Я ПОЧТИ, кроме одного. Перекрестное замирание некоторых анимаций с определенными геометриями имеет небольшую задержку, которой не было в r72. Я взломал функции r72 BlendCharacter и Animation, чтобы разрешить обратные вызовы, и он отлично работает. В 73 это не было необходимо, если бы эта функция была встроена через триггер события.

В следующей скрипте все работает по назначению (r72).

http://jsfiddle.net/titansoftime/a93w5hw0/

<script src="http://www.titansoftime.com/webgl/Three72.full.js"></script>
<script src="http://www.titansoftime.com/webgl/BlendCharacter2.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/DDSLoader.js"></script>

var scene, camera, renderer, ambient, directional;
var mesh, geoCache={};
var clock, jsLoader, ddsLoader;

init();
animate();

function init() {

        scene = new THREE.Scene();

        camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
        camera.position.z = 20;
        camera.position.y = 10;

        ambient = new THREE.AmbientLight(0xffffff);    
        scene.add(ambient);

        directional = new THREE.DirectionalLight(0xffffff,1);
        directional.position.set(1,1,0);
        scene.add(directional);

        clock = new THREE.Clock();

        jsLoader = new THREE.JSONLoader(true);    
        ddsLoader = new THREE.DDSLoader();

        renderer = new THREE.WebGLRenderer({antialias:true});
        renderer.setSize( window.innerWidth, window.innerHeight );
        renderer.setClearColor( 0xffffff, 1 );

        document.getElementById('idle').onclick = function(e){
            play('Idle',true);
        };

        document.getElementById('run').onclick = function(e){
            play('Run',true);
        };

        document.getElementById('melee').onclick = function(e){
            play('MelleAttack');
        };

        document.getElementById('magic').onclick = function(e){
            play('MagicAttack');
        };    

        document.body.appendChild( renderer.domElement );

        loadFloor();

        loadModel();

}

function createModel(json){

        var geo, geo2;

        if( geoCache[json.name] ){

                geo = geoCache[json.name];   

        }else{

                geo2 = jsLoader.parse(json).geometry;

                var m = new THREE.SkinnedMesh( geo2 );
                m.normalizeSkinWeights();
                geo2 = m.geometry;

                geo = new THREE.BufferGeometry().fromGeometry(geo2);
                geo.bones = geo2.bones;
                geo.animations = geo2.animations;

                geoCache[json.name] = geo;

        }

        var tex = ddsLoader.load('http://www.titansoftime.com/utils.php?task=getTexture&id=16');

        var mat = new THREE.MeshPhongMaterial({map:tex,skinning:true,side:THREE.DoubleSide});

        mesh = new THREE.BlendCharacter();
        mesh.load(geo,mat);

        //mesh.scale.set(10,10,10);

        //mesh.mixer = new THREE.AnimationMixer( mesh );

        //parseAnimations();    

        scene.add(mesh);

        play('Idle',true);

        camera.lookAt(new THREE.Vector3(mesh.position.x,7,mesh.position.z));

}

function loadModel(){

        $.ajax({

                url: 'http://www.titansoftime.com/utils.php',
                data: 'task=getModel&id=16',
                crossDomain: true,
                type: 'POST',
                success: function(response){
                        createModel(JSON.parse(response));
                }

        });    

}

function loadFloor(){

        var geo = new THREE.PlaneBufferGeometry(50,50);

        geo.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2));

        var mat = new THREE.MeshBasicMaterial({color:0x0000ff});

        var mesh = new THREE.Mesh(geo,mat);

        scene.add(mesh);

}

function play(name,loop){

    loop = loop || false;

    var anim = mesh.animations[name];

    anim.loop = loop;

    if( mesh.currentAnimation ){

        var cur = mesh.animations[mesh.currentAnimation];

        var theTime = 0.175;                

        if( !cur.loop ){

            var diff = cur.data.length - cur.currentTime;

            theTime = Math.max(0,Math.min(theTime,diff));

        }

        console.log('blending: '+name);
        mesh.crossfade(name,theTime,function(){

            play('Idle',true)

        });

    }else{              
        console.log('playing: '+name);
        mesh.play(name,loop);
    }

}

function animate() {

        requestAnimationFrame( animate );

        var delta = clock.getDelta();

        if( mesh ){

                mesh.update( delta );

        } 

        THREE.AnimationHandler.update(delta);

        renderer.render( scene, camera );

}

Этот (r78) работает почти отлично, за исключением одной анимации (Magic Attack) имеет небольшую, но заметную задержку перед возвратом в анимацию Idle. На других моделях это анимация ближнего боя, на некоторых нет проблем вообще. Супер путают, поскольку все они работают правильно в 72.

http://jsfiddle.net/titansoftime/2sh95etj/

<script src="https://rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/loaders/DDSLoader.js"></script>

var scene, camera, renderer, ambient, directional;
var mesh, geoCache={};
var clock, jsLoader, ddsLoader;

init();
animate();

function init() {

        scene = new THREE.Scene();

        camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
        camera.position.z = 20;
        camera.position.y = 10;

        ambient = new THREE.AmbientLight(0xffffff);    
        scene.add(ambient);

        directional = new THREE.DirectionalLight(0xffffff,1);
        directional.position.set(1,1,0);
        scene.add(directional);

        clock = new THREE.Clock();

        jsLoader = new THREE.JSONLoader(true);    
        ddsLoader = new THREE.DDSLoader();

        renderer = new THREE.WebGLRenderer({antialias:true});
        renderer.setSize( window.innerWidth, window.innerHeight );
        renderer.setClearColor( 0xffffff, 1 );

        document.getElementById('idle').onclick = function(e){
            play('Idle',true);
        };

        document.getElementById('run').onclick = function(e){
            play('Run',true);
        };

        document.getElementById('melee').onclick = function(e){
            play('MelleAttack');
        };

        document.getElementById('magic').onclick = function(e){
            play('MagicAttack');
        };    

        document.body.appendChild( renderer.domElement );

        loadFloor();

        loadModel();

}

function createModel(json){

        var geo, geo2;

        if( geoCache[json.name] ){

                geo = geoCache[json.name];   

        }else{

                geo2 = jsLoader.parse(json).geometry;

                var m = new THREE.SkinnedMesh( geo2 );
                m.normalizeSkinWeights();
                geo2 = m.geometry;

                geo = new THREE.BufferGeometry().fromGeometry(geo2);
                geo.bones = geo2.bones;
                geo.animations = geo2.animations;

                geoCache[json.name] = geo;

        }

        var tex = ddsLoader.load('http://www.titansoftime.com/utils.php?task=getTexture&id=16');

        var mat = new THREE.MeshPhongMaterial({map:tex,skinning:true,side:THREE.DoubleSide});

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

        //mesh.scale.set(10,10,10);

        mesh.mixer = new THREE.AnimationMixer( mesh );

        parseAnimations();

        play('Idle',true);

        scene.add(mesh);

        camera.lookAt(new THREE.Vector3(mesh.position.x,7,mesh.position.z));

}

function loadModel(){

        $.ajax({

                url: 'http://www.titansoftime.com/utils.php',
                data: 'task=getModel&id=16',
                crossDomain: true,
                type: 'POST',
                success: function(response){
                        createModel(JSON.parse(response));
                }

        });    

}

function loadFloor(){

        var geo = new THREE.PlaneBufferGeometry(50,50);

        geo.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2));

        var mat = new THREE.MeshBasicMaterial({color:0x0000ff});

        var mesh = new THREE.Mesh(geo,mat);

        scene.add(mesh);

}

function play(name,loop){

    var to = mesh.animations[ name ];       

    if( mesh.currentAnimation ){

        var from = mesh.animations[ mesh.currentAnimation ];

        to.reset();

        if( loop ){

            to.setLoop(THREE.LoopRepeat);
            to.clampWhenFinished = false;

        }else{

            to.setLoop(THREE.LoopOnce, 0);
            to.clampWhenFinished = true;                    

            mesh.mixer.addEventListener('finished',function(e){

                play('Idle',true);

            });                     

        }

        from.play();
        to.play();

        from.enabled = true;
        to.enabled = true;

        from.crossFadeTo( to, 0.3 );                    

    }else{

        to.play();

    }

    mesh.currentAnimation = name;

}

function parseAnimations(){

    var o, anim, anims = {};

    console.log(mesh);

    for( var i=0,len=mesh.geometry.animations.length;i<len;i++){

        o = mesh.geometry.animations[i];
        if( o ){

            anim = mesh.mixer.clipAction(o,mesh);
            anim.setEffectiveWeight(1);

            anims[o.name] = anim;

        }

    }

    mesh.animations = anims;

}

function animate() {

        requestAnimationFrame( animate );

        var delta = clock.getDelta();

        if( mesh ){

            if( mesh.mixer ){

                mesh.mixer.update( delta );

            }

         } 

        renderer.render( scene, camera );

}

Почему это происходит?

ОБНОВЛЕНИЕ: Я заметил, что это проблема не ограничивается смешением между анимациями. Одна из моих анимаций, только что зацикливающая, теперь имеет задержку!

72: http://jsfiddle.net/titansoftime/8v0pasp5/

78: http://jsfiddle.net/titansoftime/n6apnj3z/

Что происходит!? Было ли какое-то автокорректирующее поведение или что-то в этом роде в 72, которое было удалено?