Main.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  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
  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");
  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");
  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 exportMaterial(const aiMaterial& mtl, const Config& config,
  250. uint32_t id)
  251. {
  252. std::string diffTex;
  253. std::string normTex;
  254. // Find the name
  255. aiString ainame;
  256. std::string name;
  257. if(mtl.Get(AI_MATKEY_NAME, ainame) == AI_SUCCESS)
  258. {
  259. name = ainame.C_Str();
  260. }
  261. else
  262. {
  263. ERROR("Material's name is missing\n");
  264. }
  265. LOGI("Exporting material %s\n", name.c_str());
  266. // Diffuse texture
  267. if(mtl.GetTextureCount(aiTextureType_DIFFUSE) < 1)
  268. {
  269. ERROR("Material has no diffuse textures");
  270. }
  271. aiString path;
  272. if(mtl.GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS)
  273. {
  274. diffTex = path.C_Str();
  275. }
  276. else
  277. {
  278. ERROR("Failed to retrieve texture\n");
  279. }
  280. // Normal texture
  281. if(mtl.GetTextureCount(aiTextureType_NORMALS) > 0)
  282. {
  283. if(mtl.GetTexture(aiTextureType_NORMALS, 0, &path) == AI_SUCCESS)
  284. {
  285. normTex = path.C_Str();
  286. }
  287. else
  288. {
  289. ERROR("Failed to retrieve texture\n");
  290. }
  291. }
  292. // Write file
  293. static const char* diffMtlStr =
  294. #include "diffTemplateMtl.h"
  295. ;
  296. static const char* diffNormMtlStr =
  297. #include "diffNormTemplateMtl.h"
  298. ;
  299. std::fstream file;
  300. file.open(config.outDir + name + ".mtl", std::ios::out);
  301. // Chose the correct template
  302. if(normTex.size() == 0)
  303. {
  304. file << replaceAllString(diffMtlStr, "%diffuseMap%",
  305. config.texturesAppend + diffTex);
  306. }
  307. else
  308. {
  309. std::string str;
  310. str = replaceAllString(diffNormMtlStr, "%diffuseMap%",
  311. config.texturesAppend + diffTex);
  312. str = replaceAllString(str, "%normalMap%",
  313. config.texturesAppend + normTex);
  314. file << str;
  315. }
  316. }
  317. //==============================================================================
  318. void exportLight(const aiLight& light, const Config& config, std::fstream& file)
  319. {
  320. if(light.mType != aiLightSource_POINT || light.mType != aiLightSource_SPOT)
  321. {
  322. LOGW("Skipping light %s. Unsupported type\n", light.mName.C_Str());
  323. return;
  324. }
  325. file << "\t<light>\n";
  326. file << "\t\t<name>" << light.mName.C_Str() << "</name>\n";
  327. file << "\t\t<diffuseColor>"
  328. << light.mColorDiffuse[0] << " "
  329. << light.mColorDiffuse[1] << " "
  330. << light.mColorDiffuse[2] << " "
  331. << light.mColorDiffuse[3]
  332. << "</diffuseColor>\n";
  333. file << "\t\t<specularColor>"
  334. << light.mColorSpecular[0] << " "
  335. << light.mColorSpecular[1] << " "
  336. << light.mColorSpecular[2] << " "
  337. << light.mColorSpecular[3]
  338. << "</specularColor>\n";
  339. aiMatrix4x4 trf;
  340. aiMatrix4x4::Translation(light.mPosition, trf);
  341. switch(light.mType)
  342. {
  343. case aiLightSource_POINT:
  344. {
  345. file << "\t\t<type>point</type>\n";
  346. // At this point I want the radius and have the attenuation factors
  347. // att = Ac + Al*d + Aq*d^2. When d = r then att = 0.0. Also if we
  348. // assume that Ac is 0 then:
  349. // 0 = Al*r + Aq*r^2. Solving by r is easy
  350. float r = -light.mAttenuationLinear / light.mAttenuationQuadratic;
  351. file << "\t\t<radius>" << r << "</radius>\n";
  352. break;
  353. }
  354. case aiLightSource_SPOT:
  355. file << "\t\t<type>spot</type>\n";
  356. break;
  357. default:
  358. assert(0);
  359. break;
  360. }
  361. // <transform>
  362. file << "\t\t<transform>";
  363. for(uint32_t i = 0; i < 16; i++)
  364. {
  365. file << trf[i] << " ";
  366. }
  367. file << "</transform>\n";
  368. file << "\t</light>\n";
  369. }
  370. //==============================================================================
  371. void exportModel(const aiScene& scene, const aiNode& node, const Config& config)
  372. {
  373. if(node.mNumMeshes == 0)
  374. {
  375. return;
  376. }
  377. std::string name = node.mName.C_Str();
  378. LOGI("Exporting model %s\n", name.c_str());
  379. std::fstream file;
  380. file.open(config.outDir + name + ".mdl", std::ios::out);
  381. file << xmlHeader << '\n';
  382. file << "<model>\n";
  383. file << "\t<modelPatches>\n";
  384. for(uint32_t i = 0; i < node.mNumMeshes; i++)
  385. {
  386. uint32_t meshIndex = node.mMeshes[i];
  387. const aiMesh& mesh = *scene.mMeshes[meshIndex];
  388. // start
  389. file << "\t\t<modelPatch>\n";
  390. // Write mesh
  391. file << "\t\t\t<mesh>" << config.rpath
  392. << mesh.mName.C_Str() << ".mesh</mesh>\n";
  393. // Write material
  394. const aiMaterial& mtl = *scene.mMaterials[mesh.mMaterialIndex];
  395. aiString mtlname;
  396. mtl.Get(AI_MATKEY_NAME, mtlname);
  397. file << "\t\t\t<material>" << config.rpath
  398. << mtlname.C_Str() << ".mtl</material>\n";
  399. // end
  400. file << "\t\t</modelPatch>\n";
  401. }
  402. file << "\t</modelPatches>\n";
  403. file << "</model>\n";
  404. }
  405. //==============================================================================
  406. void exportNode(const aiScene& scene, const aiNode* node, const Config& config)
  407. {
  408. if(node == nullptr)
  409. {
  410. return;
  411. }
  412. // Write the .mdl
  413. exportModel(scene, *node, config);
  414. // Go to children
  415. for(uint32_t i = 0; i < node->mNumChildren; i++)
  416. {
  417. exportNode(scene, node->mChildren[i], config);
  418. }
  419. }
  420. //==============================================================================
  421. void exportScene(const aiScene& scene, const Config& config)
  422. {
  423. LOGI("Exporting scene to %s\n", config.outDir.c_str());
  424. // Meshes
  425. for(uint32_t i = 0; i < scene.mNumMeshes; i++)
  426. {
  427. exportMesh(*scene.mMeshes[i], config);
  428. }
  429. // Materials
  430. for(uint32_t i = 0; i < scene.mNumMaterials; i++)
  431. {
  432. exportMaterial(*scene.mMaterials[i], config, i);
  433. }
  434. // The nodes
  435. exportNode(scene, scene.mRootNode, config);
  436. LOGI("Done exporting scene!\n");
  437. }
  438. //==============================================================================
  439. int main(int argc, char** argv)
  440. {
  441. try
  442. {
  443. Config config;
  444. parseConfig(argc, argv, config);
  445. // Load
  446. Assimp::Importer importer;
  447. const aiScene& scene = load(config.inputFname, importer);
  448. // Export
  449. exportScene(scene, config);
  450. }
  451. catch(std::exception& e)
  452. {
  453. std::cerr << "Exception: " << e.what() << std::endl;
  454. }
  455. }