Main.cpp 22 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057
  1. #include <assimp/Importer.hpp>
  2. #include <assimp/scene.h>
  3. #include <assimp/postprocess.h>
  4. #include <iostream>
  5. #include <stdexcept>
  6. #include <cstdarg>
  7. #include <fstream>
  8. #include <cstdint>
  9. #include <sstream>
  10. #include <cassert>
  11. static const char* xmlHeader = R"(<?xml version="1.0" encoding="UTF-8" ?>)";
  12. struct Mesh
  13. {
  14. uint32_t index = 0xFFFFFFFF; ///< Mesh index in the scene
  15. std::vector<aiMatrix4x4> transforms;
  16. uint32_t mtlIndex = 0xFFFFFFFF;
  17. };
  18. struct Material
  19. {
  20. uint32_t index = 0xFFFFFFFF;
  21. std::vector<uint32_t> meshIndices;
  22. };
  23. struct Config
  24. {
  25. std::string inputFname;
  26. std::string outDir;
  27. std::string rpath;
  28. std::string texpath;
  29. bool flipyz = false;
  30. std::vector<Mesh> meshes;
  31. std::vector<Material> materials;
  32. };
  33. Config config;
  34. //==============================================================================
  35. // Log and errors
  36. #define STR(s) #s
  37. #define XSTR(s) STR(s)
  38. #define LOGI(...) \
  39. printf("[I] (" __FILE__ ":" XSTR(__LINE__) ") " __VA_ARGS__)
  40. #define ERROR(...) \
  41. do { \
  42. fprintf(stderr, "[E] (" __FILE__ ":" XSTR(__LINE__) ") " __VA_ARGS__); \
  43. exit(0); \
  44. } while(0)
  45. #define LOGW(...) \
  46. fprintf(stderr, "[W] (" __FILE__ ":" XSTR(__LINE__) ") " __VA_ARGS__)
  47. //==============================================================================
  48. static std::string replaceAllString(
  49. const std::string& str,
  50. const std::string& from,
  51. const std::string& to)
  52. {
  53. if(from.empty())
  54. {
  55. return str;
  56. }
  57. std::string out = str;
  58. size_t start_pos = 0;
  59. while((start_pos = out.find(from, start_pos)) != std::string::npos)
  60. {
  61. out.replace(start_pos, from.length(), to);
  62. start_pos += to.length();
  63. }
  64. return out;
  65. }
  66. //==============================================================================
  67. static std::string getFilename(const std::string& path)
  68. {
  69. std::string out;
  70. const size_t last = path.find_last_of("/");
  71. if(std::string::npos != last)
  72. {
  73. out.insert(out.end(), path.begin() + last + 1, path.end());
  74. }
  75. else
  76. {
  77. out = path;
  78. }
  79. return out;
  80. }
  81. //==============================================================================
  82. static aiMatrix4x4 toAnkiMatrix(const aiMatrix4x4& in)
  83. {
  84. static const aiMatrix4x4 toLeftHanded(
  85. 1, 0, 0, 0,
  86. 0, 0, 1, 0,
  87. 0, -1, 0, 0,
  88. 0, 0, 0, 1);
  89. static const aiMatrix4x4 toLeftHandedInv(
  90. 1, 0, 0, 0,
  91. 0, 0, -1, 0,
  92. 0, 1, 0, 0,
  93. 0, 0, 0, 1);
  94. if(config.flipyz)
  95. {
  96. return toLeftHanded * in * toLeftHandedInv;
  97. }
  98. else
  99. {
  100. return in;
  101. }
  102. }
  103. //==============================================================================
  104. static aiMatrix3x3 toAnkiMatrix(const aiMatrix3x3& in)
  105. {
  106. static const aiMatrix3x3 toLeftHanded(
  107. 1, 0, 0,
  108. 0, 0, 1,
  109. 0, -1, 0);
  110. static const aiMatrix3x3 toLeftHandedInv(
  111. 1, 0, 0,
  112. 0, 0, -1,
  113. 0, 1, 0);
  114. if(config.flipyz)
  115. {
  116. return toLeftHanded * in;
  117. }
  118. else
  119. {
  120. return in;
  121. }
  122. }
  123. //==============================================================================
  124. static void parseConfig(int argc, char** argv)
  125. {
  126. static const char* usage = R"(Usage: %s in_file out_dir [options]
  127. Options:
  128. -rpath <string> : Append a string to the meshes and materials
  129. -texrpath <string> : Append a string to the textures paths
  130. -flipyz : Flip y with z (For blender exports)
  131. )";
  132. // Parse config
  133. if(argc < 3)
  134. {
  135. goto error;
  136. }
  137. config.inputFname = argv[1];
  138. config.outDir = argv[2] + std::string("/");
  139. for(int i = 3; i < argc; i++)
  140. {
  141. if(strcmp(argv[i], "-texrpath") == 0)
  142. {
  143. ++i;
  144. if(i < argc)
  145. {
  146. config.texpath = argv[i] + std::string("/");
  147. }
  148. else
  149. {
  150. goto error;
  151. }
  152. }
  153. else if(strcmp(argv[i], "-rpath") == 0)
  154. {
  155. ++i;
  156. if(i < argc)
  157. {
  158. config.rpath = argv[i] + std::string("/");
  159. }
  160. else
  161. {
  162. goto error;
  163. }
  164. }
  165. else if(strcmp(argv[i], "-flipyz") == 0)
  166. {
  167. config.flipyz = true;
  168. }
  169. else
  170. {
  171. goto error;
  172. }
  173. }
  174. if(config.rpath.empty())
  175. {
  176. config.rpath = config.outDir;
  177. }
  178. if(config.texpath.empty())
  179. {
  180. config.texpath = config.outDir;
  181. }
  182. return;
  183. error:
  184. printf(usage, argv[0]);
  185. exit(0);
  186. }
  187. //==============================================================================
  188. /// Load the scene
  189. static const aiScene& load(
  190. const std::string& filename,
  191. Assimp::Importer& importer)
  192. {
  193. LOGI("Loading file %s\n", filename.c_str());
  194. const aiScene* scene = importer.ReadFile(filename, 0
  195. //| aiProcess_FindInstances
  196. | aiProcess_Triangulate
  197. | aiProcess_JoinIdenticalVertices
  198. //| aiProcess_SortByPType
  199. | aiProcess_ImproveCacheLocality
  200. | aiProcess_OptimizeMeshes
  201. | aiProcess_RemoveRedundantMaterials
  202. );
  203. if(!scene)
  204. {
  205. ERROR("%s\n", importer.GetErrorString());
  206. }
  207. LOGI("File loaded successfully!\n");
  208. return *scene;
  209. }
  210. //==============================================================================
  211. static const uint32_t MAX_BONES_PER_VERTEX = 4;
  212. /// Bone/weight info per vertex
  213. struct Vw
  214. {
  215. uint32_t boneIds[MAX_BONES_PER_VERTEX];
  216. float weigths[MAX_BONES_PER_VERTEX];
  217. uint32_t bonesCount;
  218. };
  219. //==============================================================================
  220. static void exportMesh(
  221. const aiMesh& mesh,
  222. const std::string* name_,
  223. const aiMatrix4x4* transform)
  224. {
  225. std::string name = (name_) ? *name_ : mesh.mName.C_Str();
  226. std::fstream file;
  227. LOGI("Exporting mesh %s\n", name.c_str());
  228. uint32_t vertsCount = mesh.mNumVertices;
  229. // Open file
  230. file.open(config.outDir + name + ".ankimesh",
  231. std::ios::out | std::ios::binary);
  232. // Write magic word
  233. file.write("ANKIMESH", 8);
  234. // Write the name
  235. uint32_t size = name.size();
  236. file.write((char*)&size, sizeof(uint32_t));
  237. file.write(&name[0], size);
  238. // Write positions
  239. file.write((char*)&vertsCount, sizeof(uint32_t));
  240. for(uint32_t i = 0; i < mesh.mNumVertices; i++)
  241. {
  242. aiVector3D pos = mesh.mVertices[i];
  243. // Transform
  244. if(transform)
  245. {
  246. pos = (*transform) * pos;
  247. }
  248. // flip
  249. if(config.flipyz)
  250. {
  251. static const aiMatrix4x4 toLefthanded(
  252. 1, 0, 0, 0,
  253. 0, 0, 1, 0,
  254. 0, -1, 0, 0,
  255. 0, 0, 0, 1);
  256. pos = toLefthanded * pos;
  257. }
  258. for(uint32_t j = 0; j < 3; j++)
  259. {
  260. file.write((char*)&pos[j], sizeof(float));
  261. }
  262. }
  263. // Write the indices
  264. file.write((char*)&mesh.mNumFaces, sizeof(uint32_t));
  265. for(uint32_t i = 0; i < mesh.mNumFaces; i++)
  266. {
  267. const aiFace& face = mesh.mFaces[i];
  268. if(face.mNumIndices != 3)
  269. {
  270. ERROR("For some reason the assimp didn't triangulate\n");
  271. }
  272. for(uint32_t j = 0; j < 3; j++)
  273. {
  274. uint32_t index = face.mIndices[j];
  275. file.write((char*)&index, sizeof(uint32_t));
  276. }
  277. }
  278. // Write the tex coords
  279. file.write((char*)&vertsCount, sizeof(uint32_t));
  280. // For all channels
  281. for(uint32_t ch = 0; ch < mesh.GetNumUVChannels(); ch++)
  282. {
  283. if(mesh.mNumUVComponents[ch] != 2)
  284. {
  285. ERROR("Incorrect number of UV components\n");
  286. }
  287. // For all tex coords of this channel
  288. for(uint32_t i = 0; i < vertsCount; i++)
  289. {
  290. aiVector3D texCoord = mesh.mTextureCoords[ch][i];
  291. for(uint32_t j = 0; j < 2; j++)
  292. {
  293. file.write((char*)&texCoord[j], sizeof(float));
  294. }
  295. }
  296. }
  297. // Write bone weigths count
  298. if(mesh.HasBones())
  299. {
  300. #if 0
  301. // Write file
  302. file.write((char*)&vertsCount, sizeof(uint32_t));
  303. // Gather info for each vertex
  304. std::vector<Vw> vw;
  305. vw.resize(vertsCount);
  306. memset(&vw[0], 0, sizeof(Vw) * vertsCount);
  307. // For all bones
  308. for(uint32_t i = 0; i < mesh.mNumBones; i++)
  309. {
  310. const aiBone& bone = *mesh.mBones[i];
  311. // for every weights of the bone
  312. for(uint32_t j = 0; j < bone.mWeightsCount; j++)
  313. {
  314. const aiVertexWeight& weigth = bone.mWeights[j];
  315. // Sanity check
  316. if(weight.mVertexId >= vertCount)
  317. {
  318. ERROR("Out of bounds vert ID");
  319. }
  320. Vm& a = vm[weight.mVertexId];
  321. // Check out of bounds
  322. if(a.bonesCount >= MAX_BONES_PER_VERTEX)
  323. {
  324. LOGW("Too many bones for vertex %d\n", weigth.mVertexId);
  325. continue;
  326. }
  327. // Write to vertex
  328. a.boneIds[a.bonesCount] = i;
  329. a.weigths[a.bonesCount] = weigth.mWeigth;
  330. ++a.bonesCount;
  331. }
  332. // Now write the file
  333. }
  334. #endif
  335. }
  336. else
  337. {
  338. uint32_t num = 0;
  339. file.write((char*)&num, sizeof(uint32_t));
  340. }
  341. }
  342. //==============================================================================
  343. static void exportSkeleton(const aiMesh& mesh)
  344. {
  345. assert(mesh.HasBones());
  346. std::string name = mesh.mName.C_Str();
  347. std::fstream file;
  348. LOGI("Exporting skeleton %s\n", name.c_str());
  349. // Open file
  350. file.open(config.outDir + name + ".skel", std::ios::out);
  351. file << xmlHeader << "\n";
  352. file << "<skeleton>\n";
  353. file << "\t<bones>\n";
  354. bool rootBoneFound = false;
  355. for(uint32_t i = 0; i < mesh.mNumBones; i++)
  356. {
  357. const aiBone& bone = *mesh.mBones[i];
  358. file << "\t\t<bone>\n";
  359. // <name>
  360. file << "\t\t\t<name>" << bone.mName.C_Str() << "</name>\n";
  361. if(strcmp(bone.mName.C_Str(), "root") == 0)
  362. {
  363. rootBoneFound = true;
  364. }
  365. // <transform>
  366. file << "\t\t\t<transform>";
  367. for(uint32_t j = 0; j < 16; j++)
  368. {
  369. file << bone.mOffsetMatrix[j] << " ";
  370. }
  371. file << "</transform>\n";
  372. file << "\t\t</bone>\n";
  373. }
  374. if(!rootBoneFound)
  375. {
  376. ERROR("There should be one bone named \"root\"\n");
  377. }
  378. file << "\t</bones>\n";
  379. file << "</skeleton>\n";
  380. }
  381. //==============================================================================
  382. static std::string getMaterialName(const aiMaterial& mtl)
  383. {
  384. aiString ainame;
  385. std::string name;
  386. if(mtl.Get(AI_MATKEY_NAME, ainame) == AI_SUCCESS)
  387. {
  388. name = ainame.C_Str();
  389. }
  390. else
  391. {
  392. ERROR("Material's name is missing\n");
  393. }
  394. return name;
  395. }
  396. //==============================================================================
  397. static void exportMaterial(
  398. const aiScene& scene,
  399. const aiMaterial& mtl,
  400. bool instanced,
  401. const std::string* name_)
  402. {
  403. std::string diffTex;
  404. std::string normTex;
  405. std::string name;
  406. if(name_)
  407. {
  408. name = *name_;
  409. }
  410. else
  411. {
  412. name = getMaterialName(mtl);
  413. }
  414. LOGI("Exporting material %s\n", name.c_str());
  415. // Diffuse texture
  416. if(mtl.GetTextureCount(aiTextureType_DIFFUSE) < 1)
  417. {
  418. ERROR("Material has no diffuse textures\n");
  419. }
  420. aiString path;
  421. if(mtl.GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS)
  422. {
  423. diffTex = getFilename(path.C_Str());
  424. }
  425. else
  426. {
  427. ERROR("Failed to retrieve texture\n");
  428. }
  429. // Normal texture
  430. if(mtl.GetTextureCount(aiTextureType_NORMALS) > 0)
  431. {
  432. if(mtl.GetTexture(aiTextureType_NORMALS, 0, &path) == AI_SUCCESS)
  433. {
  434. normTex = getFilename(path.C_Str());
  435. }
  436. else
  437. {
  438. ERROR("Failed to retrieve texture\n");
  439. }
  440. }
  441. // Write file
  442. static const char* diffMtlStr =
  443. #include "diffTemplateMtl.h"
  444. ;
  445. static const char* diffNormMtlStr =
  446. #include "diffNormTemplateMtl.h"
  447. ;
  448. std::fstream file;
  449. file.open(config.outDir + name + ".ankimtl", std::ios::out);
  450. // Chose the correct template
  451. std::string str;
  452. if(normTex.size() == 0)
  453. {
  454. str = diffMtlStr;
  455. }
  456. else
  457. {
  458. str = replaceAllString(diffNormMtlStr, "%normalMap%",
  459. config.texpath + normTex);
  460. }
  461. str = replaceAllString(str, "%instanced%", (instanced) ? "1" : "0");
  462. str = replaceAllString(str, "%diffuseMap%", config.texpath + diffTex);
  463. file << str;
  464. }
  465. //==============================================================================
  466. static void exportLight(
  467. const aiLight& light,
  468. std::fstream& file)
  469. {
  470. if(light.mType != aiLightSource_POINT || light.mType != aiLightSource_SPOT)
  471. {
  472. LOGW("Skipping light %s. Unsupported type\n", light.mName.C_Str());
  473. return;
  474. }
  475. file << "\t<light>\n";
  476. file << "\t\t<name>" << light.mName.C_Str() << "</name>\n";
  477. file << "\t\t<diffuseColor>"
  478. << light.mColorDiffuse[0] << " "
  479. << light.mColorDiffuse[1] << " "
  480. << light.mColorDiffuse[2] << " "
  481. << light.mColorDiffuse[3]
  482. << "</diffuseColor>\n";
  483. file << "\t\t<specularColor>"
  484. << light.mColorSpecular[0] << " "
  485. << light.mColorSpecular[1] << " "
  486. << light.mColorSpecular[2] << " "
  487. << light.mColorSpecular[3]
  488. << "</specularColor>\n";
  489. aiMatrix4x4 trf;
  490. aiMatrix4x4::Translation(light.mPosition, trf);
  491. switch(light.mType)
  492. {
  493. case aiLightSource_POINT:
  494. {
  495. file << "\t\t<type>point</type>\n";
  496. // At this point I want the radius and have the attenuation factors
  497. // att = Ac + Al*d + Aq*d^2. When d = r then att = 0.0. Also if we
  498. // assume that Ac is 0 then:
  499. // 0 = Al*r + Aq*r^2. Solving by r is easy
  500. float r = -light.mAttenuationLinear / light.mAttenuationQuadratic;
  501. file << "\t\t<radius>" << r << "</radius>\n";
  502. break;
  503. }
  504. case aiLightSource_SPOT:
  505. file << "\t\t<type>spot</type>\n";
  506. break;
  507. default:
  508. assert(0);
  509. break;
  510. }
  511. // <transform>
  512. file << "\t\t<transform>";
  513. for(uint32_t i = 0; i < 16; i++)
  514. {
  515. file << trf[i] << " ";
  516. }
  517. file << "</transform>\n";
  518. file << "\t</light>\n";
  519. }
  520. //==============================================================================
  521. static void exportModel(
  522. const aiScene& scene,
  523. const aiNode& node)
  524. {
  525. if(node.mNumMeshes == 0)
  526. {
  527. return;
  528. }
  529. std::string name = node.mName.C_Str();
  530. LOGI("Exporting model %s\n", name.c_str());
  531. std::fstream file;
  532. file.open(config.outDir + name + ".ankimdl", std::ios::out);
  533. file << xmlHeader << '\n';
  534. file << "<model>\n";
  535. file << "\t<modelPatches>\n";
  536. for(uint32_t i = 0; i < node.mNumMeshes; i++)
  537. {
  538. uint32_t meshIndex = node.mMeshes[i];
  539. const aiMesh& mesh = *scene.mMeshes[meshIndex];
  540. // start
  541. file << "\t\t<modelPatch>\n";
  542. // Write mesh
  543. file << "\t\t\t<mesh>" << config.rpath
  544. << mesh.mName.C_Str() << ".ankimesh</mesh>\n";
  545. // Write material
  546. const aiMaterial& mtl = *scene.mMaterials[mesh.mMaterialIndex];
  547. aiString mtlname;
  548. mtl.Get(AI_MATKEY_NAME, mtlname);
  549. file << "\t\t\t<material>" << config.rpath
  550. << mtlname.C_Str() << ".ankimtl</material>\n";
  551. // end
  552. file << "\t\t</modelPatch>\n";
  553. }
  554. file << "\t</modelPatches>\n";
  555. file << "</model>\n";
  556. }
  557. //==============================================================================
  558. static void exportAnimation(
  559. const aiAnimation& anim,
  560. uint32_t index,
  561. const aiScene& scene)
  562. {
  563. // Get name
  564. std::string name = anim.mName.C_Str();
  565. if(name.size() == 0)
  566. {
  567. name = std::string("animation_") + std::to_string(index);
  568. }
  569. // Find if it's skeleton animation
  570. /*bool isSkeletalAnimation = false;
  571. for(uint32_t i = 0; i < scene.mNumMeshes; i++)
  572. {
  573. const aiMesh& mesh = *scene.mMeshes[i];
  574. if(mesh.HasBones())
  575. {
  576. }
  577. }*/
  578. std::fstream file;
  579. LOGI("Exporting animation %s\n", name.c_str());
  580. file.open(config.outDir + name + ".ankianim", std::ios::out);
  581. file << xmlHeader << "\n";
  582. file << "<animation>\n";
  583. file << "\t<duration>" << anim.mDuration << "</duration>\n";
  584. file << "\t<channels>\n";
  585. for(uint32_t i = 0; i < anim.mNumChannels; i++)
  586. {
  587. const aiNodeAnim& nAnim = *anim.mChannels[i];
  588. file << "\t\t<channel>\n";
  589. // Name
  590. file << "\t\t\t<name>" << nAnim.mNodeName.C_Str() << "</name>\n";
  591. // Positions
  592. file << "\t\t\t<positionKeys>\n";
  593. for(uint32_t j = 0; j < nAnim.mNumPositionKeys; j++)
  594. {
  595. const aiVectorKey& key = nAnim.mPositionKeys[j];
  596. if(config.flipyz)
  597. {
  598. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  599. << "<value>" << key.mValue[0] << " "
  600. << key.mValue[2] << " " << -key.mValue[1]
  601. << "</value></key>\n";
  602. }
  603. else
  604. {
  605. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  606. << "<value>" << key.mValue[0] << " "
  607. << key.mValue[1] << " " << key.mValue[2]
  608. << "</value></key>\n";
  609. }
  610. }
  611. file << "\t\t\t</positionKeys>\n";
  612. // Rotations
  613. file << "\t\t\t<rotationKeys>\n";
  614. for(uint32_t j = 0; j < nAnim.mNumRotationKeys; j++)
  615. {
  616. const aiQuatKey& key = nAnim.mRotationKeys[j];
  617. aiMatrix3x3 mat = toAnkiMatrix(key.mValue.GetMatrix());
  618. aiQuaternion quat(mat);
  619. //aiQuaternion quat(key.mValue);
  620. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  621. << "<value>" << quat.x << " " << quat.y
  622. << " " << quat.z << " "
  623. << quat.w << "</value></key>\n";
  624. }
  625. file << "\t\t\t</rotationKeys>\n";
  626. // Scale
  627. file << "\t\t\t<scalingKeys>\n";
  628. for(uint32_t j = 0; j < nAnim.mNumScalingKeys; j++)
  629. {
  630. const aiVectorKey& key = nAnim.mScalingKeys[j];
  631. // Note: only uniform scale
  632. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  633. << "<value>"
  634. << ((key.mValue[0] + key.mValue[1] + key.mValue[2]) / 3.0)
  635. << "</value></key>\n";
  636. }
  637. file << "\t\t\t</scalingKeys>\n";
  638. file << "\t\t</channel>\n";
  639. }
  640. file << "\t</channels>\n";
  641. file << "</animation>\n";
  642. }
  643. //==============================================================================
  644. static void visitNode(const aiNode* node, const aiScene& scene)
  645. {
  646. if(node == nullptr)
  647. {
  648. return;
  649. }
  650. // For every mesh of this node
  651. for(uint32_t i = 0; i < node->mNumMeshes; i++)
  652. {
  653. uint32_t meshIndex = node->mMeshes[i];
  654. const aiMesh& mesh = *scene.mMeshes[meshIndex];
  655. // Is material set?
  656. if(config.meshes[meshIndex].mtlIndex == 0xFFFFFFFF)
  657. {
  658. // Connect mesh with material
  659. config.meshes[meshIndex].mtlIndex = mesh.mMaterialIndex;
  660. // Connect material with mesh
  661. config.materials[mesh.mMaterialIndex].meshIndices.push_back(
  662. meshIndex);
  663. }
  664. else if(config.meshes[meshIndex].mtlIndex != mesh.mMaterialIndex)
  665. {
  666. ERROR("Previous material index conflict\n");
  667. }
  668. config.meshes[meshIndex].transforms.push_back(node->mTransformation);
  669. }
  670. // Go to children
  671. for(uint32_t i = 0; i < node->mNumChildren; i++)
  672. {
  673. visitNode(node->mChildren[i], scene);
  674. }
  675. }
  676. //==============================================================================
  677. static void exportScene(const aiScene& scene)
  678. {
  679. LOGI("Exporting scene to %s\n", config.outDir.c_str());
  680. //
  681. // Open scene file
  682. //
  683. std::ofstream file;
  684. file.open(config.outDir + "master.ankiscene");
  685. file << xmlHeader << "\n"
  686. << "<scene>\n";
  687. //
  688. // Get all the data
  689. //
  690. config.meshes.resize(scene.mNumMeshes);
  691. config.materials.resize(scene.mNumMaterials);
  692. int i = 0;
  693. for(Mesh& mesh : config.meshes)
  694. {
  695. mesh.index = i++;
  696. }
  697. i = 0;
  698. for(Material& mtl : config.materials)
  699. {
  700. mtl.index = i++;
  701. }
  702. const aiNode* node = scene.mRootNode;
  703. visitNode(node, scene);
  704. #if 0
  705. //
  706. // Export non instanced static meshes
  707. //
  708. for(uint32_t i = 0; i < config.meshes.size(); i++)
  709. {
  710. // Check if instance is one
  711. if(config.meshes[i].transforms.size() == 1)
  712. {
  713. continue;
  714. }
  715. // Export the material
  716. aiMaterial& aimtl = *scene.mMaterials[mesh.mtlIndex];
  717. std::string mtlName = getMaterialName(aimtl);
  718. exportMaterial(scene, aimtl, false, &mtlName);
  719. // Export mesh
  720. std::string meshName = std::string(scene.mMeshes[i]->mName.C_Str())
  721. + "_static_" + std::to_string(i);
  722. exportMesh(*scene.mMeshes[i], &meshName, nullptr);
  723. for(uint32_t t = 0; t < config.meshes[i].transforms.size(); t++)
  724. {
  725. std::string nname = name + "_" + std::to_string(t);
  726. exportMesh(*scene.mMeshes[i], &nname,
  727. &config.meshes[i].transforms[t], config);
  728. }
  729. }
  730. #endif
  731. //
  732. // Write the instanced meshes
  733. //
  734. for(uint32_t i = 0; i < config.meshes.size(); i++)
  735. {
  736. const Mesh& mesh = config.meshes[i];
  737. // Skip meshes that are not instance candidates
  738. if(mesh.transforms.size() == 0)
  739. {
  740. continue;
  741. }
  742. // Export the material
  743. aiMaterial& aimtl = *scene.mMaterials[mesh.mtlIndex];
  744. std::string mtlName = getMaterialName(aimtl) + "_instanced";
  745. exportMaterial(scene, aimtl, true, &mtlName);
  746. // Export mesh
  747. std::string meshName = std::string(scene.mMeshes[i]->mName.C_Str())
  748. + "_instanced_" + std::to_string(i);
  749. exportMesh(*scene.mMeshes[i], &meshName, nullptr);
  750. // Write model file
  751. std::string modelName = mtlName + "_" + std::to_string(i);
  752. {
  753. std::ofstream file;
  754. file.open(
  755. config.outDir + modelName + ".ankimdl");
  756. file << xmlHeader << "\n"
  757. << "<model>\n"
  758. << "\t<modelPatches>\n"
  759. << "\t\t<modelPatch>\n"
  760. << "\t\t\t<mesh>" << config.rpath << meshName
  761. << ".ankimesh</mesh>\n"
  762. << "\t\t\t<material>" << config.rpath << mtlName
  763. << ".ankimtl</material>\n"
  764. << "\t\t</modelPatch>\n"
  765. << "\t</modelPatches>\n"
  766. << "</model>\n";
  767. }
  768. // Node name
  769. std::string nodeName = getMaterialName(aimtl) + "_instanced_"
  770. + std::to_string(i);
  771. // Write the scene file
  772. file << "\t<modelNode>\n"
  773. << "\t\t<name>" << nodeName << "</name>\n"
  774. << "\t\t<model>" << config.rpath << modelName
  775. << ".ankimdl</model>\n"
  776. << "\t\t<instancesCount>"
  777. << mesh.transforms.size() << "</instancesCount>\n";
  778. for(uint32_t j = 0; j < mesh.transforms.size(); j++)
  779. {
  780. file << "\t\t<transform>";
  781. aiMatrix4x4 trf = toAnkiMatrix(mesh.transforms[j]);
  782. for(uint32_t a = 0; a < 4; a++)
  783. {
  784. for(uint32_t b = 0; b < 4; b++)
  785. {
  786. file << trf[a][b] << " ";
  787. }
  788. }
  789. file << "</transform>\n";
  790. }
  791. file << "\t</modelNode>\n";
  792. }
  793. #if 0
  794. // Write bmeshes
  795. for(uint32_t mtlId = 0; mtlId < config.materials.size(); mtlId++)
  796. {
  797. const Material& mtl = config.materials[mtlId];
  798. // Check if used
  799. if(mtl.meshIndices.size() < 1)
  800. {
  801. continue;
  802. }
  803. std::string name = getMaterialName(*scene.mMaterials[mtlId]) + ".bmesh";
  804. std::fstream file;
  805. file.open(config.outDir + name, std::ios::out);
  806. file << xmlHeader << "\n";
  807. file << "<bucketMesh>\n";
  808. file << "\t<meshes>\n";
  809. for(uint32_t j = 0; j < mtl.meshIndices.size(); j++)
  810. {
  811. uint32_t meshId = mtl.meshIndices[j];
  812. const Mesh& mesh = config.meshes[meshId];
  813. for(uint32_t k = 0; k < mesh.transforms.size(); k++)
  814. {
  815. file << "\t\t<mesh>" << config.rpath
  816. << "mesh_" + std::to_string(meshId) << "_"
  817. << std::to_string(k)
  818. << ".mesh</mesh>\n";
  819. }
  820. }
  821. file << "\t</meshes>\n";
  822. file << "</bucketMesh>\n";
  823. }
  824. // Create the master model
  825. std::fstream file;
  826. file.open(config.outDir + "static_geometry.mdl", std::ios::out);
  827. file << xmlHeader << "\n";
  828. file << "<model>\n";
  829. file << "\t<modelPatches>\n";
  830. for(uint32_t i = 0; i < config.materials.size(); i++)
  831. {
  832. // Check if used
  833. if(config.materials[i].meshIndices.size() < 1)
  834. {
  835. continue;
  836. }
  837. file << "\t\t<modelPatch>\n";
  838. file << "\t\t\t<bucketMesh>" << config.rpath
  839. << getMaterialName(*scene.mMaterials[i]) << ".bmesh</bucketMesh>\n";
  840. file << "\t\t\t<material>" << config.rpath
  841. << getMaterialName(*scene.mMaterials[i])
  842. << ".mtl</material>\n";
  843. file << "\t\t</modelPatch>\n";
  844. }
  845. file << "\t</modelPatches>\n";
  846. file << "</model>\n";
  847. #endif
  848. //
  849. // Animations
  850. //
  851. for(unsigned i = 0; i < scene.mNumAnimations; i++)
  852. {
  853. exportAnimation(*scene.mAnimations[i], i, scene);
  854. }
  855. file << "</scene>\n";
  856. LOGI("Done exporting scene!\n");
  857. }
  858. //==============================================================================
  859. int main(int argc, char** argv)
  860. {
  861. try
  862. {
  863. parseConfig(argc, argv);
  864. // Load
  865. Assimp::Importer importer;
  866. const aiScene& scene = load(config.inputFname, importer);
  867. // Export
  868. exportScene(scene);
  869. }
  870. catch(std::exception& e)
  871. {
  872. std::cerr << "Exception: " << e.what() << std::endl;
  873. }
  874. }