AssetImporter.cpp 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Animation.h"
  24. #include "Context.h"
  25. #include "File.h"
  26. #include "FileSystem.h"
  27. #include "Geometry.h"
  28. #include "Graphics.h"
  29. #include "IndexBuffer.h"
  30. #include "Light.h"
  31. #include "Material.h"
  32. #include "Model.h"
  33. #include "Octree.h"
  34. #include "PhysicsWorld.h"
  35. #include "Quaternion.h"
  36. #include "ResourceCache.h"
  37. #include "Scene.h"
  38. #include "StaticModel.h"
  39. #include "StringUtils.h"
  40. #include "Vector3.h"
  41. #include "VertexBuffer.h"
  42. #include "XMLFile.h"
  43. #include "Zone.h"
  44. #include <algorithm>
  45. #include <cstring>
  46. #include <iostream>
  47. #include <map>
  48. #include <set>
  49. #include <assimp.hpp>
  50. #include <aiScene.h>
  51. #include <aiPostProcess.h>
  52. #include "DebugNew.h"
  53. enum Command
  54. {
  55. CMD_NONE = 0,
  56. CMD_MODEL,
  57. CMD_SCENE,
  58. CMD_DUMP,
  59. CMD_LOD
  60. };
  61. struct OutModel
  62. {
  63. OutModel() :
  64. totalVertices_(0),
  65. totalIndices_(0),
  66. rootBone_(0)
  67. {
  68. }
  69. std::string outName_;
  70. aiNode* rootNode_;
  71. std::set<unsigned> meshIndices_;
  72. std::vector<aiMesh*> meshes_;
  73. std::vector<aiNode*> meshNodes_;
  74. std::vector<aiNode*> bones_;
  75. std::vector<aiAnimation*> animations_;
  76. std::vector<float> boneRadii_;
  77. std::vector<BoundingBox> boneHitboxes_;
  78. aiNode* rootBone_;
  79. unsigned totalVertices_;
  80. unsigned totalIndices_;
  81. };
  82. struct OutScene
  83. {
  84. std::string outName_;
  85. aiNode* rootNode_;
  86. std::vector<OutModel> models_;
  87. std::vector<aiNode*> nodes_;
  88. std::vector<unsigned> nodeModelIndices_;
  89. };
  90. SharedPtr<Context> context_(new Context());
  91. SharedPtr<FileSystem> fileSystem_(new FileSystem(context_));
  92. Command command_ = CMD_NONE;
  93. const aiScene* scene_ = 0;
  94. aiNode* rootNode_ = 0;
  95. std::string materialListName_;
  96. std::string resourcePath_;
  97. bool useSubdirs_ = true;
  98. bool localIDs_ = false;
  99. bool saveBinary_ = false;
  100. bool createZone_ = true;
  101. bool noAnimations_ = false;
  102. int main(int argc, char** argv);
  103. void Run(const std::vector<std::string>& arguments);
  104. void DumpNodes(aiNode* rootNode, unsigned level);
  105. void ExportModel(const std::string& outName);
  106. void CollectMeshes(OutModel& model, aiNode* node);
  107. void CollectBones(OutModel& model);
  108. void CollectBonesFinal(std::vector<aiNode*>& dest, const std::set<aiNode*>& necessary, aiNode* node);
  109. void CollectAnimations(OutModel& model);
  110. void BuildBoneCollisionInfo(OutModel& model);
  111. void BuildAndSaveModel(OutModel& model);
  112. void BuildAndSaveAnimations(OutModel& model);
  113. void ExportScene(const std::string& outName);
  114. void CollectSceneModels(OutScene& scene, aiNode* node);
  115. void BuildAndSaveScene(OutScene& scene);
  116. void ExportMaterials(std::set<std::string>& usedTextures);
  117. void BuildAndSaveMaterial(aiMaterial* material, std::set<std::string>& usedTextures);
  118. void CopyTextures(const std::set<std::string>& usedTextures, const std::string& sourcePath);
  119. void CombineLods(const std::vector<float>& lodDistances, const std::vector<std::string>& modelNames, const std::string& outName);
  120. void GetMeshesUnderNode(std::vector<std::pair<aiNode*, aiMesh*> >& meshes, aiNode* node);
  121. unsigned GetMeshIndex(aiMesh* mesh);
  122. unsigned GetBoneIndex(OutModel& model, const std::string& boneName);
  123. aiBone* GetMeshBone(OutModel& model, const std::string& boneName);
  124. Matrix4x3 GetOffsetMatrix(OutModel& model, const std::string& boneName);
  125. void GetBlendData(OutModel& model, aiMesh* mesh, std::vector<unsigned>& boneMappings, std::vector<std::vector<unsigned char> >&
  126. blendIndices, std::vector<std::vector<float> >& blendWeights);
  127. std::string GetMeshMaterialName(aiMesh* mesh);
  128. void WriteShortIndices(unsigned short*& dest, aiMesh* mesh, unsigned index, unsigned offset);
  129. void WriteLargeIndices(unsigned*& dest, aiMesh* mesh, unsigned index, unsigned offset);
  130. void WriteVertex(float*& dest, aiMesh* mesh, unsigned index, unsigned elementMask, BoundingBox& box,
  131. const Matrix4x3& vertexTransform, const Matrix3& normalTransform, std::vector<std::vector<unsigned char> >& blendIndices,
  132. std::vector<std::vector<float> >& blendWeights);
  133. unsigned GetElementMask(aiMesh* mesh);
  134. aiNode* FindNode(const std::string& name, aiNode* rootNode, bool caseSensitive = true);
  135. aiMatrix4x4 GetDerivedTransform(aiNode* node, aiNode* rootNode);
  136. aiMatrix4x4 GetDerivedTransform(aiMatrix4x4 transform, aiNode* node, aiNode* rootNode);
  137. aiMatrix4x4 GetMeshBakingTransform(aiNode* meshNode, aiNode* modelRootNode);
  138. void GetPosRotScale(const aiMatrix4x4& transform, Vector3& pos, Quaternion& rot, Vector3& scale);
  139. std::string ToStdString(const aiString& str);
  140. Vector3 ToVector3(const aiVector3D& vec);
  141. Vector2 ToVector2(const aiVector2D& vec);
  142. Quaternion ToQuaternion(const aiQuaternion& quat);
  143. std::string SanitateAssetName(const std::string& name);
  144. void ErrorExit(const std::string& error);
  145. int main(int argc, char** argv)
  146. {
  147. std::vector<std::string> arguments;
  148. for (int i = 1; i < argc; ++i)
  149. arguments.push_back(argv[i]);
  150. Run(arguments);
  151. return 0;
  152. }
  153. void Run(const std::vector<std::string>& arguments)
  154. {
  155. if (arguments.size() < 2)
  156. {
  157. ErrorExit(
  158. "Usage: AssetImporter <command> <input file> <output file> [options]\n"
  159. "See http://assimp.sourceforge.net/main_features_formats.html for input formats\n\n"
  160. "Commands:\n"
  161. "model Export a model\n"
  162. "scene Export a scene\n"
  163. "dump Dump scene node structure. No output file is generated\n"
  164. "lod Combine several Urho3D models as LOD levels of the output model\n"
  165. " Syntax: lod <dist0> <mdl0> <dist1 <mdl1> ... <output file>\n"
  166. "\n"
  167. "Options:\n"
  168. "-b Save scene in binary format, default format is XML\n"
  169. "-i Use local ID's for scene nodes\n"
  170. "-mX Output a material list file X (model mode only)\n"
  171. "-na Do not export animations\n"
  172. "-nm Do not export materials\n"
  173. "-ns Do not create subdirectories for resources\n"
  174. "-nz Do not create a zone and a directional light (scene mode only)\n"
  175. "-pX Set path X for scene resources. Default is output file path\n"
  176. "-rX Use scene node X as root node\n"
  177. "-t Generate tangents to model(s)\n"
  178. );
  179. }
  180. RegisterSceneLibrary(context_);
  181. RegisterGraphicsLibrary(context_);
  182. RegisterPhysicsLibrary(context_);
  183. context_->RegisterSubsystem(new FileSystem(context_));
  184. context_->RegisterSubsystem(new ResourceCache(context_));
  185. std::string command = ToLower(arguments[0]);
  186. std::string rootNodeName;
  187. bool noMaterials = false;
  188. unsigned flags =
  189. aiProcess_ConvertToLeftHanded |
  190. aiProcess_JoinIdenticalVertices |
  191. aiProcess_Triangulate |
  192. aiProcess_GenSmoothNormals |
  193. aiProcess_LimitBoneWeights |
  194. aiProcess_ImproveCacheLocality |
  195. aiProcess_FixInfacingNormals |
  196. aiProcess_FindInvalidData |
  197. aiProcess_FindInstances |
  198. aiProcess_OptimizeMeshes;
  199. for (unsigned i = 2; i < arguments.size(); ++i)
  200. {
  201. if ((arguments[i].length() >= 2) && (arguments[i][0] == '-'))
  202. {
  203. std::string parameter;
  204. if (arguments[i].length() >= 3)
  205. parameter = arguments[i].substr(2);
  206. switch (tolower(arguments[i][1]))
  207. {
  208. case 'b':
  209. saveBinary_ = true;
  210. break;
  211. case 'i':
  212. localIDs_ = true;
  213. break;
  214. case 'm':
  215. materialListName_ = Replace(parameter, '\\', '/');
  216. break;
  217. case 'p':
  218. resourcePath_ = AddTrailingSlash(parameter);
  219. break;
  220. case 'r':
  221. rootNodeName = parameter;
  222. break;
  223. case 't':
  224. flags |= aiProcess_CalcTangentSpace;
  225. break;
  226. case 'n':
  227. if (!parameter.empty())
  228. {
  229. switch (tolower(parameter[0]))
  230. {
  231. case 'a':
  232. noAnimations_ = true;
  233. break;
  234. case 'm':
  235. noMaterials = true;
  236. break;
  237. case 's':
  238. useSubdirs_ = false;
  239. break;
  240. case 'z':
  241. createZone_ = false;
  242. break;
  243. }
  244. }
  245. break;
  246. }
  247. }
  248. }
  249. if (command == "model")
  250. command_ = CMD_MODEL;
  251. else if (command == "scene")
  252. command_ = CMD_SCENE;
  253. else if (command == "dump")
  254. command_ = CMD_DUMP;
  255. else if (command == "lod")
  256. command_ = CMD_LOD;
  257. else
  258. ErrorExit("Unrecognized command " + command);
  259. if (command_ != CMD_LOD)
  260. {
  261. std::string inFile = arguments[1];
  262. std::string outFile;
  263. if ((arguments.size() > 2) && (arguments[2][0] != '-'))
  264. outFile = Replace(arguments[2], '\\', '/');
  265. if (resourcePath_.empty())
  266. {
  267. resourcePath_ = GetPath(outFile);
  268. // If output file already has the Models/ path (model mode), do not take it into the resource path
  269. if (command_ == CMD_MODEL)
  270. {
  271. std::string resPathLower = ToLower(resourcePath_);
  272. if (resPathLower.rfind("models/") == resPathLower.length() - 7)
  273. resourcePath_ = resourcePath_.substr(0, resourcePath_.length() - 7);
  274. }
  275. if (resourcePath_.empty())
  276. resourcePath_ = "./";
  277. }
  278. resourcePath_ = AddTrailingSlash(resourcePath_);
  279. Assimp::Importer importer;
  280. std::cout << "Reading file " << inFile << std::endl;
  281. scene_ = importer.ReadFile(GetNativePath(inFile).c_str(), flags);
  282. if (!scene_)
  283. ErrorExit("Could not open or parse input file " + inFile);
  284. rootNode_ = scene_->mRootNode;
  285. if (!rootNodeName.empty())
  286. {
  287. rootNode_ = FindNode(rootNodeName, rootNode_, false);
  288. if (!rootNode_)
  289. ErrorExit("Could not find scene node " + rootNodeName);
  290. }
  291. switch (command_)
  292. {
  293. case CMD_DUMP:
  294. DumpNodes(rootNode_, 0);
  295. return;
  296. case CMD_MODEL:
  297. ExportModel(outFile);
  298. break;
  299. case CMD_SCENE:
  300. ExportScene(outFile);
  301. break;
  302. }
  303. if (!noMaterials)
  304. {
  305. std::set<std::string> usedTextures;
  306. ExportMaterials(usedTextures);
  307. CopyTextures(usedTextures, GetPath(inFile));
  308. }
  309. }
  310. else
  311. {
  312. std::vector<float> lodDistances;
  313. std::vector<std::string> modelNames;
  314. std::string outFile;
  315. unsigned numLodArguments = 0;
  316. for (unsigned i = 1; i < arguments.size(); ++i)
  317. {
  318. if (arguments[i][0] == '-')
  319. break;
  320. ++numLodArguments;
  321. }
  322. if (numLodArguments < 4)
  323. ErrorExit("Must define at least 2 LOD levels");
  324. if (!(numLodArguments & 1))
  325. ErrorExit("No output file defined");
  326. for (unsigned i = 1; i < numLodArguments + 1; ++i)
  327. {
  328. if (i == numLodArguments)
  329. outFile = Replace(arguments[i], '\\', '/');
  330. else
  331. {
  332. if (i & 1)
  333. lodDistances.push_back(Max(ToFloat(arguments[i]), 0.0f));
  334. else
  335. modelNames.push_back(Replace(arguments[i], '\\', '/'));
  336. }
  337. }
  338. if (lodDistances[0] != 0.0f)
  339. {
  340. std::cout << "Warning: first LOD distance forced to 0" << std::endl;
  341. lodDistances[0] = 0.0f;
  342. }
  343. CombineLods(lodDistances, modelNames, outFile);
  344. }
  345. }
  346. void DumpNodes(aiNode* rootNode, unsigned level)
  347. {
  348. if (!rootNode)
  349. return;
  350. std::string indent;
  351. indent.resize(level * 2);
  352. for (unsigned i = 0; i < level * 2; ++i)
  353. indent[i] = ' ';
  354. Vector3 pos, scale;
  355. Quaternion rot;
  356. aiMatrix4x4 transform = GetDerivedTransform(rootNode, rootNode_);
  357. GetPosRotScale(transform, pos, rot, scale);
  358. std::cout << indent << "Node " << ToStdString(rootNode->mName) << " pos " << ToString(pos) << std::endl;
  359. if (rootNode->mNumMeshes == 1)
  360. std::cout << indent << " " << rootNode->mNumMeshes << " geometry" << std::endl;
  361. if (rootNode->mNumMeshes > 1)
  362. std::cout << indent << " " << rootNode->mNumMeshes << " geometries" << std::endl;
  363. for (unsigned i = 0; i < rootNode->mNumChildren; ++i)
  364. DumpNodes(rootNode->mChildren[i], level + 1);
  365. }
  366. void ExportModel(const std::string& outName)
  367. {
  368. if (outName.empty())
  369. ErrorExit("No output file defined");
  370. OutModel model;
  371. model.rootNode_ = rootNode_;
  372. model.outName_ = outName;
  373. CollectMeshes(model, model.rootNode_);
  374. CollectBones(model);
  375. BuildBoneCollisionInfo(model);
  376. BuildAndSaveModel(model);
  377. if (!noAnimations_)
  378. {
  379. CollectAnimations(model);
  380. BuildAndSaveAnimations(model);
  381. }
  382. // Write material references if requested
  383. if (!materialListName_.empty())
  384. {
  385. File listFile(context_);
  386. if (listFile.Open(materialListName_, FILE_WRITE))
  387. {
  388. for (unsigned i = 0; i < model.meshes_.size(); ++i)
  389. listFile.WriteLine(GetMeshMaterialName(model.meshes_[i]));
  390. }
  391. else
  392. std::cout << "Warning: could not write material list file " + materialListName_ << std::endl;
  393. }
  394. }
  395. void CollectMeshes(OutModel& model, aiNode* node)
  396. {
  397. for (unsigned i = 0; i < node->mNumMeshes; ++i)
  398. {
  399. aiMesh* mesh = scene_->mMeshes[node->mMeshes[i]];
  400. for (unsigned j = 0; j < model.meshes_.size(); ++j)
  401. {
  402. if (mesh == model.meshes_[j])
  403. {
  404. std::cout << "Warning: same mesh found multiple times" << std::endl;
  405. break;
  406. }
  407. }
  408. model.meshIndices_.insert(node->mMeshes[i]);
  409. model.meshes_.push_back(mesh);
  410. model.meshNodes_.push_back(node);
  411. model.totalVertices_ += mesh->mNumVertices;
  412. model.totalIndices_ += mesh->mNumFaces * 3;
  413. }
  414. for (unsigned i = 0; i < node->mNumChildren; ++i)
  415. CollectMeshes(model, node->mChildren[i]);
  416. }
  417. void CollectBones(OutModel& model)
  418. {
  419. std::set<aiNode*> necessary;
  420. std::set<aiNode*> rootNodes;
  421. for (unsigned i = 0; i < model.meshes_.size(); ++i)
  422. {
  423. aiMesh* mesh = model.meshes_[i];
  424. aiNode* meshNode = model.meshNodes_[i];
  425. aiNode* meshParentNode = meshNode->mParent;
  426. aiNode* rootNode = 0;
  427. for (unsigned j = 0; j < mesh->mNumBones; ++j)
  428. {
  429. aiBone* bone = mesh->mBones[j];
  430. std::string boneName(ToStdString(bone->mName));
  431. aiNode* boneNode = FindNode(boneName, scene_->mRootNode, true);
  432. if (!boneNode)
  433. ErrorExit("Could not find scene node for bone " + boneName);
  434. necessary.insert(boneNode);
  435. rootNode = boneNode;
  436. for (;;)
  437. {
  438. boneNode = boneNode->mParent;
  439. if ((!boneNode) || (boneNode == meshNode) || (boneNode == meshParentNode))
  440. break;
  441. rootNode = boneNode;
  442. necessary.insert(boneNode);
  443. }
  444. if (rootNodes.find(rootNode) == rootNodes.end())
  445. rootNodes.insert(rootNode);
  446. }
  447. }
  448. // If we find multiple root nodes, try to remedy by using their parent instead
  449. if (rootNodes.size() > 1)
  450. {
  451. aiNode* commonParent = (*rootNodes.begin())->mParent;
  452. for (std::set<aiNode*>::iterator i = rootNodes.begin(); i != rootNodes.end(); ++i)
  453. {
  454. if ((*i) != commonParent)
  455. {
  456. if ((!commonParent) || ((*i)->mParent != commonParent))
  457. ErrorExit("Skeleton with multiple root nodes found, not supported");
  458. }
  459. }
  460. rootNodes.clear();
  461. rootNodes.insert(commonParent);
  462. necessary.insert(commonParent);
  463. }
  464. if (rootNodes.empty())
  465. return;
  466. model.rootBone_ = *rootNodes.begin();
  467. CollectBonesFinal(model.bones_, necessary, model.rootBone_);
  468. // Initialize the bone collision info
  469. model.boneRadii_.resize(model.bones_.size());
  470. model.boneHitboxes_.resize(model.bones_.size());
  471. for (unsigned i = 0; i < model.bones_.size(); ++i)
  472. {
  473. model.boneRadii_[i] = 0.0f;
  474. model.boneHitboxes_[i] = BoundingBox(0.0f, 0.0f);
  475. }
  476. }
  477. void CollectBonesFinal(std::vector<aiNode*>& dest, const std::set<aiNode*>& necessary, aiNode* node)
  478. {
  479. if (necessary.find(node) != necessary.end())
  480. {
  481. dest.push_back(node);
  482. for (unsigned i = 0; i < node->mNumChildren; ++i)
  483. CollectBonesFinal(dest, necessary, node->mChildren[i]);
  484. }
  485. }
  486. void CollectAnimations(OutModel& model)
  487. {
  488. const aiScene* scene = scene_;
  489. for (unsigned i = 0; i < scene->mNumAnimations; ++i)
  490. {
  491. aiAnimation* anim = scene->mAnimations[i];
  492. bool modelBoneFound = false;
  493. for (unsigned j = 0; j < anim->mNumChannels; ++j)
  494. {
  495. aiNodeAnim* channel = anim->mChannels[j];
  496. std::string channelName = ToStdString(channel->mNodeName);
  497. if (GetBoneIndex(model, channelName) != M_MAX_UNSIGNED)
  498. {
  499. modelBoneFound = true;
  500. break;
  501. }
  502. }
  503. if (modelBoneFound)
  504. model.animations_.push_back(anim);
  505. }
  506. /// \todo Vertex morphs are ignored for now
  507. }
  508. void BuildBoneCollisionInfo(OutModel& model)
  509. {
  510. for (unsigned i = 0; i < model.meshes_.size(); ++i)
  511. {
  512. aiMesh* mesh = model.meshes_[i];
  513. for (unsigned j = 0; j < mesh->mNumBones; ++j)
  514. {
  515. aiBone* bone = mesh->mBones[j];
  516. std::string boneName = ToStdString(bone->mName);
  517. unsigned boneIndex = GetBoneIndex(model, boneName);
  518. if (boneIndex == M_MAX_UNSIGNED)
  519. continue;
  520. aiNode* boneNode = model.bones_[boneIndex];
  521. for (unsigned k = 0; k < bone->mNumWeights; ++k)
  522. {
  523. float weight = bone->mWeights[k].mWeight;
  524. if (weight > 0.33f)
  525. {
  526. aiVector3D vertexBoneSpace = bone->mOffsetMatrix * mesh->mVertices[bone->mWeights[k].mVertexId];
  527. Vector3 vertex = ToVector3(vertexBoneSpace);
  528. float radius = vertex.GetLength();
  529. if (radius > model.boneRadii_[boneIndex])
  530. model.boneRadii_[boneIndex] = radius;
  531. model.boneHitboxes_[boneIndex].Merge(vertex);
  532. }
  533. }
  534. }
  535. }
  536. }
  537. void BuildAndSaveModel(OutModel& model)
  538. {
  539. if (!model.rootNode_)
  540. ErrorExit("Null root node for model");
  541. std::string rootNodeName = ToStdString(model.rootNode_->mName);
  542. if (!model.meshes_.size())
  543. ErrorExit("No geometries found starting from node " + rootNodeName);
  544. std::cout << "Writing model " << rootNodeName << std::endl;
  545. SharedPtr<Model> outModel(new Model(context_));
  546. outModel->SetNumGeometries(model.meshes_.size());
  547. std::vector<std::vector<unsigned> > allBoneMappings;
  548. BoundingBox box;
  549. bool combineBuffers = true;
  550. // Check if buffers can be combined (same vertex element mask, under 65535 vertices)
  551. unsigned elementMask = GetElementMask(model.meshes_[0]);
  552. for (unsigned i = 1; i < model.meshes_.size(); ++i)
  553. {
  554. if (GetElementMask(model.meshes_[i]) != elementMask)
  555. {
  556. combineBuffers = false;
  557. break;
  558. }
  559. }
  560. // Check if keeping separate buffers allows to avoid 32-bit indices
  561. if ((combineBuffers) && (model.totalVertices_ > 65535))
  562. {
  563. bool allUnder65k = true;
  564. for (unsigned i = 0; i < model.meshes_.size(); ++i)
  565. {
  566. if (model.meshes_[i]->mNumVertices > 65535)
  567. allUnder65k = false;
  568. }
  569. if (allUnder65k == true)
  570. combineBuffers = false;
  571. }
  572. if (!combineBuffers)
  573. {
  574. std::cout << "Writing separate buffers" << std::endl;
  575. for (unsigned i = 0; i < model.meshes_.size(); ++i)
  576. {
  577. // Get the world transform of the mesh for baking into the vertices
  578. Matrix4x3 vertexTransform;
  579. Matrix3 normalTransform;
  580. Vector3 pos, scale;
  581. Quaternion rot;
  582. GetPosRotScale(GetMeshBakingTransform(model.meshNodes_[i], model.rootNode_), pos, rot, scale);
  583. vertexTransform = Matrix4x3(pos, rot, scale);
  584. normalTransform = rot.GetRotationMatrix();
  585. SharedPtr<IndexBuffer> ib(new IndexBuffer(context_));
  586. SharedPtr<VertexBuffer> vb(new VertexBuffer(context_));
  587. SharedPtr<Geometry> geom(new Geometry(context_));
  588. aiMesh* mesh = model.meshes_[i];
  589. std::cout << "Writing geometry " << i << " with " << mesh->mNumVertices << " vertices " << mesh->mNumFaces * 3
  590. << " indices" << std::endl;
  591. bool largeIndices = mesh->mNumVertices > 65535;
  592. unsigned elementMask = GetElementMask(mesh);
  593. ib->SetSize(mesh->mNumFaces * 3, largeIndices);
  594. vb->SetSize(mesh->mNumVertices, elementMask);
  595. // Build the index data
  596. void* indexData = ib->Lock(0, ib->GetIndexCount(), LOCK_NORMAL);
  597. if (!largeIndices)
  598. {
  599. unsigned short* dest = (unsigned short*)indexData;
  600. for (unsigned j = 0; j < mesh->mNumFaces; ++j)
  601. WriteShortIndices(dest, mesh, j, 0);
  602. }
  603. else
  604. {
  605. unsigned* dest = (unsigned*)indexData;
  606. for (unsigned j = 0; j < mesh->mNumFaces; ++j)
  607. WriteLargeIndices(dest, mesh, j, 0);
  608. }
  609. // Build the vertex data
  610. // If there are bones, get blend data
  611. std::vector<std::vector<unsigned char> > blendIndices;
  612. std::vector<std::vector<float> > blendWeights;
  613. std::vector<unsigned> boneMappings;
  614. if (model.bones_.size())
  615. GetBlendData(model, mesh, boneMappings, blendIndices, blendWeights);
  616. void* vertexData = vb->Lock(0, vb->GetVertexCount(), LOCK_NORMAL);
  617. float* dest = (float*)vertexData;
  618. for (unsigned j = 0; j < mesh->mNumVertices; ++j)
  619. WriteVertex(dest, mesh, j, elementMask, box, vertexTransform, normalTransform, blendIndices, blendWeights);
  620. ib->Unlock();
  621. vb->Unlock();
  622. // Define the geometry
  623. geom->SetIndexBuffer(ib);
  624. geom->SetVertexBuffer(0, vb);
  625. geom->SetDrawRange(TRIANGLE_LIST, 0, mesh->mNumFaces * 3, true);
  626. outModel->SetNumGeometryLodLevels(i, 1);
  627. outModel->SetGeometry(i, 0, geom);
  628. if (model.bones_.size() > MAX_SKIN_MATRICES)
  629. allBoneMappings.push_back(boneMappings);
  630. }
  631. }
  632. else
  633. {
  634. SharedPtr<IndexBuffer> ib(new IndexBuffer(context_));
  635. SharedPtr<VertexBuffer> vb(new VertexBuffer(context_));
  636. bool largeIndices = model.totalIndices_ > 65535;
  637. ib->SetSize(model.totalIndices_, largeIndices);
  638. vb->SetSize(model.totalVertices_, elementMask);
  639. unsigned startVertexOffset = 0;
  640. unsigned startIndexOffset = 0;
  641. void* indexData = ib->Lock(0, ib->GetIndexCount(), LOCK_NORMAL);
  642. void* vertexData = vb->Lock(0, vb->GetVertexCount(), LOCK_NORMAL);
  643. // The buffer is in CPU memory, and therefore locking is irrelevant. Unlock so that draw range checking can lock again
  644. ib->Unlock();
  645. vb->Unlock();
  646. for (unsigned i = 0; i < model.meshes_.size(); ++i)
  647. {
  648. // Get the world transform of the mesh for baking into the vertices
  649. Matrix4x3 vertexTransform;
  650. Matrix3 normalTransform;
  651. Vector3 pos, scale;
  652. Quaternion rot;
  653. GetPosRotScale(GetMeshBakingTransform(model.meshNodes_[i], model.rootNode_), pos, rot, scale);
  654. vertexTransform = Matrix4x3(pos, rot, scale);
  655. normalTransform = rot.GetRotationMatrix();
  656. SharedPtr<Geometry> geom(new Geometry(context_));
  657. aiMesh* mesh = model.meshes_[i];
  658. std::cout << "Writing geometry " << i << " with " << mesh->mNumVertices << " vertices " << mesh->mNumFaces * 3
  659. << " indices" << std::endl;
  660. // Build the index data
  661. if (!largeIndices)
  662. {
  663. unsigned short* dest = (unsigned short*)indexData + startIndexOffset;
  664. for (unsigned j = 0; j < mesh->mNumFaces; ++j)
  665. WriteShortIndices(dest, mesh, j, startVertexOffset);
  666. }
  667. else
  668. {
  669. unsigned* dest = (unsigned*)indexData + startIndexOffset;
  670. for (unsigned j = 0; j < mesh->mNumFaces; ++j)
  671. WriteLargeIndices(dest, mesh, j, startVertexOffset);
  672. }
  673. // Build the vertex data
  674. // If there are bones, get blend data
  675. std::vector<std::vector<unsigned char> > blendIndices;
  676. std::vector<std::vector<float> > blendWeights;
  677. std::vector<unsigned> boneMappings;
  678. if (model.bones_.size())
  679. GetBlendData(model, mesh, boneMappings, blendIndices, blendWeights);
  680. float* dest = (float*)((unsigned char*)vertexData + startVertexOffset * vb->GetVertexSize());
  681. for (unsigned j = 0; j < mesh->mNumVertices; ++j)
  682. WriteVertex(dest, mesh, j, elementMask, box, vertexTransform, normalTransform, blendIndices, blendWeights);
  683. // Define the geometry
  684. geom->SetIndexBuffer(ib);
  685. geom->SetVertexBuffer(0, vb);
  686. geom->SetDrawRange(TRIANGLE_LIST, startIndexOffset, mesh->mNumFaces * 3, true);
  687. outModel->SetNumGeometryLodLevels(i, 1);
  688. outModel->SetGeometry(i, 0, geom);
  689. if (model.bones_.size() > MAX_SKIN_MATRICES)
  690. allBoneMappings.push_back(boneMappings);
  691. startVertexOffset += mesh->mNumVertices;
  692. startIndexOffset += mesh->mNumFaces * 3;
  693. }
  694. }
  695. outModel->SetBoundingBox(box);
  696. // Build skeleton if necessary
  697. if ((model.bones_.size()) && (model.rootBone_))
  698. {
  699. std::cout << "Writing skeleton with " << model.bones_.size() << " bones, rootbone " +
  700. ToStdString(model.rootBone_->mName) << std::endl;
  701. Skeleton skeleton;
  702. std::vector<Bone>& bones = skeleton.GetModifiableBones();
  703. for (unsigned i = 0; i < model.bones_.size(); ++i)
  704. {
  705. aiNode* boneNode = model.bones_[i];
  706. std::string boneName(ToStdString(boneNode->mName));
  707. Bone newBone;
  708. newBone.name_ = boneName;
  709. aiMatrix4x4 transform = boneNode->mTransformation;
  710. // Make the root bone transform relative to the model's root node, if it is not already
  711. if (boneNode == model.rootBone_)
  712. transform = GetDerivedTransform(boneNode, model.rootNode_);
  713. GetPosRotScale(transform, newBone.initialPosition_, newBone.initialRotation_, newBone.initialScale_);
  714. // Get offset information if exists
  715. newBone.offsetMatrix_ = GetOffsetMatrix(model, boneName);
  716. newBone.radius_ = model.boneRadii_[i];
  717. newBone.boundingBox_ = model.boneHitboxes_[i];
  718. newBone.collisionMask_ = BONECOLLISION_SPHERE | BONECOLLISION_BOX;
  719. newBone.parentIndex_ = i;
  720. bones.push_back(newBone);
  721. }
  722. // Set the bone hierarchy
  723. for (unsigned i = 1; i < model.bones_.size(); ++i)
  724. {
  725. std::string parentName = ToStdString(model.bones_[i]->mParent->mName);
  726. for (unsigned j = 0; j < bones.size(); ++j)
  727. {
  728. if (bones[j].name_ == parentName)
  729. {
  730. bones[i].parentIndex_ = j;
  731. break;
  732. }
  733. }
  734. }
  735. outModel->SetSkeleton(skeleton);
  736. if (model.bones_.size() > MAX_SKIN_MATRICES)
  737. outModel->SetGeometryBoneMappings(allBoneMappings);
  738. }
  739. File outFile(context_);
  740. if (!outFile.Open(model.outName_, FILE_WRITE))
  741. ErrorExit("Could not open output file " + model.outName_);
  742. outModel->Save(outFile);
  743. }
  744. void BuildAndSaveAnimations(OutModel& model)
  745. {
  746. for (unsigned i = 0; i < model.animations_.size(); ++i)
  747. {
  748. aiAnimation* anim = model.animations_[i];
  749. std::string aniname_ = ToStdString(anim->mName);
  750. if (aniname_.empty())
  751. aniname_ = "Anim" + ToString(i + 1);
  752. std::string anioutName_ = GetPath(model.outName_) + GetFileName(model.outName_) + "_" + SanitateAssetName(aniname_) + ".ani";
  753. SharedPtr<Animation> outAnim(new Animation(context_));
  754. float tickConversion = 1.0f / (float)anim->mTicksPerSecond;
  755. outAnim->SetAnimationName(aniname_);
  756. outAnim->SetLength((float)anim->mDuration * tickConversion);
  757. std::cout << "Writing animation " << aniname_ << " length " << outAnim->GetLength() << std::endl;
  758. std::vector<AnimationTrack> tracks;
  759. for (unsigned j = 0; j < anim->mNumChannels; ++j)
  760. {
  761. aiNodeAnim* channel = anim->mChannels[j];
  762. std::string channelName = ToStdString(channel->mNodeName);
  763. unsigned boneIndex = GetBoneIndex(model, channelName);
  764. if (boneIndex == M_MAX_UNSIGNED)
  765. {
  766. std::cout << "Warning: skipping animation track " << channelName << " not found in model skeleton" << std::endl;
  767. continue;
  768. }
  769. aiNode* boneNode = model.bones_[boneIndex];
  770. AnimationTrack track;
  771. track.name_ = channelName;
  772. track.nameHash_ = StringHash(channelName);
  773. // Check which channels are used
  774. track.channelMask_ = 0;
  775. if (channel->mNumPositionKeys > 1)
  776. track.channelMask_ |= CHANNEL_POSITION;
  777. if (channel->mNumRotationKeys > 1)
  778. track.channelMask_ |= CHANNEL_ROTATION;
  779. if (channel->mNumScalingKeys > 1)
  780. track.channelMask_ |= CHANNEL_SCALE;
  781. // Check for redundant identity scale in all keyframes and remove in that case
  782. if (track.channelMask_ & CHANNEL_SCALE)
  783. {
  784. bool redundantScale = true;
  785. for (unsigned k = 0; k < channel->mNumScalingKeys; ++k)
  786. {
  787. float SCALE_EPSILON = 0.000001f;
  788. Vector3 scaleVec = ToVector3(channel->mScalingKeys[k].mValue);
  789. if ((fabsf(scaleVec.x_ - 1.0f) >= SCALE_EPSILON) || (fabsf(scaleVec.y_ - 1.0f) >= SCALE_EPSILON) ||
  790. (fabsf(scaleVec.z_ - 1.0f) >= SCALE_EPSILON))
  791. {
  792. redundantScale = false;
  793. break;
  794. }
  795. }
  796. if (redundantScale)
  797. track.channelMask_ &= ~CHANNEL_SCALE;
  798. }
  799. if (!track.channelMask_)
  800. std::cout << "Warning: skipping animation track " << channelName << " with no keyframes" << std::endl;
  801. // Currently only same amount of keyframes is supported
  802. // Note: should also check the times of individual keyframes for match
  803. if (((channel->mNumPositionKeys > 1) && (channel->mNumRotationKeys > 1) && (channel->mNumPositionKeys != channel->mNumRotationKeys)) ||
  804. ((channel->mNumPositionKeys > 1) && (channel->mNumScalingKeys > 1) && (channel->mNumPositionKeys != channel->mNumScalingKeys)) ||
  805. ((channel->mNumRotationKeys > 1) && (channel->mNumScalingKeys > 1) && (channel->mNumRotationKeys != channel->mNumScalingKeys)))
  806. {
  807. std::cout << "Warning: differing amounts of channel keyframes, skipping animation track " << channelName << std::endl;
  808. continue;
  809. }
  810. unsigned keyFrames = channel->mNumPositionKeys;
  811. if (channel->mNumRotationKeys > keyFrames)
  812. keyFrames = channel->mNumRotationKeys;
  813. if (channel->mNumScalingKeys > keyFrames)
  814. keyFrames = channel->mNumScalingKeys;
  815. for (unsigned k = 0; k < keyFrames; ++k)
  816. {
  817. AnimationKeyFrame kf;
  818. kf.time_ = 0.0f;
  819. kf.position_ = Vector3::ZERO;
  820. kf.rotation_ = Quaternion::IDENTITY;
  821. kf.scale_ = Vector3::UNITY;
  822. // Get time for the keyframe
  823. if ((track.channelMask_ & CHANNEL_POSITION) && (k < channel->mNumPositionKeys))
  824. kf.time_ = (float)channel->mPositionKeys[k].mTime * tickConversion;
  825. else if ((track.channelMask_ & CHANNEL_ROTATION) && (k < channel->mNumRotationKeys))
  826. kf.time_ = (float)channel->mRotationKeys[k].mTime * tickConversion;
  827. else if ((track.channelMask_ & CHANNEL_SCALE) && (k < channel->mNumScalingKeys))
  828. kf.time_ = (float)channel->mScalingKeys[k].mTime * tickConversion;
  829. // Start with the bone's base transform
  830. aiMatrix4x4 boneTransform = boneNode->mTransformation;
  831. aiVector3D pos, scale;
  832. aiQuaternion rot;
  833. boneTransform.Decompose(scale, rot, pos);
  834. // Then apply the active channels
  835. if ((track.channelMask_ & CHANNEL_POSITION) && (k < channel->mNumPositionKeys))
  836. pos = channel->mPositionKeys[k].mValue;
  837. if ((track.channelMask_ & CHANNEL_ROTATION) && (k < channel->mNumRotationKeys))
  838. rot = channel->mRotationKeys[k].mValue;
  839. if ((track.channelMask_ & CHANNEL_SCALE) && (k < channel->mNumScalingKeys))
  840. scale = channel->mScalingKeys[k].mValue;
  841. // If root bone, transform with the model root node transform
  842. if (!boneIndex)
  843. {
  844. aiMatrix4x4 transMat, scaleMat, rotMat;
  845. aiMatrix4x4::Translation(pos, transMat);
  846. aiMatrix4x4::Scaling(scale, scaleMat);
  847. rotMat = aiMatrix4x4(rot.GetMatrix());
  848. aiMatrix4x4 tform = transMat * rotMat * scaleMat;
  849. tform = GetDerivedTransform(tform, boneNode, model.rootNode_);
  850. tform.Decompose(scale, rot, pos);
  851. }
  852. if (track.channelMask_ & CHANNEL_POSITION)
  853. kf.position_ = ToVector3(pos);
  854. if (track.channelMask_ & CHANNEL_ROTATION)
  855. kf.rotation_ = ToQuaternion(rot);
  856. if (track.channelMask_ & CHANNEL_SCALE)
  857. kf.scale_ = ToVector3(scale);
  858. track.keyFrames_.push_back(kf);
  859. }
  860. tracks.push_back(track);
  861. }
  862. outAnim->SetTracks(tracks);
  863. File outFile(context_);
  864. if (!outFile.Open(anioutName_, FILE_WRITE))
  865. ErrorExit("Could not open output file " + anioutName_);
  866. outAnim->Save(outFile);
  867. }
  868. }
  869. void ExportScene(const std::string& outName)
  870. {
  871. OutScene outScene;
  872. outScene.outName_ = outName;
  873. outScene.rootNode_ = rootNode_;
  874. if (useSubdirs_)
  875. fileSystem_->CreateDir(resourcePath_ + "Models");
  876. CollectSceneModels(outScene, rootNode_);
  877. // Save models
  878. for (unsigned i = 0; i < outScene.models_.size(); ++i)
  879. BuildAndSaveModel(outScene.models_[i]);
  880. // Save scene
  881. BuildAndSaveScene(outScene);
  882. }
  883. void CollectSceneModels(OutScene& scene, aiNode* node)
  884. {
  885. std::vector<std::pair<aiNode*, aiMesh*> > meshes;
  886. GetMeshesUnderNode(meshes, node);
  887. if (meshes.size())
  888. {
  889. OutModel model;
  890. model.rootNode_ = node;
  891. model.outName_ = resourcePath_ + (useSubdirs_ ? "Models/" : "") + SanitateAssetName(ToStdString(node->mName)) + ".mdl";
  892. for (unsigned i = 0; i < meshes.size(); ++i)
  893. {
  894. aiMesh* mesh = meshes[i].second;
  895. unsigned meshIndex = GetMeshIndex(mesh);
  896. model.meshIndices_.insert(meshIndex);
  897. model.meshes_.push_back(mesh);
  898. model.meshNodes_.push_back(meshes[i].first);
  899. model.totalVertices_ += mesh->mNumVertices;
  900. model.totalIndices_ += mesh->mNumFaces * 3;
  901. }
  902. // Check if a model with identical mesh indices already exists. If yes, do not export twice
  903. bool unique = true;
  904. for (unsigned i = 0; i < scene.models_.size(); ++i)
  905. {
  906. if (scene.models_[i].meshIndices_ == model.meshIndices_)
  907. {
  908. std::cout << "Added node " << ToStdString(node->mName) << std::endl;
  909. scene.nodes_.push_back(node);
  910. scene.nodeModelIndices_.push_back(i);
  911. unique = false;
  912. break;
  913. }
  914. }
  915. if (unique)
  916. {
  917. std::cout << "Added model " << model.outName_ << std::endl;
  918. std::cout << "Added node " << ToStdString(node->mName) << std::endl;
  919. CollectBones(model);
  920. BuildBoneCollisionInfo(model);
  921. if (!noAnimations_)
  922. {
  923. CollectAnimations(model);
  924. BuildAndSaveAnimations(model);
  925. }
  926. scene.models_.push_back(model);
  927. scene.nodes_.push_back(node);
  928. scene.nodeModelIndices_.push_back(scene.models_.size() - 1);
  929. }
  930. }
  931. for (unsigned i = 0; i < node->mNumChildren; ++i)
  932. CollectSceneModels(scene, node->mChildren[i]);
  933. }
  934. void BuildAndSaveScene(OutScene& scene)
  935. {
  936. std::cout << "Writing scene" << std::endl;
  937. SharedPtr<Scene> outScene(new Scene(context_));
  938. outScene->SetName(GetFileName(scene.outName_));
  939. /// \todo Make the physics properties configurable
  940. PhysicsWorld* physicsWorld = outScene->CreateComponent<PhysicsWorld>();
  941. physicsWorld->SetGravity(Vector3(0.0f, -9.81f, 0.0f));
  942. /// \todo Make the octree properties configurable, or detect from the scene contents
  943. Octree* octree = outScene->CreateComponent<Octree>();
  944. if (createZone_)
  945. {
  946. Node* zoneNode = outScene->CreateChild("Zone");
  947. Zone* zone = zoneNode->CreateComponent<Zone>();
  948. zone->SetBoundingBox(BoundingBox(-1000.0f, 1000.f));
  949. zone->SetAmbientColor(Color(0.25f, 0.25f, 0.25f));
  950. Node* lightNode = outScene->CreateChild("GlobalLight");
  951. Light* light = lightNode->CreateComponent<Light>();
  952. light->SetLightType(LIGHT_DIRECTIONAL);
  953. lightNode->SetRotation(Quaternion(60.0f, 30.0f, 0.0f));
  954. }
  955. ResourceCache* cache = context_->GetSubsystem<ResourceCache>();
  956. for (unsigned i = 0; i < scene.nodes_.size(); ++i)
  957. {
  958. const OutModel& model = scene.models_[scene.nodeModelIndices_[i]];
  959. // Create a static model component for each node
  960. Node* modelNode = outScene->CreateChild(ToStdString(scene.nodes_[i]->mName));
  961. StaticModel* staticModel = modelNode->CreateComponent<StaticModel>();
  962. // Create a dummy model so that the reference can be stored
  963. std::string modelName = (useSubdirs_ ? "Models/" : "") + GetFileNameAndExtension(model.outName_);
  964. if (!cache->Exists(modelName))
  965. {
  966. Model* dummyModel = new Model(context_);
  967. dummyModel->SetName(modelName);
  968. dummyModel->SetNumGeometries(model.meshes_.size());
  969. cache->AddManualResource(dummyModel);
  970. }
  971. staticModel->SetModel(cache->GetResource<Model>(modelName));
  972. // Set a flattened transform
  973. Vector3 pos, scale;
  974. Quaternion rot;
  975. GetPosRotScale(GetDerivedTransform(scene.nodes_[i], rootNode_), pos, rot, scale);
  976. modelNode->SetTransform(pos, rot, scale);
  977. // Set materials if they are known
  978. for (unsigned j = 0; j < model.meshes_.size(); ++j)
  979. {
  980. std::string matName = GetMeshMaterialName(model.meshes_[j]);
  981. if (!matName.empty())
  982. {
  983. // Create a dummy material so that the reference can be stored
  984. if (!cache->Exists(matName))
  985. {
  986. Material* dummyMat = new Material(context_);
  987. dummyMat->SetName(matName);
  988. cache->AddManualResource(dummyMat);
  989. }
  990. staticModel->SetMaterial(cache->GetResource<Material>(matName));
  991. }
  992. }
  993. }
  994. File file(context_);
  995. if (!file.Open(scene.outName_, FILE_WRITE))
  996. ErrorExit("Could not open output file " + scene.outName_);
  997. if (!saveBinary_)
  998. outScene->SaveXML(file);
  999. else
  1000. outScene->Save(file);
  1001. }
  1002. void ExportMaterials(std::set<std::string>& usedTextures)
  1003. {
  1004. if (useSubdirs_)
  1005. fileSystem_->CreateDir(resourcePath_ + "Materials");
  1006. for (unsigned i = 0; i < scene_->mNumMaterials; ++i)
  1007. BuildAndSaveMaterial(scene_->mMaterials[i], usedTextures);
  1008. }
  1009. void BuildAndSaveMaterial(aiMaterial* material, std::set<std::string>& usedTextures)
  1010. {
  1011. // Material must have name so it can be successfully saved
  1012. aiString matNameStr;
  1013. material->Get(AI_MATKEY_NAME, matNameStr);
  1014. std::string matName = SanitateAssetName(ToStdString(matNameStr));
  1015. if (matName.empty())
  1016. return;
  1017. std::cout << "Writing material " << matName << std::endl;
  1018. // Do not actually create a material instance, but instead craft an xml file manually
  1019. XMLFile outMaterial(context_);
  1020. XMLElement materialElem = outMaterial.CreateRootElement("material");
  1021. std::string diffuseTexName;
  1022. std::string normalTexName;
  1023. Color diffuseColor;
  1024. bool hasAlpha = false;
  1025. bool twoSided = false;
  1026. float specIntensity = 0.0f;
  1027. float specPower = 1.0f;
  1028. aiString stringVal;
  1029. float floatVal;
  1030. int intVal;
  1031. aiColor3D colorVal;
  1032. if (material->Get(AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0), stringVal) == AI_SUCCESS)
  1033. diffuseTexName = GetFileNameAndExtension(ToStdString(stringVal));
  1034. if (material->Get(AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0), stringVal) == AI_SUCCESS)
  1035. normalTexName = GetFileNameAndExtension(ToStdString(stringVal));
  1036. if (material->Get(AI_MATKEY_COLOR_DIFFUSE, colorVal) == AI_SUCCESS)
  1037. diffuseColor = Color(colorVal.r, colorVal.g, colorVal.b);
  1038. if (material->Get(AI_MATKEY_OPACITY, floatVal) == AI_SUCCESS)
  1039. {
  1040. if (floatVal < 1.0f)
  1041. hasAlpha = true;
  1042. diffuseColor.a_ = floatVal;
  1043. }
  1044. if (material->Get(AI_MATKEY_SHININESS, floatVal) == AI_SUCCESS)
  1045. specPower = floatVal;
  1046. if (material->Get(AI_MATKEY_SHININESS_STRENGTH, floatVal) == AI_SUCCESS)
  1047. specIntensity = floatVal;
  1048. if (material->Get(AI_MATKEY_TWOSIDED, intVal) == AI_SUCCESS)
  1049. twoSided = (intVal != 0);
  1050. std::string techniqueName = "Techniques/NoTexture";
  1051. if (!diffuseTexName.empty())
  1052. {
  1053. techniqueName = "Techniques/Diff";
  1054. if (!normalTexName.empty())
  1055. techniqueName += "Normal";
  1056. }
  1057. if (hasAlpha)
  1058. techniqueName += "Alpha";
  1059. XMLElement techniqueElem = materialElem.CreateChildElement("technique");
  1060. techniqueElem.SetString("name", techniqueName + ".xml");
  1061. if (!diffuseTexName.empty())
  1062. {
  1063. XMLElement diffuseElem = materialElem.CreateChildElement("texture");
  1064. diffuseElem.SetString("unit", "diffuse");
  1065. diffuseElem.SetString("name", (useSubdirs_ ? "Textures/" : "") + diffuseTexName);
  1066. usedTextures.insert(diffuseTexName);
  1067. }
  1068. if (!normalTexName.empty())
  1069. {
  1070. XMLElement normalElem = materialElem.CreateChildElement("texture");
  1071. normalElem.SetString("unit", "diffuse");
  1072. normalElem.SetString("name", (useSubdirs_ ? "Textures/" : "") + normalTexName);
  1073. usedTextures.insert(normalTexName);
  1074. }
  1075. XMLElement diffuseColorElem = materialElem.CreateChildElement("parameter");
  1076. diffuseColorElem.SetString("name", "MatDiffColor");
  1077. diffuseColorElem.SetColor("value", diffuseColor);
  1078. XMLElement specularElem = materialElem.CreateChildElement("parameter");
  1079. specularElem.SetString("name", "MatSpecProperties");
  1080. specularElem.SetVector2("value", Vector2(specIntensity, specPower));
  1081. if (twoSided)
  1082. {
  1083. XMLElement cullElem = materialElem.CreateChildElement("cull");
  1084. XMLElement shadowCullElem = materialElem.CreateChildElement("shadowcull");
  1085. cullElem.SetString("value", "none");
  1086. shadowCullElem.SetString("value", "none");
  1087. }
  1088. File outFile(context_);
  1089. std::string outFileName = resourcePath_ + (useSubdirs_ ? "Materials/" : "" ) + matName + ".xml";
  1090. if (!outFile.Open(outFileName, FILE_WRITE))
  1091. ErrorExit("Could not open output file " + outFileName);
  1092. outMaterial.Save(outFile);
  1093. }
  1094. void CopyTextures(const std::set<std::string>& usedTextures, const std::string& sourcePath)
  1095. {
  1096. if (useSubdirs_)
  1097. fileSystem_->CreateDir(resourcePath_ + "Textures");
  1098. for (std::set<std::string>::const_iterator i = usedTextures.begin(); i != usedTextures.end(); ++i)
  1099. {
  1100. std::cout << "Copying texture " << *i << std::endl;
  1101. fileSystem_->Copy(sourcePath + *i, resourcePath_ + (useSubdirs_ ? "Textures/" : "") + *i);
  1102. }
  1103. }
  1104. void CombineLods(const std::vector<float>& lodDistances, const std::vector<std::string>& modelNames, const std::string& outName)
  1105. {
  1106. // Load models
  1107. std::vector<SharedPtr<Model> > srcModels;
  1108. for (unsigned i = 0; i < modelNames.size(); ++i)
  1109. {
  1110. std::cout << "Reading LOD level " << i << ": model " + modelNames[i] << " distance " << lodDistances[i] << std::endl;
  1111. File srcFile(context_);
  1112. srcFile.Open(modelNames[i]);
  1113. SharedPtr<Model> srcModel(new Model(context_));
  1114. if (!srcModel->Load(srcFile))
  1115. ErrorExit("Could not load input model " + modelNames[i]);
  1116. srcModels.push_back(srcModel);
  1117. }
  1118. // Check that none of the models already has LOD levels
  1119. for (unsigned i = 0; i < srcModels.size(); ++i)
  1120. {
  1121. for (unsigned j = 0; j < srcModels[i]->GetNumGeometries(); ++j)
  1122. {
  1123. if (srcModels[i]->GetNumGeometryLodLevels(j) > 1)
  1124. ErrorExit(modelNames[i] + " already has multiple LOD levels defined");
  1125. }
  1126. }
  1127. // Check for number of geometries (need to have same amount for now)
  1128. for (unsigned i = 1; i < srcModels.size(); ++i)
  1129. {
  1130. if (srcModels[i]->GetNumGeometries() != srcModels[0]->GetNumGeometries())
  1131. ErrorExit(modelNames[i] + " has different amount of geometries than " + modelNames[0]);
  1132. }
  1133. // If there are bones, check for compatibility (need to have exact match for now)
  1134. for (unsigned i = 1; i < srcModels.size(); ++i)
  1135. {
  1136. if (srcModels[i]->GetSkeleton().GetNumBones() != srcModels[0]->GetSkeleton().GetNumBones())
  1137. ErrorExit(modelNames[i] + " has different amount of bones than " + modelNames[0]);
  1138. for (unsigned j = 0; j < srcModels[0]->GetSkeleton().GetNumBones(); ++j)
  1139. {
  1140. if (srcModels[i]->GetSkeleton().GetBone(j)->name_ != srcModels[0]->GetSkeleton().GetBone(j)->name_)
  1141. ErrorExit(modelNames[i] + " has different bones than " + modelNames[0]);
  1142. }
  1143. if (srcModels[i]->GetGeometryBoneMappings() != srcModels[0]->GetGeometryBoneMappings())
  1144. ErrorExit(modelNames[i] + " has different per-geometry bone mappings than " + modelNames[0]);
  1145. }
  1146. // Create the final model
  1147. SharedPtr<Model> outModel(new Model(context_));
  1148. outModel->SetNumGeometries(srcModels[0]->GetNumGeometries());
  1149. for (unsigned i = 0; i < srcModels[0]->GetNumGeometries(); ++i)
  1150. {
  1151. outModel->SetNumGeometryLodLevels(i, srcModels.size());
  1152. for (unsigned j = 0; j < srcModels.size(); ++j)
  1153. {
  1154. Geometry* geom = srcModels[j]->GetGeometry(i, 0);
  1155. geom->SetLodDistance(lodDistances[j]);
  1156. outModel->SetGeometry(i, j, geom);
  1157. }
  1158. }
  1159. outModel->SetSkeleton(srcModels[0]->GetSkeleton());
  1160. outModel->SetGeometryBoneMappings(srcModels[0]->GetGeometryBoneMappings());
  1161. outModel->SetBoundingBox(srcModels[0]->GetBoundingBox());
  1162. /// \todo Vertex morphs are ignored for now
  1163. // Save the final model
  1164. std::cout << "Writing output model" << std::endl;
  1165. File outFile(context_);
  1166. if (!outFile.Open(outName, FILE_WRITE))
  1167. ErrorExit("Could not open output file " + outName);
  1168. outModel->Save(outFile);
  1169. }
  1170. void GetMeshesUnderNode(std::vector<std::pair<aiNode*, aiMesh*> >& dest, aiNode* node)
  1171. {
  1172. for (unsigned i = 0; i < node->mNumMeshes; ++i)
  1173. dest.push_back(std::make_pair(node, scene_->mMeshes[node->mMeshes[i]]));
  1174. }
  1175. unsigned GetMeshIndex(aiMesh* mesh)
  1176. {
  1177. for (unsigned i = 0; i < scene_->mNumMeshes; ++i)
  1178. {
  1179. if (scene_->mMeshes[i] == mesh)
  1180. return i;
  1181. }
  1182. return M_MAX_UNSIGNED;
  1183. }
  1184. unsigned GetBoneIndex(OutModel& model, const std::string& boneName)
  1185. {
  1186. for (unsigned i = 0; i < model.bones_.size(); ++i)
  1187. {
  1188. if (ToStdString(model.bones_[i]->mName) == boneName)
  1189. return i;
  1190. }
  1191. return M_MAX_UNSIGNED;
  1192. }
  1193. aiBone* GetMeshBone(OutModel& model, const std::string& boneName)
  1194. {
  1195. for (unsigned i = 0; i < model.meshes_.size(); ++i)
  1196. {
  1197. aiMesh* mesh = model.meshes_[i];
  1198. for (unsigned j = 0; j < mesh->mNumBones; ++j)
  1199. {
  1200. aiBone* bone = mesh->mBones[j];
  1201. if (ToStdString(bone->mName) == boneName)
  1202. return bone;
  1203. }
  1204. }
  1205. return 0;
  1206. }
  1207. Matrix4x3 GetOffsetMatrix(OutModel& model, const std::string& boneName)
  1208. {
  1209. for (unsigned i = 0; i < model.meshes_.size(); ++i)
  1210. {
  1211. aiMesh* mesh = model.meshes_[i];
  1212. aiNode* node = model.meshNodes_[i];
  1213. for (unsigned j = 0; j < mesh->mNumBones; ++j)
  1214. {
  1215. aiBone* bone = mesh->mBones[j];
  1216. if (ToStdString(bone->mName) == boneName)
  1217. {
  1218. aiMatrix4x4 offset = bone->mOffsetMatrix;
  1219. aiMatrix4x4 nodeDerivedInverse = GetMeshBakingTransform(node, model.rootNode_);
  1220. nodeDerivedInverse.Inverse();
  1221. offset *= nodeDerivedInverse;
  1222. Matrix4x3 ret;
  1223. memcpy(&ret.m00_, &offset.a1, sizeof(Matrix4x3));
  1224. return ret;
  1225. }
  1226. }
  1227. }
  1228. return Matrix4x3::IDENTITY;
  1229. }
  1230. void GetBlendData(OutModel& model, aiMesh* mesh, std::vector<unsigned>& boneMappings, std::vector<std::vector<unsigned char> >&
  1231. blendIndices, std::vector<std::vector<float> >& blendWeights)
  1232. {
  1233. blendIndices.resize(mesh->mNumVertices);
  1234. blendWeights.resize(mesh->mNumVertices);
  1235. boneMappings.clear();
  1236. // If model has more bones than can fit vertex shader parameters, write the per-geometry mappings
  1237. if (model.bones_.size() > MAX_SKIN_MATRICES)
  1238. {
  1239. if (mesh->mNumBones > MAX_SKIN_MATRICES)
  1240. ErrorExit("Geometry has too many bone influences");
  1241. boneMappings.resize(mesh->mNumBones);
  1242. for (unsigned i = 0; i < mesh->mNumBones; ++i)
  1243. {
  1244. aiBone* bone = mesh->mBones[i];
  1245. std::string boneName = ToStdString(bone->mName);
  1246. unsigned globalIndex = GetBoneIndex(model, boneName);
  1247. if (globalIndex == M_MAX_UNSIGNED)
  1248. ErrorExit("Bone " + boneName + " not found");
  1249. boneMappings[i] = globalIndex;
  1250. for (unsigned j = 0; j < bone->mNumWeights; ++j)
  1251. {
  1252. unsigned vertex = bone->mWeights[j].mVertexId;
  1253. blendIndices[vertex].push_back(i);
  1254. blendWeights[vertex].push_back(bone->mWeights[j].mWeight);
  1255. if (blendWeights[vertex].size() > 4)
  1256. ErrorExit("More than 4 bone influences on vertex");
  1257. }
  1258. }
  1259. }
  1260. else
  1261. {
  1262. for (unsigned i = 0; i < mesh->mNumBones; ++i)
  1263. {
  1264. aiBone* bone = mesh->mBones[i];
  1265. std::string boneName = ToStdString(bone->mName);
  1266. unsigned globalIndex = GetBoneIndex(model, boneName);
  1267. if (globalIndex == M_MAX_UNSIGNED)
  1268. ErrorExit("Bone " + boneName + " not found");
  1269. for (unsigned j = 0; j < bone->mNumWeights; ++j)
  1270. {
  1271. unsigned vertex = bone->mWeights[j].mVertexId;
  1272. blendIndices[vertex].push_back(globalIndex);
  1273. blendWeights[vertex].push_back(bone->mWeights[j].mWeight);
  1274. if (blendWeights[vertex].size() > 4)
  1275. ErrorExit("More than 4 bone influences on vertex");
  1276. }
  1277. }
  1278. }
  1279. }
  1280. std::string GetMeshMaterialName(aiMesh* mesh)
  1281. {
  1282. aiMaterial* material = scene_->mMaterials[mesh->mMaterialIndex];
  1283. aiString matNameStr;
  1284. material->Get(AI_MATKEY_NAME, matNameStr);
  1285. std::string matName = SanitateAssetName(ToStdString(matNameStr));
  1286. if (matName.empty())
  1287. return matName;
  1288. else
  1289. return (useSubdirs_ ? "Materials/" : "") + matName + ".xml";
  1290. }
  1291. void WriteShortIndices(unsigned short*& dest, aiMesh* mesh, unsigned index, unsigned offset)
  1292. {
  1293. *dest++ = mesh->mFaces[index].mIndices[0] + offset;
  1294. *dest++ = mesh->mFaces[index].mIndices[1] + offset;
  1295. *dest++ = mesh->mFaces[index].mIndices[2] + offset;
  1296. }
  1297. void WriteLargeIndices(unsigned*& dest, aiMesh* mesh, unsigned index, unsigned offset)
  1298. {
  1299. *dest++ = mesh->mFaces[index].mIndices[0] + offset;
  1300. *dest++ = mesh->mFaces[index].mIndices[1] + offset;
  1301. *dest++ = mesh->mFaces[index].mIndices[2] + offset;
  1302. }
  1303. void WriteVertex(float*& dest, aiMesh* mesh, unsigned index, unsigned elementMask, BoundingBox& box,
  1304. const Matrix4x3& vertexTransform, const Matrix3& normalTransform, std::vector<std::vector<unsigned char> >& blendIndices,
  1305. std::vector<std::vector<float> >& blendWeights)
  1306. {
  1307. Vector3 vertex = vertexTransform * ToVector3(mesh->mVertices[index]);
  1308. box.Merge(vertex);
  1309. *dest++ = vertex.x_;
  1310. *dest++ = vertex.y_;
  1311. *dest++ = vertex.z_;
  1312. if (elementMask & MASK_NORMAL)
  1313. {
  1314. Vector3 normal = normalTransform * ToVector3(mesh->mNormals[index]);
  1315. *dest++ = normal.x_;
  1316. *dest++ = normal.y_;
  1317. *dest++ = normal.z_;
  1318. }
  1319. if (elementMask & MASK_COLOR)
  1320. {
  1321. *((unsigned*)dest) = Color(mesh->mColors[0][index].r, mesh->mColors[0][index].g, mesh->mColors[0][index].b,
  1322. mesh->mColors[0][index].a).ToUInt();
  1323. ++dest;
  1324. }
  1325. if (elementMask & MASK_TEXCOORD1)
  1326. {
  1327. Vector3 texCoord = ToVector3(mesh->mTextureCoords[0][index]);
  1328. *dest++ = texCoord.x_;
  1329. *dest++ = texCoord.y_;
  1330. }
  1331. if (elementMask & MASK_TEXCOORD2)
  1332. {
  1333. Vector3 texCoord = ToVector3(mesh->mTextureCoords[1][index]);
  1334. *dest++ = texCoord.x_;
  1335. *dest++ = texCoord.y_;
  1336. }
  1337. if (elementMask & MASK_TANGENT)
  1338. {
  1339. Vector3 tangent = normalTransform * ToVector3(mesh->mTangents[index]);
  1340. Vector3 normal = normalTransform * ToVector3(mesh->mNormals[index]);
  1341. Vector3 bitangent = normalTransform * ToVector3(mesh->mBitangents[index]);
  1342. // Check handedness
  1343. float w = 1.0f;
  1344. if ((tangent.CrossProduct(normal)).DotProduct(bitangent) < 0.5f)
  1345. w = -1.0f;
  1346. *dest++ = tangent.x_;
  1347. *dest++ = tangent.y_;
  1348. *dest++ = tangent.z_;
  1349. *dest++ = w;
  1350. }
  1351. if (elementMask & MASK_BLENDWEIGHTS)
  1352. {
  1353. for (unsigned i = 0; i < 4; ++i)
  1354. {
  1355. if (i < blendWeights[index].size())
  1356. *dest++ = blendWeights[index][i];
  1357. else
  1358. *dest++ = 0.0f;
  1359. }
  1360. }
  1361. if (elementMask & MASK_BLENDINDICES)
  1362. {
  1363. unsigned char* destBytes = (unsigned char*)dest;
  1364. ++dest;
  1365. for (unsigned i = 0; i < 4; ++i)
  1366. {
  1367. if (i < blendIndices[index].size())
  1368. *destBytes++ = blendIndices[index][i];
  1369. else
  1370. *destBytes++ = 0;
  1371. }
  1372. }
  1373. }
  1374. unsigned GetElementMask(aiMesh* mesh)
  1375. {
  1376. unsigned elementMask = MASK_POSITION;
  1377. if (mesh->HasNormals())
  1378. elementMask |= MASK_NORMAL;
  1379. if (mesh->HasTangentsAndBitangents())
  1380. elementMask |= MASK_TANGENT;
  1381. if (mesh->GetNumColorChannels() > 0)
  1382. elementMask |= MASK_COLOR;
  1383. if (mesh->GetNumUVChannels() > 0)
  1384. elementMask |= MASK_TEXCOORD1;
  1385. if (mesh->GetNumUVChannels() > 1)
  1386. elementMask |= MASK_TEXCOORD2;
  1387. if (mesh->HasBones())
  1388. elementMask |= (MASK_BLENDWEIGHTS | MASK_BLENDINDICES);
  1389. return elementMask;
  1390. }
  1391. aiNode* FindNode(const std::string& name, aiNode* rootNode, bool caseSensitive)
  1392. {
  1393. if (!rootNode)
  1394. return 0;
  1395. if (!caseSensitive)
  1396. {
  1397. if (ToLower(ToStdString(rootNode->mName)) == ToLower(name))
  1398. return rootNode;
  1399. }
  1400. else
  1401. {
  1402. if (ToStdString(rootNode->mName) == name)
  1403. return rootNode;
  1404. }
  1405. for (unsigned i = 0; i < rootNode->mNumChildren; ++i)
  1406. {
  1407. aiNode* found = FindNode(name, rootNode->mChildren[i], caseSensitive);
  1408. if (found)
  1409. return found;
  1410. }
  1411. return 0;
  1412. }
  1413. aiMatrix4x4 GetDerivedTransform(aiNode* node, aiNode* rootNode)
  1414. {
  1415. return GetDerivedTransform(node->mTransformation, node, rootNode);
  1416. }
  1417. aiMatrix4x4 GetDerivedTransform(aiMatrix4x4 transform, aiNode* node, aiNode* rootNode)
  1418. {
  1419. // If basenode is defined, go only up to it in the parent chain
  1420. while ((node) && (node != rootNode))
  1421. {
  1422. node = node->mParent;
  1423. if (node)
  1424. transform = node->mTransformation * transform;
  1425. }
  1426. return transform;
  1427. }
  1428. aiMatrix4x4 GetMeshBakingTransform(aiNode* meshNode, aiNode* modelRootNode)
  1429. {
  1430. if (meshNode == modelRootNode)
  1431. return aiMatrix4x4();
  1432. else
  1433. return GetDerivedTransform(meshNode, modelRootNode);
  1434. }
  1435. void GetPosRotScale(const aiMatrix4x4& transform, Vector3& pos, Quaternion& rot, Vector3& scale)
  1436. {
  1437. aiVector3D aiPos;
  1438. aiQuaternion aiRot;
  1439. aiVector3D aiScale;
  1440. transform.Decompose(aiScale, aiRot, aiPos);
  1441. pos = ToVector3(aiPos);
  1442. rot = ToQuaternion(aiRot);
  1443. scale = ToVector3(aiScale);
  1444. }
  1445. std::string ToStdString(const aiString& str)
  1446. {
  1447. if ((!str.data) || (!str.length))
  1448. return std::string();
  1449. else
  1450. return std::string(str.data);
  1451. }
  1452. Vector3 ToVector3(const aiVector3D& vec)
  1453. {
  1454. return Vector3(vec.x, vec.y, vec.z);
  1455. }
  1456. Vector2 ToVector2(const aiVector2D& vec)
  1457. {
  1458. return Vector2(vec.x, vec.y);
  1459. }
  1460. Quaternion ToQuaternion(const aiQuaternion& quat)
  1461. {
  1462. return Quaternion(quat.w, quat.x, quat.y, quat.z);
  1463. }
  1464. std::string SanitateAssetName(const std::string& name)
  1465. {
  1466. std::string fixedName = name;
  1467. ReplaceInPlace(fixedName, "<", "");
  1468. ReplaceInPlace(fixedName, ">", "");
  1469. ReplaceInPlace(fixedName, "?", "");
  1470. ReplaceInPlace(fixedName, "*", "");
  1471. ReplaceInPlace(fixedName, ":", "");
  1472. ReplaceInPlace(fixedName, "\"", "");
  1473. ReplaceInPlace(fixedName, "/", "");
  1474. ReplaceInPlace(fixedName, "\\", "");
  1475. ReplaceInPlace(fixedName, "|", "");
  1476. return fixedName;
  1477. }
  1478. void ErrorExit(const std::string& error)
  1479. {
  1480. std::cout << error;
  1481. exit(1);
  1482. }