Main.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  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. //==============================================================================
  13. // Log and errors
  14. #define STR(s) #s
  15. #define XSTR(s) STR(s)
  16. #define LOGI(...) \
  17. printf("[I] (" __FILE__ ":" XSTR(__LINE__) ") " __VA_ARGS__)
  18. #define ERROR(...) \
  19. do { \
  20. fprintf(stderr, "[E] (" __FILE__ ":" XSTR(__LINE__) ") " __VA_ARGS__); \
  21. exit(0); \
  22. } while(0)
  23. #define LOGW(...) \
  24. fprintf(stderr, "[W] (" __FILE__ ":" XSTR(__LINE__) ") " __VA_ARGS__)
  25. //==============================================================================
  26. std::string replaceAllString(const std::string& str, const std::string& from,
  27. const std::string& to)
  28. {
  29. if(from.empty())
  30. {
  31. return str;
  32. }
  33. std::string out = str;
  34. size_t start_pos = 0;
  35. while((start_pos = out.find(from, start_pos)) != std::string::npos)
  36. {
  37. out.replace(start_pos, from.length(), to);
  38. start_pos += to.length();
  39. }
  40. return out;
  41. }
  42. //==============================================================================
  43. struct Config
  44. {
  45. std::string inputFname;
  46. std::string outDir;
  47. std::string texturesAppend;
  48. std::string rpath;
  49. bool flipyz = false;
  50. };
  51. //==============================================================================
  52. void parseConfig(int argc, char** argv, Config& config)
  53. {
  54. static const char* usage = R"(Usage: 2anki in_file out_dir [options]
  55. Options:
  56. -texpath <string> : Append a string to the paths of textures
  57. -rpath <string> : Append a string to the meshes and materials
  58. -flipyz : Flip y with z (For blender exports)
  59. )";
  60. // Parse config
  61. if(argc < 3)
  62. {
  63. goto error;
  64. }
  65. config.inputFname = argv[1];
  66. config.outDir = argv[2] + std::string("/");
  67. for(int i = 3; i < argc; i++)
  68. {
  69. if(strcmp(argv[i], "-texpath") == 0)
  70. {
  71. ++i;
  72. if(i < argc)
  73. {
  74. config.texturesAppend = argv[i] + std::string("/");
  75. }
  76. else
  77. {
  78. goto error;
  79. }
  80. }
  81. else if(strcmp(argv[i], "-rpath") == 0)
  82. {
  83. ++i;
  84. if(i < argc)
  85. {
  86. config.rpath = argv[i] + std::string("/");
  87. }
  88. else
  89. {
  90. goto error;
  91. }
  92. }
  93. else if(strcmp(argv[i], "-flipyz") == 0)
  94. {
  95. config.flipyz = true;
  96. }
  97. else
  98. {
  99. goto error;
  100. }
  101. }
  102. return;
  103. error:
  104. printf("%s", usage);
  105. exit(0);
  106. }
  107. //==============================================================================
  108. /// Load the scene
  109. const aiScene& load(const std::string& filename, Assimp::Importer& importer)
  110. {
  111. LOGI("Loading file %s\n", filename.c_str());
  112. const aiScene* scene = importer.ReadFile(filename,
  113. aiProcess_Triangulate
  114. | aiProcess_JoinIdenticalVertices
  115. | aiProcess_SortByPType
  116. | aiProcess_ImproveCacheLocality
  117. | aiProcess_OptimizeMeshes
  118. //| aiProcess_CalcTangentSpace
  119. );
  120. if(!scene)
  121. {
  122. ERROR("%s\n", importer.GetErrorString());
  123. }
  124. LOGI("File loaded successfully!\n");
  125. return *scene;
  126. }
  127. //==============================================================================
  128. static const uint32_t MAX_BONES_PER_VERTEX = 4;
  129. /// Bone/weight info per vertex
  130. struct Vw
  131. {
  132. uint32_t boneIds[MAX_BONES_PER_VERTEX];
  133. float weigths[MAX_BONES_PER_VERTEX];
  134. uint32_t bonesCount;
  135. };
  136. //==============================================================================
  137. void exportMesh(const aiMesh& mesh, const Config& config)
  138. {
  139. std::string name = mesh.mName.C_Str();
  140. std::fstream file;
  141. LOGI("Exporting mesh %s\n", name.c_str());
  142. uint32_t vertsCount = mesh.mNumVertices;
  143. // Open file
  144. file.open(config.outDir + name + ".mesh",
  145. std::ios::out | std::ios::binary);
  146. // Write magic word
  147. file.write("ANKIMESH", 8);
  148. // Write the name
  149. uint32_t size = name.size();
  150. file.write((char*)&size, sizeof(uint32_t));
  151. file.write(&name[0], size);
  152. // Write positions
  153. file.write((char*)&vertsCount, sizeof(uint32_t));
  154. for(uint32_t i = 0; i < mesh.mNumVertices; i++)
  155. {
  156. aiVector3D pos = mesh.mVertices[i];
  157. // flip
  158. if(config.flipyz)
  159. {
  160. float tmp = pos[1];
  161. pos[1] = pos[2];
  162. pos[2] = -tmp;
  163. }
  164. for(uint32_t j = 0; j < 3; j++)
  165. {
  166. file.write((char*)&pos[j], sizeof(float));
  167. }
  168. }
  169. // Write the indices
  170. file.write((char*)&mesh.mNumFaces, sizeof(uint32_t));
  171. for(uint32_t i = 0; i < mesh.mNumFaces; i++)
  172. {
  173. const aiFace& face = mesh.mFaces[i];
  174. if(face.mNumIndices != 3)
  175. {
  176. ERROR("For some reason the assimp didn't triangulate\n");
  177. }
  178. for(uint32_t j = 0; j < 3; j++)
  179. {
  180. uint32_t index = face.mIndices[j];
  181. file.write((char*)&index, sizeof(uint32_t));
  182. }
  183. }
  184. // Write the tex coords
  185. file.write((char*)&vertsCount, sizeof(uint32_t));
  186. // For all channels
  187. for(uint32_t ch = 0; ch < mesh.GetNumUVChannels(); ch++)
  188. {
  189. if(mesh.mNumUVComponents[ch] != 2)
  190. {
  191. ERROR("Incorrect number of UV components\n");
  192. }
  193. // For all tex coords of this channel
  194. for(uint32_t i = 0; i < vertsCount; i++)
  195. {
  196. aiVector3D texCoord = mesh.mTextureCoords[ch][i];
  197. for(uint32_t j = 0; j < 2; j++)
  198. {
  199. file.write((char*)&texCoord[j], sizeof(float));
  200. }
  201. }
  202. }
  203. // Write bone weigths count
  204. if(mesh.HasBones())
  205. {
  206. #if 0
  207. // Write file
  208. file.write((char*)&vertsCount, sizeof(uint32_t));
  209. // Gather info for each vertex
  210. std::vector<Vw> vw;
  211. vw.resize(vertsCount);
  212. memset(&vw[0], 0, sizeof(Vw) * vertsCount);
  213. // For all bones
  214. for(uint32_t i = 0; i < mesh.mNumBones; i++)
  215. {
  216. const aiBone& bone = *mesh.mBones[i];
  217. // for every weights of the bone
  218. for(uint32_t j = 0; j < bone.mWeightsCount; j++)
  219. {
  220. const aiVertexWeight& weigth = bone.mWeights[j];
  221. // Sanity check
  222. if(weight.mVertexId >= vertCount)
  223. {
  224. ERROR("Out of bounds vert ID");
  225. }
  226. Vm& a = vm[weight.mVertexId];
  227. // Check out of bounds
  228. if(a.bonesCount >= MAX_BONES_PER_VERTEX)
  229. {
  230. LOGW("Too many bones for vertex %d\n", weigth.mVertexId);
  231. continue;
  232. }
  233. // Write to vertex
  234. a.boneIds[a.bonesCount] = i;
  235. a.weigths[a.bonesCount] = weigth.mWeigth;
  236. ++a.bonesCount;
  237. }
  238. // Now write the file
  239. }
  240. #endif
  241. }
  242. else
  243. {
  244. uint32_t num = 0;
  245. file.write((char*)&num, sizeof(uint32_t));
  246. }
  247. }
  248. //==============================================================================
  249. void exportSkeleton(const aiMesh& mesh, const Config& config)
  250. {
  251. assert(mesh.HasBones());
  252. std::string name = mesh.mName.C_Str();
  253. std::fstream file;
  254. LOGI("Exporting skeleton %s\n", name.c_str());
  255. // Open file
  256. file.open(config.outDir + name + ".skel", std::ios::out);
  257. file << xmlHeader << "\n";
  258. file << "<skeleton>\n";
  259. file << "\t<bones>\n";
  260. bool rootBoneFound = false;
  261. for(uint32_t i = 0; i < mesh.mNumBones; i++)
  262. {
  263. const aiBone& bone = *mesh.mBones[i];
  264. file << "\t\t<bone>\n";
  265. // <name>
  266. file << "\t\t\t<name>" << bone.mName.C_Str() << "</name>\n";
  267. if(strcmp(bone.mName.C_Str(), "root") == 0)
  268. {
  269. rootBoneFound = true;
  270. }
  271. // <transform>
  272. file << "\t\t\t<transform>";
  273. for(uint32_t j = 0; j < 16; j++)
  274. {
  275. file << bone.mOffsetMatrix[j] << " ";
  276. }
  277. file << "</transform>\n";
  278. file << "\t\t</bone>\n";
  279. }
  280. if(!rootBoneFound)
  281. {
  282. ERROR("There should be one bone named \"root\"\n");
  283. }
  284. file << "\t</bones>\n";
  285. file << "</skeleton>\n";
  286. }
  287. //==============================================================================
  288. void exportMaterial(const aiMaterial& mtl, const Config& config)
  289. {
  290. std::string diffTex;
  291. std::string normTex;
  292. // Find the name
  293. aiString ainame;
  294. std::string name;
  295. if(mtl.Get(AI_MATKEY_NAME, ainame) == AI_SUCCESS)
  296. {
  297. name = ainame.C_Str();
  298. }
  299. else
  300. {
  301. ERROR("Material's name is missing\n");
  302. }
  303. LOGI("Exporting material %s\n", name.c_str());
  304. // Diffuse texture
  305. if(mtl.GetTextureCount(aiTextureType_DIFFUSE) < 1)
  306. {
  307. ERROR("Material has no diffuse textures\n");
  308. }
  309. aiString path;
  310. if(mtl.GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS)
  311. {
  312. diffTex = path.C_Str();
  313. }
  314. else
  315. {
  316. ERROR("Failed to retrieve texture\n");
  317. }
  318. // Normal texture
  319. if(mtl.GetTextureCount(aiTextureType_NORMALS) > 0)
  320. {
  321. if(mtl.GetTexture(aiTextureType_NORMALS, 0, &path) == AI_SUCCESS)
  322. {
  323. normTex = path.C_Str();
  324. }
  325. else
  326. {
  327. ERROR("Failed to retrieve texture\n");
  328. }
  329. }
  330. // Write file
  331. static const char* diffMtlStr =
  332. #include "diffTemplateMtl.h"
  333. ;
  334. static const char* diffNormMtlStr =
  335. #include "diffNormTemplateMtl.h"
  336. ;
  337. std::fstream file;
  338. file.open(config.outDir + name + ".mtl", std::ios::out);
  339. // Chose the correct template
  340. if(normTex.size() == 0)
  341. {
  342. file << replaceAllString(diffMtlStr, "%diffuseMap%",
  343. config.texturesAppend + diffTex);
  344. }
  345. else
  346. {
  347. std::string str;
  348. str = replaceAllString(diffNormMtlStr, "%diffuseMap%",
  349. config.texturesAppend + diffTex);
  350. str = replaceAllString(str, "%normalMap%",
  351. config.texturesAppend + normTex);
  352. file << str;
  353. }
  354. }
  355. //==============================================================================
  356. void exportLight(const aiLight& light, const Config& config, std::fstream& file)
  357. {
  358. if(light.mType != aiLightSource_POINT || light.mType != aiLightSource_SPOT)
  359. {
  360. LOGW("Skipping light %s. Unsupported type\n", light.mName.C_Str());
  361. return;
  362. }
  363. file << "\t<light>\n";
  364. file << "\t\t<name>" << light.mName.C_Str() << "</name>\n";
  365. file << "\t\t<diffuseColor>"
  366. << light.mColorDiffuse[0] << " "
  367. << light.mColorDiffuse[1] << " "
  368. << light.mColorDiffuse[2] << " "
  369. << light.mColorDiffuse[3]
  370. << "</diffuseColor>\n";
  371. file << "\t\t<specularColor>"
  372. << light.mColorSpecular[0] << " "
  373. << light.mColorSpecular[1] << " "
  374. << light.mColorSpecular[2] << " "
  375. << light.mColorSpecular[3]
  376. << "</specularColor>\n";
  377. aiMatrix4x4 trf;
  378. aiMatrix4x4::Translation(light.mPosition, trf);
  379. switch(light.mType)
  380. {
  381. case aiLightSource_POINT:
  382. {
  383. file << "\t\t<type>point</type>\n";
  384. // At this point I want the radius and have the attenuation factors
  385. // att = Ac + Al*d + Aq*d^2. When d = r then att = 0.0. Also if we
  386. // assume that Ac is 0 then:
  387. // 0 = Al*r + Aq*r^2. Solving by r is easy
  388. float r = -light.mAttenuationLinear / light.mAttenuationQuadratic;
  389. file << "\t\t<radius>" << r << "</radius>\n";
  390. break;
  391. }
  392. case aiLightSource_SPOT:
  393. file << "\t\t<type>spot</type>\n";
  394. break;
  395. default:
  396. assert(0);
  397. break;
  398. }
  399. // <transform>
  400. file << "\t\t<transform>";
  401. for(uint32_t i = 0; i < 16; i++)
  402. {
  403. file << trf[i] << " ";
  404. }
  405. file << "</transform>\n";
  406. file << "\t</light>\n";
  407. }
  408. //==============================================================================
  409. void exportModel(const aiScene& scene, const aiNode& node, const Config& config)
  410. {
  411. if(node.mNumMeshes == 0)
  412. {
  413. return;
  414. }
  415. std::string name = node.mName.C_Str();
  416. LOGI("Exporting model %s\n", name.c_str());
  417. std::fstream file;
  418. file.open(config.outDir + name + ".mdl", std::ios::out);
  419. file << xmlHeader << '\n';
  420. file << "<model>\n";
  421. file << "\t<modelPatches>\n";
  422. for(uint32_t i = 0; i < node.mNumMeshes; i++)
  423. {
  424. uint32_t meshIndex = node.mMeshes[i];
  425. const aiMesh& mesh = *scene.mMeshes[meshIndex];
  426. // start
  427. file << "\t\t<modelPatch>\n";
  428. // Write mesh
  429. file << "\t\t\t<mesh>" << config.rpath
  430. << mesh.mName.C_Str() << ".mesh</mesh>\n";
  431. // Write material
  432. const aiMaterial& mtl = *scene.mMaterials[mesh.mMaterialIndex];
  433. aiString mtlname;
  434. mtl.Get(AI_MATKEY_NAME, mtlname);
  435. file << "\t\t\t<material>" << config.rpath
  436. << mtlname.C_Str() << ".mtl</material>\n";
  437. // end
  438. file << "\t\t</modelPatch>\n";
  439. }
  440. file << "\t</modelPatches>\n";
  441. file << "</model>\n";
  442. }
  443. //==============================================================================
  444. void exportAnimation(const aiAnimation& anim, uint32_t index,
  445. const aiScene& scene, const Config& config)
  446. {
  447. // Get name
  448. std::string name = anim.mName.C_Str();
  449. if(name.size() == 0)
  450. {
  451. name = std::string("animation_") + std::to_string(index);
  452. }
  453. // Find if it's skeleton animation
  454. /*bool isSkeletalAnimation = false;
  455. for(uint32_t i = 0; i < scene.mNumMeshes; i++)
  456. {
  457. const aiMesh& mesh = *scene.mMeshes[i];
  458. if(mesh.HasBones())
  459. {
  460. }
  461. }*/
  462. std::fstream file;
  463. LOGI("Exporting animation %s\n", name.c_str());
  464. file.open(config.outDir + name + ".anim", std::ios::out);
  465. file << xmlHeader << "\n";
  466. file << "<animation>\n";
  467. file << "\t<duration>" << anim.mDuration << "</duration>\n";
  468. file << "\t<channels>\n";
  469. for(uint32_t i = 0; i < anim.mNumChannels; i++)
  470. {
  471. const aiNodeAnim& nAnim = *anim.mChannels[i];
  472. file << "\t\t<channel>\n";
  473. // Name
  474. file << "\t\t\t<name>" << nAnim.mNodeName.C_Str() << "</name>\n";
  475. // Positions
  476. file << "\t\t\t<positionKeys>";
  477. for(uint32_t j = 0; j < nAnim.mNumPositionKeys; j++)
  478. {
  479. const aiVectorKey& key = nAnim.mPositionKeys[j];
  480. file << key.mTime << " " << key.mValue[0] << " "
  481. << key.mValue[1] << " " << key.mValue[2] << " ";
  482. }
  483. file << "</positionKeys>\n";
  484. // Rotations
  485. file << "\t\t\t<rotationKeys>";
  486. for(uint32_t j = 0; j < nAnim.mNumRotationKeys; j++)
  487. {
  488. const aiQuatKey& key = nAnim.mRotationKeys[j];
  489. file << key.mTime << " " << key.mValue.x << " "
  490. << key.mValue.y << " " << key.mValue.z << " "
  491. << key.mValue.w << " ";
  492. }
  493. file << "</rotationKeys>\n";
  494. file << "\t\t</channel>\n";
  495. }
  496. file << "\t</channels>\n";
  497. file << "</animation>\n";
  498. }
  499. //==============================================================================
  500. void exportNode(const aiScene& scene, const aiNode* node, const Config& config)
  501. {
  502. if(node == nullptr)
  503. {
  504. return;
  505. }
  506. // Write the .mdl
  507. exportModel(scene, *node, config);
  508. // Go to children
  509. for(uint32_t i = 0; i < node->mNumChildren; i++)
  510. {
  511. exportNode(scene, node->mChildren[i], config);
  512. }
  513. }
  514. //==============================================================================
  515. void exportScene(const aiScene& scene, const Config& config)
  516. {
  517. LOGI("Exporting scene to %s\n", config.outDir.c_str());
  518. // Meshes and skeletons
  519. for(uint32_t i = 0; i < scene.mNumMeshes; i++)
  520. {
  521. exportMesh(*scene.mMeshes[i], config);
  522. if(scene.mMeshes[i]->HasBones())
  523. {
  524. exportSkeleton(*scene.mMeshes[i], config);
  525. }
  526. }
  527. // Materials
  528. for(uint32_t i = 0; i < scene.mNumMaterials; i++)
  529. {
  530. exportMaterial(*scene.mMaterials[i], config);
  531. }
  532. // The nodes
  533. exportNode(scene, scene.mRootNode, config);
  534. // The animations
  535. for(uint32_t i = 0; i < scene.mNumAnimations; i++)
  536. {
  537. exportAnimation(*scene.mAnimations[i], i, scene, config);
  538. }
  539. LOGI("Done exporting scene!\n");
  540. }
  541. //==============================================================================
  542. int main(int argc, char** argv)
  543. {
  544. try
  545. {
  546. Config config;
  547. parseConfig(argc, argv, config);
  548. // Load
  549. Assimp::Importer importer;
  550. const aiScene& scene = load(config.inputFname, importer);
  551. // Export
  552. exportScene(scene, config);
  553. }
  554. catch(std::exception& e)
  555. {
  556. std::cerr << "Exception: " << e.what() << std::endl;
  557. }
  558. }