以前milestone2已經作完的工做, 如今趁有時間記下筆記.編輯器
1.設計工具
這裏是指兼容3ds max導出/fbx格式轉換等等一系列工做的設計.測試
最開始, Blade的3dsmax導出插件, 所有代碼都是寫在導出的DLL裏面的, 後來考慮到FBX等等其餘格式, 如今把模塊分紅兩部分:優化
導入/導出之類的也天然分爲兩個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的包圍盒就不用更新.
這裏有一個死結, 就是模型的可見性自己也依賴包圍盒, 若是不更新骨骼動畫和包圍盒, 就可能影響可見性.
目前解決方案是: 拿模型的靜態包圍盒作粗略的視錐剔除
動畫LOD
因爲遠處的物體自己就比較小, 因此能夠不更新包圍盒. 這個能夠經過骨骼動畫的LOD來調整.
LOD的計算並不是是根據距離, 實際上應該根據屏幕上投影后的大小來, 由於遠處的動畫可能很大, 未必就應該忽略.
而骨骼動畫的計算頻率也不須要滿幀計算, 記得以前提到過, 30FPS已經到達人眼的識別上限, 實際上大部分電影24FPS就足夠, 但遊戲有不少不一樣http://www.zhihu.com/question/21081976, 並且現代電影都是48FPS, 有兩幀同樣的畫面來組成.
目前Blade會根據投影后的尺寸來調整骨骼動畫計算頻率, 好比較遠/較小的動畫就會用低頻更新, 最高頻率也不會是滿幀, 而是大約66FPS.