• 引擎设计跟踪(九.14.3.4) mile stone 2


    之前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 mesh

    这么做的好处是builidng mesh/animation的代码全部可以复用, 而且building mesh的代码和复杂度才是最大的. 由于model builder内置到model模块内部, 只要根据工厂创建出内置的对象就可以了, 这有点像Java的风格.

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

    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.

  • 相关阅读:
    各国语言缩写列表,各国语言缩写-各国语言简称,世界各国域名缩写
    How to see log files in MySQL?
    git 设置和取消代理
    使用本地下载和管理的免费 Windows 10 虚拟机测试 IE11 和旧版 Microsoft Edge
    在Microsoft SQL SERVER Management Studio下如何完整输出NVARCHAR(MAX)字段或变量的内容
    windows 10 x64系统下在vmware workstation pro 15安装macOS 10.15 Catelina, 并设置分辨率为3840x2160
    在Windows 10系统下将Git项目签出到磁盘分区根目录的方法
    群晖NAS(Synology NAS)环境下安装GitLab, 并在Windows 10环境下使用Git
    使用V-2ray和V-2rayN搭建本地代理服务器供局域网用户连接
    windows 10 专业版安装VMware虚拟机碰到的坑
  • 原文地址:https://www.cnblogs.com/crazii/p/5387196.html
Copyright © 2020-2023  润新知