javascript - 将 Three.js 骨骼动画更新到新的基于混合器的系统

标签 javascript three.js skeletal-animation

混音器系统是在 r73 中引入的,从那时起我一直在尝试将我的游戏更新到这个新系统。

除了一件事,我几乎就在那里。某些具有特定几何形状的动画的淡入淡出有轻微的延迟,这在 r72 中是不存在的。我破解了 r72 的 BlendCharacter 和 Animation 函数以允许回调并且效果很好。在 73 中,这不是必需的,因为它通过事件触发器内置了此功能。

在下面的 fiddle 中,一切都按预期工作(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) 几乎可以正常工作,除了一个动画(魔法攻击)在返回空闲动画之前有一个小但明显的延迟。在其他型号上是近战动画,在某些型号上则完全没有问题。 super 困惑,因为它们都可以在 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 中是否有某种自动更正行为或类似的东西已被删除?

最佳答案

这是 three.js 中的一个错误。

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

结束。

关于javascript - 将 Three.js 骨骼动画更新到新的基于混合器的系统,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38043123/

相关文章:

javascript - 如果在此期间发生其他事件,如何停止调用延迟函数?

Three.js r111 THREE.ShaderLib.cube 将 tCube 统一到 envMap 问题

three.js - 在 WebGL Globe (Google) 中选择栏

javascript - 使用 Three.js 如何沿指定平面移动对象?

c++ - 如何减慢从 BVH 文件读取的 opengl 动画?

javascript - String.normalize() 的意义何在?

javascript - 如何正确使用基于 Javascript Promise 的函数

matrix - 将权重应用于矩阵和顶点(骨骼旋转)

Blender collada 导出多个动画

javascript - 使用 QUnit 测试项目是否具有某个类