Main.cpp 16 KB

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