zoukankan      html  css  js  c++  java
  • Three.js 对模型多个动画切换展示(fbx)

    来源 :https://blog.csdn.net/qq_30100043/article/details/80087471

    简介

    上一节本想直接了结动画这一章。最后一想,没有做过模型动画切换的案例。就此,再加一章,关于模型多个动画之间如何切换的问题。

    案例实现

    案例查看地址:http://www.wjceo.com/blog/threejs/2018-04-25/153.html

    首先,我们需要先将模型导入,之前案例已经讲过如何导入,这里就不赘述。

    //加载模型
    var loader = new THREE.FBXLoader();
    loader.load("/lib/models/fbx/Naruto.fbx", function (mesh) {
        mesh.position.y += 100;
        scene.add(mesh);
    });
    
    

    导入模型以后,我们需要查看当前模型的动画的长度,也就是mesh.animations的长度,如果只有一个长度,代表只有一个动画,如果有多个,就是多个动画的模型。然后我们需要遍历mesh.animations数组,为每个动画创建一个action,作为后面调用:

    mixer = mesh.mixer = new THREE.AnimationMixer(mesh);
    var actions = []; //所有的动画数组
    for(var i=0; i<mesh.animations.length; i++){
        actions[i] = mixer.clipAction(mesh.animations[i]);
    }

    在切换动画的时候,我们需要做的就是,将当前的所有的action除去需要播放的那个action全部暂停动画播放,让需要播放的哪一个动画调用play()事件:

    gui["action"+i] = function () {
        for(var j=0; j<actions.length; j++){
            if(j === i){
                actions[j].play();
            }
            else{
                actions[j].stop();
            }
        }
    };
    
    animations.add(gui, "action"+i);

    这样,我们就实现了对模型的动画切换。

    案例代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style type="text/css">
            html, body {
                margin: 0;
                height: 100%;
            }
    
            canvas {
                display: block;
            }
        </style>
    </head>
    
    <body onload="draw();">
    </body>
    <script src="https://cdn.bootcss.com/three.js/91/three.min.js"></script>
    <script src="/lib/js/libs/inflate.min.js"></script>
    <script src="/lib/js/loaders/FBXLoader.js"></script>
    <script src="/lib/js/controls/OrbitControls.js"></script>
    <script src="https://cdn.bootcss.com/stats.js/r17/Stats.min.js"></script>
    <script src="https://cdn.bootcss.com/dat-gui/0.7.1/dat.gui.min.js"></script>
    <script src="/lib/js/Detector.js"></script>
    
    <script>
        var renderer, camera, scene, gui, light, stats, controls, meshHelper, mixer, action,datGui;
        var clock = new THREE.Clock();
    
        function initRender() {
            renderer = new THREE.WebGLRenderer({antialias: true});
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.setClearColor(0xeeeeee);
            renderer.shadowMap.enabled = true;
            //告诉渲染器需要阴影效果
            document.body.appendChild(renderer.domElement);
        }
    
        function initCamera() {
            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
            camera.position.set(100, 200, 300 );
        }
    
        function initScene() {
            scene = new THREE.Scene();
            scene.background = new THREE.Color( 0xa0a0a0 );
            scene.fog = new THREE.Fog( 0xa0a0a0, 200, 1000 );
        }
    
        //初始化dat.GUI简化试验流程
        function initGui() {
            //声明一个保存需求修改的相关数据的对象
            gui = {
                helper:true //模型辅助线
            };
            datGui = new dat.GUI();
            //将设置属性添加到gui当中,gui.add(对象,属性,最小值,最大值)
    
            datGui.add(gui, "helper").onChange(function (e) {
                meshHelper.visible = e;
            })
        }
    
        function initLight() {
            scene.add(new THREE.AmbientLight(0x444444));
    
            light = new THREE.DirectionalLight(0xffffff);
            light.position.set(0, 200, 100 );
    
            light.castShadow = true;
            light.shadow.camera.top = 180;
            light.shadow.camera.bottom = -100;
            light.shadow.camera.left = -120;
            light.shadow.camera.right = 120;
    
            //告诉平行光需要开启阴影投射
            light.castShadow = true;
    
            scene.add(light);
        }
    
        function initModel() {
    
            //辅助工具
            var helper = new THREE.AxesHelper(50);
            scene.add(helper);
    
            // 地板
            var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0xffffff, depthWrite: false } ) );
            mesh.rotation.x = - Math.PI / 2;
            mesh.receiveShadow = true;
            scene.add( mesh );
    
            //添加地板割线
            var grid = new THREE.GridHelper( 2000, 20, 0x000000, 0x000000 );
            grid.material.opacity = 0.2;
            grid.material.transparent = true;
            scene.add( grid );
    
            //加载模型
            var loader = new THREE.FBXLoader();
            loader.load("/lib/models/fbx/Naruto.fbx", function (mesh) {
    
                console.log(mesh);
    
                //添加骨骼辅助
                meshHelper = new THREE.SkeletonHelper(mesh);
                scene.add(meshHelper);
    
                //设置模型的每个部位都可以投影
                mesh.traverse( function ( child ) {
                    if ( child.isMesh ) {
                        child.castShadow = true;
                        child.receiveShadow = true;
                    }
                } );
    
                //AnimationMixer是场景中特定对象的动画播放器。当场景中的多个对象独立动画时,可以为每个对象使用一个AnimationMixer
                mixer = mesh.mixer = new THREE.AnimationMixer(mesh);
    
                //mixer.clipAction 返回一个可以控制动画的AnimationAction对象  参数需要一个AnimationClip 对象
                //AnimationAction.setDuration 设置一个循环所需要的时间,当前设置了一秒
                //告诉AnimationAction启动该动作
                //action = mixer.clipAction(mesh.animations[0]);
                //action.play();
    
                var actions = []; //所有的动画数组
                var animations = datGui.addFolder("animations");
    
                for(var i=0; i<mesh.animations.length; i++){
                    createAction(i);
                }
    
                function createAction(i){
                    actions[i] = mixer.clipAction(mesh.animations[i]);
                    gui["action"+i] = function () {
                        for(var j=0; j<actions.length; j++){
                            if(j === i){
                                actions[j].play();
                            }
                            else{
                                actions[j].stop();
                            }
                        }
                    };
    
                    animations.add(gui, "action"+i);
                }
    
                //添加暂停所有动画的按键
                gui.stop = function(){
                    for(var i=0; i<actions.length; i++){
                        actions[i].stop();
                    }
                };
    
                datGui.add(gui, "stop");
    
                mesh.position.y += 100;
    
                scene.add(mesh);
            });
        }
    
        //初始化性能插件
        function initStats() {
            stats = new Stats();
            document.body.appendChild(stats.dom);
        }
    
        function initControls() {
    
            controls = new THREE.OrbitControls(camera, renderer.domElement);
            //设置控制器的中心点
            //controls.target.set( 0, 100, 0 );
            // 如果使用animate方法时,将此函数删除
            //controls.addEventListener( 'change', render );
            // 使动画循环使用时阻尼或自转 意思是否有惯性
            controls.enableDamping = true;
            //动态阻尼系数 就是鼠标拖拽旋转灵敏度
            //controls.dampingFactor = 0.25;
            //是否可以缩放
            controls.enableZoom = true;
            //是否自动旋转
            controls.autoRotate = false;
            controls.autoRotateSpeed = 0.5;
            //设置相机距离原点的最远距离
            controls.minDistance = 1;
            //设置相机距离原点的最远距离
            controls.maxDistance = 2000;
            //是否开启右键拖拽
            controls.enablePan = true;
        }
    
        function render() {
    
            var time = clock.getDelta();
            if (mixer) {
                mixer.update(time);
            }
    
            controls.update();
        }
    
        //窗口变动触发的函数
        function onWindowResize() {
    
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
    
        }
    
        function animate() {
            //更新控制器
            render();
    
            //更新性能插件
            stats.update();
    
            renderer.render(scene, camera);
    
            requestAnimationFrame(animate);
        }
    
        function draw() {
            //兼容性判断
            if (!Detector.webgl) Detector.addGetWebGLMessage();
    
            initGui();
            initRender();
            initScene();
            initCamera();
            initLight();
            initModel();
            initControls();
            initStats();
    
            animate();
            window.onresize = onWindowResize;
        }
    
    
    </script>
    </html>
  • 相关阅读:
    计算机二级-C语言-程序修改题-190114记录-对整型变量进行取余操作可以取得各个位上的值。
    计算机二级C语言选择题错题知识点记录。
    计算机二级-C语言-对文件的读写操作。链表的定义与赋值。对字符串的遍历和处理。
    二十七、Java基础之数组的排列
    二十六、Java语言之二维数组
    二十五、Java基础之一维数组
    二十四、Java基础之自定义异常
    二十三、Java基础之异常及异常处理机制
    二十二、Java基础之内部类
    二十一、Java基础之访问控制权限
  • 原文地址:https://www.cnblogs.com/lst619247/p/9195028.html
Copyright © 2011-2022 走看看