从3dMax导出供threeJS使用的带动作模型与加载

评论区发现的建议,最近没空测试,先贴这

 

 还有好多人说找不到插件的 https://pan.baidu.com/s/1Q5g0... 密码:b43e 。 应该是他们现在只是维护blender,只有这个的插件,不如改用blender?

在自己做的一个小玩意中,发现要从3dMax中导出js文件供给threeJS使用,真是太多坑了!所以打算详细记录一下方法,好像开发会3dMax的比较少,但是至少可以帮助开发与美工更好的沟通与交流。在文末,我会附上一个可加载的js模型,方便学习~

导出文件类型选型

在《THREE.JS开发指南》2015年第一印第一版中提及的支持的模型主要有一下几种

 图中的确是一些常见格式,我们可以通过这个表格对常见格式做一些了解,但是我们从Three.js的官方实例的Loaders包中可以看到,发现他现在提供了各种加载器来支持文件加载,我们可以根据我们的需求来选择我们需要的加载方式

与3dmax自带的导出选择相比较,可以导出.FBX,Collada,STL,OBJ和MTL等文件,然后使用对应的Loader进行加载。我们可以选择自己熟悉的工具,熟悉的格式进行模型的导出,但是也正是由于他的多样性,导致无从下手,在尝试了Blender导出动作模型,与DDS素材转换等等工具之后,最终决定采用,我较为熟悉3dMax为建模工具,来导出我最熟悉的js文件来完成模型的导出与加载

下面是我经过实验后对一些常见格式的记录与分析大致统计出的一些选型分析

 

如果不需要做那么多选型的话,直接导出js好了~各方面都比较合适。

如何导出动态模型的js文件

安装3dMax导出插件

 .OBJ导出来的仅仅是模型,.MTL导出来的仅仅是材质,那动作呢?js在Three.js中支持的动作导出,在3dmax,maya,blender中并没有直接导出js的方法,庆幸的是大牛已经给我们开发了越来越丰富的插件了。在utils中的export中包含了这些插件(文件目录. hree.js-master hree.js-masterutilsexportersmax),我们需要将这些插件直接复制到3dmax的plugin文件夹中,如果都是需要导出包含动作的,只需要安装ThreeJSAnimationExporter.ms这一个文件就够了,重启3dmax后打开就可以看到这个插件了。(如下图)

如何获取一个可加载模型(以一个简单的圆柱体为例)

  1. 首先你的模型必须是一个整体,比如你的模型是熊猫吃竹子,如果你的熊猫和竹子要一块导出,那么你的熊猫和竹子必须是一个整体
  2. 贴图只能有一张,且贴图是png格式
  3. 给bone(骨骼)认真命名,js导出后你可能会发现骨骼有点偏移与错位,严格的命名有助于你在建模或者导出的时候进行修改,不过对开发来说这点会比较麻烦,这点也不是必须的。
  4. 模型的每一个点都必须赋予权重,如果发生破面行为,那么非常有可能是加了动作的模型却有的店没有权重
  5. 骨骼测试可用的有bone和CS骨骼,如果采用其他骨骼最好进行测试
  6. 导出的时候你的模型必须是一个Mesh,非常重要!这就意味着你不能导出一个可编辑多边形或者其他形式,在我们添加skin修改器的时候,先添加一个EditMesh修改器在我们的物体上,如果你已经是一个editable mesh,那这就不需要了。最好再使用Unwrap, UVW,中途出现warning点确定就好。

 导出的时候选择模型就好,不要选择骨骼,如果把骨骼一起选择导出会出现如下错误

 

 添加一个XForm修改器于你的模型当你开始导出之前,他会重置你物件的位置,转向等等,将它变为初始那样,最小化在导出时可能遇到的问题

 

  1. 如果发生错误,修复完毕后需要重新启动3dmax才能正常使用导出插件
  2. 可以先验证模型导出的数据量和点格式的正确与否再进行加载,一般导出文件的贴图地址会与你的地址不同,需要打开导出文件(如下图)修改贴图对应地址

  1. 加载过程中你有可能看不见模型,那么很有可能是你的模型过大或者过小,可以直接在代码中用scale函数进行调整测试
  2. 如果还有问题,建议在重复以上几点查看一遍

代码中如何加载动态模型

在程序中主要是这几个步骤

下面是代码的部分节选,其实官方的教程例子写的很完整,之后把一个完整可运行的实例传在GIt上~

/**
 * @method animateModel
 * @description 
 *  一个创建带动画模型的方法
 * @param {object} object.g 几何体 object.m 材质  object.name 名字
 */
var zz;
var animateModel = function(config) {
    var geometry = config.g;
    var materials = config.m;
    for (var i = 0; i < materials.length; i++) {
        var m = materials[i];
        m.skinning = true
    }

    // 创建材质,由于材质是多面材质,由材质数组组成故要调用MultiMaterial方法来创建一个新的材质
    var material = new THREE.MultiMaterial(materials);
    //创建出一个骨骼带蒙皮的网格对象
    var mesh = new THREE.SkinnedMesh(geometry, material);
    //给这个网格模型增加他的属性
    mesh.name = config.name;
    // 初始化模型位置
    mesh.position.set(0, 0, 0);
    mesh.geometry.computeVertexNormals();

    //用你的网格模型去创建一个骨骼帮助器 
    var skeletonHelper = new THREE.SkeletonHelper(mesh)
    skeletonHelper.material.linewidth = 3;
    // 我们可以打开skeletonHelper,这样可以看到骨骼,便于调整
    skeletonHelper.visible = true;
    //将骨骼添加到场景
    scene.add(skeletonHelper);

    // AnimationMixer 动画混合器 理解为这个动画各方面的一个管理者吧
    var mixer = new THREE.AnimationMixer(mesh);
    // 骨骼动画的动作片段保存在geometry中 下面是读取第一个动画的方式,所以animationFirst是一个AnimationClip
    var firstAnimation = geometry.animations[0];
    // AnimationAction是动作的schedule,之所以叫schedule是因为他可以控制着动画开始 结束 停止 这些流程
    var action = mixer.clipAction(firstAnimation);

    // 接下来可以为这个动画配置一些细节了
    action.clampWhenFinished = false;
//  0会停止,这里设置为0默认停止,不停要注意其他的地方是否有设置这个值,值越大越快
    action.setEffectiveTimeScale(0);
    action.play();
    mesh.mixer = mixer;
    mesh.action = action;
    mesh.skeletonHelper = skeletonHelper;
    return mesh;
}

//加载模型数据
var loader = new THREE.JSONLoader();
loader.load("static/img/model/czz3.js", function(geometry, materials) {
    var config = { name: "zz", g: geometry, m: materials };
                zz = animateModel(config);
                zz.status = 1;
                zz.action.time = 0;
                zz.action.setEffectiveTimeScale(0.7);
                zz.rotation.set(-26, 0, 0);
                zz.scale.set(7, 7, 7)
                zz.visible = true;
                scene.add(zz);
                addAnimateModel.animate();
                console.log(TWEEN.Tween)
                var zz_tween = new TWEEN.Tween(jichan.position).to({ z: 20 }, 10000)
                zz_tween.repeat(Infinity);
                zz_tween.start()
});
//主要理解render和animate这两个函数,模型能载入,缺动不起来主要都是这个袁术
var clock = new THREE.Clock();
function render() {
    // if (animation) animation.update(delta);
    var r = Date.now() * 0.0005;
    var delta = clock.getDelta();
    zz.mixer.update(delta);
    zz.skeletonHelper.update();
    stats.update();
    renderer.render(scene, camera);
}
function animate() {
    render();
    requestAnimationFrame(animate);
    trackBallControl.update();
}

静态模型的导出与加载

静态模型的导出,对我而言用3dmax直接导出obj最简单了,用导出OBJ和MTL格式,如果只要选择导出不带贴图的模型,那么在Material这一栏的Create mat-library则不用勾选.

require('OBJLoader.js')
require('MTLLoader.js')

/**
 * 加载一个有贴图模型
 */
const createMtlObj = argv => {

    THREE.Loader.Handlers.add(/.dds$/i, new THREE.DDSLoader());
    //创建材质加载器
    let mtlLoader = new THREE.MTLLoader();
    // 设置材质加载路径 (相对路径)
    mtlLoader.setPath(argv.mtlPath);

    mtlLoader.load(argv.mtlFileName, function(materials) {
        materials.preload();
        let objLoader = new THREE.OBJLoader();
        objLoader.setMaterials(materials);
        objLoader.setPath(argv.objPath);
        objLoader.load(argv.objFileName, function(object) {
                // 加载模型完成后的回调函数
                if (typeof argv.completeCallback === 'function') {
                    argv.completeCallback(object);
                }
            },

            // 加载中
            function(xhr) {
                if (xhr.lengthComputable) {
                    var percentComplete = xhr.loaded / xhr.total * 100;
                    console.log(Math.round(percentComplete, 2) + '% downloaded');
                }
            },
            function(error) { console.log('error:' + error) }
        );
    });

}

一个js动态模型文件——一头不吓人的蜘蛛

太长了,链接: https://pan.baidu.com/s/1boI98hL 密码: ethr

 

原文地址:https://www.cnblogs.com/smedas/p/12445397.html