引擎设计跟踪(九.14.3.4) mile stone 2 - model和fbx导入的补漏

以前milestone2已经作完的工做, 如今趁有时间记下笔记.编辑器

1.设计工具

这里是指兼容3ds max导出/fbx格式转换等等一系列工做的设计.测试

最开始, Blade的3dsmax导出插件, 所有代码都是写在导出的DLL里面的, 后来考虑到FBX等等其余格式, 如今把模块分红两部分:优化

  • Model/Anim Collector: 预约义的接口, 用于收集其余模型的相关数据. 用户负责扩展实现, 好比FBXCollector, MaxCollector, 或者其余格式.
  • Model/Anim Builder: 负责调用Model Collector的预约义接口来生成Blade的Mesh, 内置在Model插件中并提供接口.

导入/导出之类的也天然分为两个phase:动画

collecting data => building meshui

这么作的好处是builidng mesh/animation的代码所有能够复用, 并且building mesh的代码和复杂度才是最大的. 因为model builder内置到model模块内部, 只要根据工厂建立出内置的对象就能够了, 这有点像Java的风格.google

而collecting data做为用户可扩展的方式, 只要有定义良好的接口, 就能够只实现model collector, 主要是使用三方SDK获取数据, 工做量相对要小不少.spa

 

2.编辑器导入插件插件

对于编辑器来讲, 设计的思路是能够直接打开FBX文件. 好比像photoshop和3ds max这样的软件, 若是安装了文件格式的插件, 就能支持打开对应格式文件.设计

以前Blade对于"文件导入"没有任何抽象, 因而添加了下面的接口:

 1     struct SEditorImporterInfo
 2     {
 3         enum
 4         {
 5             IMPORTER_ID_TAG = 0x80000000,
 6         };
 7         TString            mName;            ///factory class
 8         TString            mTarget;        ///target IEditorFile type
 9         TString            mTargetExt;        ///target file extension
10         TStringList        mExtensions;    ///supported extensions
11         //importer type: assigned by framework
12         FileTypeID        mTypeID;        ///importer type IDs are in the same space of editor file type ids
13                                         ///FileTypeID with IMPORTER_ID_TAG represent a importer.
14         IconIndex        mIconID;
15 
16         inline bool operator<(const SEditorImporterInfo& rhs) const {return mName < rhs.mName;}
17         static inline bool comparePtr(const SEditorImporterInfo* lhs, const SEditorImporterInfo* rhs) {return *lhs < *rhs;}
18     };
19 
20     class BLADE_EDITOR_API IImporter : public TempAllocatable
21     {
22     public:
23         virtual ~IImporter() {}
24 
25         /**
26         @describe get the factory class name of the importer,
27         corresponding to SEditorImporterInfo::mName
28         @param
29         @return
30         */
31         virtual const TString&    getName() const = 0;
32 
33         /**
34         @describe 
35         @param source: input source file stream that importer can support
36         @param dest: output converted/imported format recognized by framework & plugins
37         @param params: extra parameters used for importing
38         @param extraFiles: extra file created by importer. extra files can only contains file names, files should be created at the same path/folder of input source file.
39         @param callback: callback for importing progress
40         @return
41         */
42         virtual bool    import(const HSTREAM& source, const HSTREAM& dest, const TParamList& params, TStringParam& extraFiles, CallbackRef& callback) = 0;
43     };
44 
45     extern template class BLADE_EDITOR_API Factory<IImporter>;
46     typedef Factory<IImporter> ImporterFactory;

导入信息记录了: 支持的源文件格式(扩展名), 目标格式. 也就是说, 导入器并不提供文件格式的解析(不会根据文件内容来建立任何编辑器对象), 只是单纯的转换成(其余插件)已经支持的其余格式.

这样以来, "导出"实际上作的事情就是格式转换. 好比FBX导入插件, 是使用FBXCollector和IModelBuilder, 生成Mesh(blm)文件到一个stream里面, 而blm文件已经有插件能够将它打开了.

在编辑器使用的时候, 能够直接拿memory stream做为dest stream, 经过导入器fbx文件完成格式转换, 而后在根据导入信息里面的"目标格式", 用工厂建立真正要打开的文件实例来打开转换后的stream; 等打开成功之后, 删除掉memory stream/memory file system里面的文件(临时mesh文件).

 

3.批量格式转换(CLI)

因为fbx文件的转换代码所有在编辑器的fbx importer插件里面, 为了模块复用, 这里的model converter工具其实是手动加载了fbx importer插件, 跳过编辑器的管线, 直接调用import接口完成文件格式转换.

同时, 添加了makefile来支持批量的fbx转换.

 

问题1: 模型/骨骼/动画合并

这个是跟具体项目的spec相关的, 不少项目的动画文件是拆分红单个clip的, 每一个动画对应一个源文件. 有的是单个文件包含全部的动画.

blade的model converter 支持多个源文件合并, 为了makefile可以方便处理, 只要将一组动画/模型放入同一个子文件夹, makefile会拿到全部文件列表并调用converter来合并出最终的模型和动画.

对于runtime, 实际项目中, 有的商业引擎没有很好的处理和优化, 致使一个角色的全部不一样的装备对应的全部骨骼, 都会参与骨骼计算, 实际上有不少是无效的 - 没有任何绑定. 并且有的引擎, 实际上想作针对性的优化, 也很难下手, 改动比较大. 由于实际项目中遇到过相似的状况, 因此Blade以及提早考虑并作了处理: 只有在runtime具备有效绑定mesh的骨骼才参与动画计算, 不然忽略.

 

问题2: 集成到工程

这个问题和之前的tex compressor同样, 把makefile集成到工程, 而且把max文件转换成fbx放入库里做为源文件, 实现一键构建, 具体怎么作再也不记录了.

因为如今的测试资源愈来愈多, 因此工程上也把美术资源和代码分开, 单独放到一个repo里面去.

使用fbx的另一个好处就是, 调试和改进mesh的导入/导出, 均可以不依赖3ds max了.

 

其余

submesh的bounding计算

在google上查到的最好方式, 是根据模型的绑定信息, 生成对应骨骼的包围. 注意全部绑定信息都在模型上面, 只要拿到对应骨骼id全部的顶点, 就能够计算出包围盒; 不须要骨骼/动画文件的任何信息.

因此包围盒能够只根据模型文件离线生成. 以后runtime更新时, 再根据骨骼动画来变换包围盒.

 

包围盒更新和可见性依赖

Blade的动画流程大体以下: 若是模型不可见, 那么就不会更新动画, submesh的包围盒就不用更新.

这里有一个死结, 就是模型的可见性自己也依赖包围盒, 若是不更新骨骼动画和包围盒, 就可能影响可见性.

目前解决方案是: 拿模型的静态包围盒作粗略的视锥剔除

  • 若是所有可见, 就不更新包围盒, 只更新骨骼动画, 虽然这个时候可能会有不可见的submesh没有更新到, 可是影响不大只会有一点overdraw.
  • 若是部分可见, 那么就更新骨骼动画, 并根据LOD信息选择性的更行submesh的包围盒, 作更细粒度的剔除.
  • 若是不可见, 那么就不更新动画和包围盒. 这里有一个前提就是模型的静态粗略的包围盒要足够大, 不然会影响视觉, 致使动画中的submesh原本可见, 可是模型的粗略视锥剔除致使其不可见.

 

动画LOD

因为远处的物体自己就比较小, 因此能够不更新包围盒. 这个能够经过骨骼动画的LOD来调整.

LOD的计算并不是是根据距离, 实际上应该根据屏幕上投影后的大小来, 由于远处的动画可能很大, 未必就应该忽略.

而骨骼动画的计算频率也不须要满帧计算, 记得以前提到过, 30FPS已经到达人眼的识别上限, 实际上大部分电影24FPS就足够, 但游戏有不少不一样http://www.zhihu.com/question/21081976, 并且现代电影都是48FPS, 有两帧同样的画面来组成.

目前Blade会根据投影后的尺寸来调整骨骼动画计算频率, 好比较远/较小的动画就会用低频更新, 最高频率也不会是满帧, 而是大约66FPS.

相关文章
相关标签/搜索