Exporter.cpp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321
  1. // Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include "Exporter.h"
  6. #include <iostream>
  7. //==============================================================================
  8. // Statics =
  9. //==============================================================================
  10. static const char* XML_HEADER = R"(<?xml version="1.0" encoding="UTF-8" ?>)";
  11. //==============================================================================
  12. static aiColor3D srgbToLinear(aiColor3D in)
  13. {
  14. const float p = 1.0 / 2.4;
  15. aiColor3D out;
  16. out[0] = pow(in[0], p);
  17. out[1] = pow(in[1], p);
  18. out[2] = pow(in[2], p);
  19. out[3] = in[3];
  20. return out;
  21. }
  22. //==============================================================================
  23. /// Convert from sRGB to linear and preserve energy
  24. static aiColor3D computeLightColor(aiColor3D in)
  25. {
  26. float energy = std::max(std::max(in[0], in[1]), in[2]);
  27. if(energy > 1.0)
  28. {
  29. in[0] /= energy;
  30. in[1] /= energy;
  31. in[2] /= energy;
  32. }
  33. else
  34. {
  35. energy = 1.0;
  36. }
  37. in = srgbToLinear(in);
  38. in[0] *= energy;
  39. in[1] *= energy;
  40. in[2] *= energy;
  41. return in;
  42. }
  43. //==============================================================================
  44. /// Round up the instances count.
  45. static uint32_t roundUpInstancesCount(uint32_t instances)
  46. {
  47. if(instances == 1)
  48. {
  49. instances = 1;
  50. }
  51. else if(instances <= 4)
  52. {
  53. instances = 4;
  54. }
  55. else if(instances <= 8)
  56. {
  57. instances = 8;
  58. }
  59. else if(instances <= 16)
  60. {
  61. instances = 16;
  62. }
  63. else if(instances <= 32)
  64. {
  65. instances = 32;
  66. }
  67. else
  68. {
  69. ERROR("Too many instances %u", instances);
  70. }
  71. return instances;
  72. }
  73. //==============================================================================
  74. static std::string getMaterialName(const aiMaterial& mtl, uint32_t instances)
  75. {
  76. aiString ainame;
  77. std::string name;
  78. if(mtl.Get(AI_MATKEY_NAME, ainame) == AI_SUCCESS)
  79. {
  80. name = ainame.C_Str();
  81. if(instances > 1)
  82. {
  83. name += "_inst" + std::to_string(roundUpInstancesCount(instances));
  84. }
  85. }
  86. else
  87. {
  88. ERROR("Material's name is missing");
  89. }
  90. return name;
  91. }
  92. //==============================================================================
  93. static std::string getMeshName(const aiMesh& mesh)
  94. {
  95. return std::string(mesh.mName.C_Str());
  96. }
  97. //==============================================================================
  98. /// Walk the node hierarchy and find the node.
  99. static const aiNode* findNodeWithName(
  100. const std::string& name,
  101. const aiNode* node)
  102. {
  103. if(node == nullptr || node->mName.C_Str() == name)
  104. {
  105. return node;
  106. }
  107. const aiNode* out = nullptr;
  108. // Go to children
  109. for(uint32_t i = 0; i < node->mNumChildren; i++)
  110. {
  111. out = findNodeWithName(name, node->mChildren[i]);
  112. if(out)
  113. {
  114. break;
  115. }
  116. }
  117. return out;
  118. }
  119. //==============================================================================
  120. static std::vector<std::string> tokenize(const std::string &source)
  121. {
  122. const char *delimiter = " ";
  123. bool keepEmpty = false;
  124. std::vector<std::string> results;
  125. size_t prev = 0;
  126. size_t next = 0;
  127. while((next = source.find_first_of(delimiter, prev)) != std::string::npos)
  128. {
  129. if(keepEmpty || (next - prev != 0))
  130. {
  131. results.push_back(source.substr(prev, next - prev));
  132. }
  133. prev = next + 1;
  134. }
  135. if(prev < source.size())
  136. {
  137. results.push_back(source.substr(prev));
  138. }
  139. return results;
  140. }
  141. //==============================================================================
  142. template<int N, typename Arr>
  143. static void stringToFloatArray(const std::string& in, Arr& out)
  144. {
  145. std::vector<std::string> tokens = tokenize(in);
  146. if(tokens.size() != N)
  147. {
  148. ERROR("Failed to parse %s", in.c_str());
  149. }
  150. int count = 0;
  151. for(const std::string& s : tokens)
  152. {
  153. out[count] = std::stof(s);
  154. ++count;
  155. }
  156. }
  157. //==============================================================================
  158. // Exporter =
  159. //==============================================================================
  160. //==============================================================================
  161. aiMatrix4x4 Exporter::toAnkiMatrix(const aiMatrix4x4& in) const
  162. {
  163. static const aiMatrix4x4 toLeftHanded(
  164. 1, 0, 0, 0,
  165. 0, 0, 1, 0,
  166. 0, -1, 0, 0,
  167. 0, 0, 0, 1);
  168. static const aiMatrix4x4 toLeftHandedInv(
  169. 1, 0, 0, 0,
  170. 0, 0, -1, 0,
  171. 0, 1, 0, 0,
  172. 0, 0, 0, 1);
  173. if(m_flipyz)
  174. {
  175. return toLeftHanded * in * toLeftHandedInv;
  176. }
  177. else
  178. {
  179. return in;
  180. }
  181. }
  182. //==============================================================================
  183. aiMatrix3x3 Exporter::toAnkiMatrix(const aiMatrix3x3& in) const
  184. {
  185. static const aiMatrix3x3 toLeftHanded(
  186. 1, 0, 0,
  187. 0, 0, 1,
  188. 0, -1, 0);
  189. static const aiMatrix3x3 toLeftHandedInv(
  190. 1, 0, 0,
  191. 0, 0, -1,
  192. 0, 1, 0);
  193. if(m_flipyz)
  194. {
  195. return toLeftHanded * in;
  196. }
  197. else
  198. {
  199. return in;
  200. }
  201. }
  202. //==============================================================================
  203. void Exporter::writeTransform(const aiMatrix4x4& mat)
  204. {
  205. std::ofstream& file = m_sceneFile;
  206. aiMatrix4x4 m = toAnkiMatrix(mat);
  207. float pos[3];
  208. pos[0] = m[0][3];
  209. pos[1] = m[1][3];
  210. pos[2] = m[2][3];
  211. file << "trf = Transform.new()\n";
  212. file << "trf:setOrigin(Vec4.new("
  213. << pos[0] << ", " << pos[1] << ", " << pos[2] << ", 0))\n";
  214. file << "rot = Mat3x4.new()\n";
  215. file << "rot:setAll(";
  216. for(unsigned j = 0; j < 3; j++)
  217. {
  218. for(unsigned i = 0; i < 4; i++)
  219. {
  220. if(i == 3)
  221. {
  222. file << "0";
  223. }
  224. else
  225. {
  226. file << m[j][i];
  227. }
  228. if(!(i == 3 && j == 2))
  229. {
  230. file << ", ";
  231. }
  232. }
  233. }
  234. file << ")\n";
  235. file << "trf:setRotation(rot)\n";
  236. file << "trf:setScale(1.0)\n";
  237. }
  238. //==============================================================================
  239. void Exporter::writeNodeTransform(
  240. const std::string& node,
  241. const aiMatrix4x4& mat)
  242. {
  243. std::ofstream& file = m_sceneFile;
  244. writeTransform(mat);
  245. file << node
  246. << ":getSceneNodeBase():getMoveComponent():setLocalTransform(trf)\n";
  247. }
  248. //==============================================================================
  249. const aiMesh& Exporter::getMeshAt(unsigned index) const
  250. {
  251. assert(index < m_scene->mNumMeshes);
  252. return *m_scene->mMeshes[index];
  253. }
  254. //==============================================================================
  255. const aiMaterial& Exporter::getMaterialAt(unsigned index) const
  256. {
  257. assert(index < m_scene->mNumMaterials);
  258. return *m_scene->mMaterials[index];
  259. }
  260. //==============================================================================
  261. std::string Exporter::getModelName(const Model& model) const
  262. {
  263. std::string name = getMeshName(getMeshAt(model.m_meshIndex));
  264. name += getMaterialName(
  265. getMaterialAt(model.m_materialIndex), model.m_instancesCount);
  266. return name;
  267. }
  268. //==============================================================================
  269. void Exporter::exportSkeleton(const aiMesh& mesh) const
  270. {
  271. assert(mesh.HasBones());
  272. std::string name = mesh.mName.C_Str();
  273. std::fstream file;
  274. LOGI("Exporting skeleton %s", name.c_str());
  275. // Open file
  276. file.open(m_outputDirectory + name + ".skel", std::ios::out);
  277. file << XML_HEADER << "\n";
  278. file << "<skeleton>\n";
  279. file << "\t<bones>\n";
  280. bool rootBoneFound = false;
  281. for(uint32_t i = 0; i < mesh.mNumBones; i++)
  282. {
  283. const aiBone& bone = *mesh.mBones[i];
  284. file << "\t\t<bone>\n";
  285. // <name>
  286. file << "\t\t\t<name>" << bone.mName.C_Str() << "</name>\n";
  287. if(strcmp(bone.mName.C_Str(), "root") == 0)
  288. {
  289. rootBoneFound = true;
  290. }
  291. // <transform>
  292. file << "\t\t\t<transform>";
  293. for(uint32_t j = 0; j < 16; j++)
  294. {
  295. file << bone.mOffsetMatrix[j] << " ";
  296. }
  297. file << "</transform>\n";
  298. file << "\t\t</bone>\n";
  299. }
  300. if(!rootBoneFound)
  301. {
  302. ERROR("There should be one bone named \"root\"");
  303. }
  304. file << "\t</bones>\n";
  305. file << "</skeleton>\n";
  306. }
  307. //==============================================================================
  308. void Exporter::exportMaterial(
  309. const aiMaterial& mtl,
  310. uint32_t instances) const
  311. {
  312. std::string diffTex;
  313. std::string normTex;
  314. std::string specColTex;
  315. std::string shininessTex;
  316. std::string dispTex;
  317. aiString path;
  318. std::string name = getMaterialName(mtl, instances);
  319. LOGI("Exporting material %s", name.c_str());
  320. // Diffuse texture
  321. if(mtl.GetTextureCount(aiTextureType_DIFFUSE) > 0)
  322. {
  323. if(mtl.GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS)
  324. {
  325. diffTex = getFilename(path.C_Str());
  326. }
  327. else
  328. {
  329. ERROR("Failed to retrieve texture");
  330. }
  331. }
  332. // Normal texture
  333. if(mtl.GetTextureCount(aiTextureType_NORMALS) > 0)
  334. {
  335. if(mtl.GetTexture(aiTextureType_NORMALS, 0, &path) == AI_SUCCESS)
  336. {
  337. normTex = getFilename(path.C_Str());
  338. }
  339. else
  340. {
  341. ERROR("Failed to retrieve texture");
  342. }
  343. }
  344. // Specular color
  345. if(mtl.GetTextureCount(aiTextureType_SPECULAR) > 0)
  346. {
  347. if(mtl.GetTexture(aiTextureType_SPECULAR, 0, &path) == AI_SUCCESS)
  348. {
  349. specColTex = getFilename(path.C_Str());
  350. }
  351. else
  352. {
  353. ERROR("Failed to retrieve texture");
  354. }
  355. }
  356. // Shininess color
  357. if(mtl.GetTextureCount(aiTextureType_SHININESS) > 0)
  358. {
  359. if(mtl.GetTexture(aiTextureType_SHININESS, 0, &path) == AI_SUCCESS)
  360. {
  361. shininessTex = getFilename(path.C_Str());
  362. }
  363. else
  364. {
  365. ERROR("Failed to retrieve texture");
  366. }
  367. }
  368. // Height texture
  369. if(mtl.GetTextureCount(aiTextureType_DISPLACEMENT) > 0)
  370. {
  371. if(mtl.GetTexture(aiTextureType_DISPLACEMENT, 0, &path) == AI_SUCCESS)
  372. {
  373. dispTex = getFilename(path.C_Str());
  374. }
  375. else
  376. {
  377. ERROR("Failed to retrieve texture");
  378. }
  379. }
  380. // Write file
  381. static const char* diffNormSpecFragTemplate =
  382. #include "templates/diffNormSpecFrag.h"
  383. ;
  384. static const char* simpleVertTemplate =
  385. #include "templates/simpleVert.h"
  386. ;
  387. static const char* tessVertTemplate =
  388. #include "templates/tessVert.h"
  389. ;
  390. static const char* readRgbFromTextureTemplate = R"(
  391. <operation>
  392. <id>%id%</id>
  393. <returnType>vec3</returnType>
  394. <function>readRgbFromTexture</function>
  395. <arguments>
  396. <argument>%map%</argument>
  397. <argument>out2</argument>
  398. </arguments>
  399. </operation>)";
  400. static const char* readRFromTextureTemplate = R"(
  401. <operation>
  402. <id>%id%</id>
  403. <returnType>float</returnType>
  404. <function>readRFromTexture</function>
  405. <arguments>
  406. <argument>%map%</argument>
  407. <argument>out2</argument>
  408. </arguments>
  409. </operation>)";
  410. // Compose full template
  411. // First geometry part
  412. std::string materialStr;
  413. materialStr = R"(<?xml version="1.0" encoding="UTF-8" ?>)";
  414. materialStr += "\n<material>\n\t<programs>\n";
  415. if(dispTex.empty())
  416. {
  417. materialStr += simpleVertTemplate;
  418. }
  419. else
  420. {
  421. materialStr += tessVertTemplate;
  422. }
  423. materialStr += "\n";
  424. // Then fragment part
  425. materialStr += diffNormSpecFragTemplate;
  426. materialStr += "\n\t</programs>\t</material>";
  427. // Replace strings
  428. if(!dispTex.empty())
  429. {
  430. materialStr = replaceAllString(materialStr, "%dispMap%",
  431. m_texrpath + dispTex);
  432. }
  433. // Diffuse
  434. if(!diffTex.empty())
  435. {
  436. materialStr = replaceAllString(materialStr, "%diffuseColorInput%",
  437. R"(<input><type>sampler2D</type><name>uDiffuseColor</name><value>)"
  438. + m_texrpath + diffTex
  439. + R"(</value></input>)");
  440. materialStr = replaceAllString(materialStr, "%diffuseColorFunc%",
  441. readRgbFromTextureTemplate);
  442. materialStr = replaceAllString(materialStr, "%id%",
  443. "10");
  444. materialStr = replaceAllString(materialStr, "%map%",
  445. "uDiffuseColor");
  446. materialStr = replaceAllString(materialStr, "%diffuseColorArg%",
  447. "out10");
  448. }
  449. else
  450. {
  451. aiColor3D diffCol = {0.0, 0.0, 0.0};
  452. mtl.Get(AI_MATKEY_COLOR_DIFFUSE, diffCol);
  453. materialStr = replaceAllString(materialStr, "%diffuseColorInput%",
  454. R"(<input><type>vec3</type><name>uDiffuseColor</name><value>)"
  455. + std::to_string(diffCol[0]) + " "
  456. + std::to_string(diffCol[1]) + " "
  457. + std::to_string(diffCol[2])
  458. + R"(</value></input>)");
  459. materialStr = replaceAllString(materialStr, "%diffuseColorFunc%",
  460. "");
  461. materialStr = replaceAllString(materialStr, "%diffuseColorArg%",
  462. "uDiffuseColor");
  463. }
  464. // Normal
  465. if(!normTex.empty())
  466. {
  467. materialStr = replaceAllString(materialStr, "%normalInput%",
  468. R"(<input><type>sampler2D</type><name>uNormal</name><value>)"
  469. + m_texrpath + normTex
  470. + R"(</value></input>)");
  471. materialStr = replaceAllString(materialStr, "%normalFunc%",
  472. R"(
  473. <operation>
  474. <id>20</id>
  475. <returnType>vec3</returnType>
  476. <function>readNormalFromTexture</function>
  477. <arguments>
  478. <argument>out0</argument>
  479. <argument>out1</argument>
  480. <argument>uNormal</argument>
  481. <argument>out2</argument>
  482. </arguments>
  483. </operation>)");
  484. materialStr = replaceAllString(materialStr, "%normalArg%",
  485. "out20");
  486. }
  487. else
  488. {
  489. materialStr = replaceAllString(materialStr, "%normalInput%", " ");
  490. materialStr = replaceAllString(materialStr, "%normalFunc%", " ");
  491. materialStr = replaceAllString(materialStr, "%normalArg%", "out0");
  492. }
  493. // Specular
  494. if(!specColTex.empty())
  495. {
  496. materialStr = replaceAllString(materialStr, "%specularColorInput%",
  497. R"(<input><type>sampler2D</type><name>uSpecularColor</name><value>)"
  498. + m_texrpath + specColTex
  499. + R"(</value></input>)");
  500. materialStr = replaceAllString(materialStr, "%specularColorFunc%",
  501. readRgbFromTextureTemplate);
  502. materialStr = replaceAllString(materialStr, "%id%",
  503. "50");
  504. materialStr = replaceAllString(materialStr, "%map%",
  505. "uSpecularColor");
  506. materialStr = replaceAllString(materialStr, "%specularColorArg%",
  507. "out50");
  508. }
  509. else
  510. {
  511. aiColor3D specCol = {0.0, 0.0, 0.0};
  512. mtl.Get(AI_MATKEY_COLOR_SPECULAR, specCol);
  513. materialStr = replaceAllString(materialStr, "%specularColorInput%",
  514. R"(<input><type>vec3</type><name>uSpecularColor</name><value>)"
  515. + std::to_string(specCol[0]) + " "
  516. + std::to_string(specCol[1]) + " "
  517. + std::to_string(specCol[2])
  518. + R"(</value></input>)");
  519. materialStr = replaceAllString(materialStr, "%specularColorFunc%",
  520. "");
  521. materialStr = replaceAllString(materialStr, "%specularColorArg%",
  522. "uSpecularColor");
  523. }
  524. if(!shininessTex.empty())
  525. {
  526. materialStr = replaceAllString(materialStr, "%specularPowerInput%",
  527. R"(<input><type>sampler2D</type><name>uSpecularPower</name><value>)"
  528. + m_texrpath + shininessTex
  529. + R"(</value></input>)");
  530. materialStr = replaceAllString(materialStr, "%specularPowerValue%",
  531. m_texrpath + shininessTex);
  532. materialStr = replaceAllString(materialStr, "%specularPowerFunc%",
  533. readRFromTextureTemplate);
  534. materialStr = replaceAllString(materialStr, "%id%",
  535. "60");
  536. materialStr = replaceAllString(materialStr, "%map%",
  537. "uSpecularPower");
  538. materialStr = replaceAllString(materialStr, "%specularPowerArg%",
  539. "out60");
  540. }
  541. else
  542. {
  543. float shininess = 0.0;
  544. mtl.Get(AI_MATKEY_SHININESS, shininess);
  545. const float MAX_SHININESS = 511.0;
  546. shininess = std::min(MAX_SHININESS, shininess);
  547. if(shininess > MAX_SHININESS)
  548. {
  549. LOGW("Shininness exceeds %f", MAX_SHININESS);
  550. }
  551. shininess = shininess / MAX_SHININESS;
  552. materialStr = replaceAllString(materialStr, "%specularPowerInput%",
  553. R"(<input><type>float</type><name>uSpecularPower</name><value>)"
  554. + std::to_string(shininess)
  555. + R"(</value></input>)");
  556. materialStr = replaceAllString(materialStr, "%specularPowerFunc%",
  557. "");
  558. materialStr = replaceAllString(materialStr, "%specularPowerArg%",
  559. "uSpecularPower");
  560. }
  561. materialStr = replaceAllString(materialStr, "%maxSpecularPower%", " ");
  562. materialStr = replaceAllString(materialStr, "%instanced%",
  563. (instances > 1) ? "1" : "0");
  564. materialStr = replaceAllString(materialStr, "%arraySize%",
  565. std::to_string(roundUpInstancesCount(instances)));
  566. materialStr = replaceAllString(materialStr, "%diffuseMap%",
  567. m_texrpath + diffTex);
  568. // Replace texture extensions with .anki
  569. materialStr = replaceAllString(materialStr, ".tga", ".ankitex");
  570. materialStr = replaceAllString(materialStr, ".png", ".ankitex");
  571. materialStr = replaceAllString(materialStr, ".jpg", ".ankitex");
  572. materialStr = replaceAllString(materialStr, ".jpeg", ".ankitex");
  573. // Open and write file
  574. std::fstream file;
  575. file.open(m_outputDirectory + name + ".ankimtl", std::ios::out);
  576. file << materialStr;
  577. }
  578. //==============================================================================
  579. void Exporter::exportModel(const Model& model) const
  580. {
  581. std::string name = getModelName(model);
  582. LOGI("Exporting model %s", name.c_str());
  583. std::fstream file;
  584. file.open(m_outputDirectory + name + ".ankimdl", std::ios::out);
  585. file << XML_HEADER << '\n';
  586. file << "<model>\n";
  587. file << "\t<modelPatches>\n";
  588. // Start patches
  589. file << "\t\t<modelPatch>\n";
  590. // Write mesh
  591. file << "\t\t\t<mesh>" << m_rpath
  592. << getMeshName(getMeshAt(model.m_meshIndex))
  593. << ".ankimesh</mesh>\n";
  594. // Write mesh1
  595. if(!model.m_lod1MeshName.empty())
  596. {
  597. bool found = false;
  598. for(unsigned i = 0; i < m_scene->mNumMeshes; i++)
  599. {
  600. if(m_scene->mMeshes[i]->mName.C_Str() == model.m_lod1MeshName)
  601. {
  602. file << "\t\t\t<mesh1>" << m_rpath
  603. << getMeshName(getMeshAt(i))
  604. << ".ankimesh</mesh1>\n";
  605. found = true;
  606. break;
  607. }
  608. }
  609. if(!found)
  610. {
  611. ERROR("Couldn't find the LOD1 %s", model.m_lod1MeshName.c_str());
  612. }
  613. }
  614. // Write material
  615. const aiMaterial& mtl = *m_scene->mMaterials[model.m_materialIndex];
  616. if(mtl.mAnKiProperties.find("material_override")
  617. == mtl.mAnKiProperties.end())
  618. {
  619. file << "\t\t\t<material>" << m_rpath
  620. << getMaterialName(getMaterialAt(model.m_materialIndex),
  621. model.m_instancesCount)
  622. << ".ankimtl</material>\n";
  623. }
  624. else
  625. {
  626. file << "\t\t\t<material>"
  627. << mtl.mAnKiProperties.at("material_override") << "</material>\n";
  628. }
  629. // End patches
  630. file << "\t\t</modelPatch>\n";
  631. file << "\t</modelPatches>\n";
  632. file << "</model>\n";
  633. }
  634. //==============================================================================
  635. void Exporter::exportLight(const aiLight& light)
  636. {
  637. std::ofstream& file = m_sceneFile;
  638. LOGI("Exporting light %s", light.mName.C_Str());
  639. if(light.mType != aiLightSource_POINT && light.mType != aiLightSource_SPOT)
  640. {
  641. LOGW("Skipping light %s. Unsupported type (0x%x)",
  642. light.mName.C_Str(), light.mType);
  643. return;
  644. }
  645. if(light.mAttenuationLinear != 0.0)
  646. {
  647. LOGW("Skipping light %s. Linear attenuation is not 0.0",
  648. light.mName.C_Str());
  649. return;
  650. }
  651. file << "\nnode = scene:new"
  652. << ((light.mType == aiLightSource_POINT) ? "Point" : "Spot")
  653. << "Light(\"" << light.mName.C_Str() << "\")\n";
  654. file << "lcomp = node:getSceneNodeBase():getLightComponent()\n";
  655. // Colors
  656. //aiColor3D linear = computeLightColor(light.mColorDiffuse);
  657. aiVector3D linear(light.mColorDiffuse[0], light.mColorDiffuse[1],
  658. light.mColorDiffuse[2]);
  659. file << "lcomp:setDiffuseColor(Vec4.new("
  660. << linear[0] << ", "
  661. << linear[1] << ", "
  662. << linear[2] << ", "
  663. << "1))\n";
  664. //linear = computeLightColor(light.mColorSpecular);
  665. if(light.mProperties.find("specular_color") != light.mProperties.end())
  666. {
  667. stringToFloatArray<3>(light.mProperties.at("specular_color"), linear);
  668. }
  669. file << "lcomp:setSpecularColor(Vec4.new("
  670. << linear[0] << ", "
  671. << linear[1] << ", "
  672. << linear[2] << ", "
  673. << "1))\n";
  674. // Geometry
  675. aiVector3D direction(0.0, 0.0, 1.0);
  676. switch(light.mType)
  677. {
  678. case aiLightSource_POINT:
  679. {
  680. // At this point I want the radius and have the attenuation factors
  681. // att = Ac + Al*d + Aq*d^2. When d = r then att = 0.0. Also if we
  682. // assume that Al is 0 then:
  683. // 0 = Ac + Aq*r^2. Solving by r is easy
  684. float r =
  685. sqrt(light.mAttenuationConstant / light.mAttenuationQuadratic);
  686. file << "lcomp:setRadius(" << r << ")\n";
  687. }
  688. break;
  689. case aiLightSource_SPOT:
  690. {
  691. float dist =
  692. sqrt(light.mAttenuationConstant / light.mAttenuationQuadratic);
  693. float outer = light.mAngleOuterCone;
  694. float inner = light.mAngleInnerCone;
  695. if(outer == inner)
  696. {
  697. inner = outer / 2.0;
  698. }
  699. file << "lcomp:setInnerAngle(" << inner << ")\n"
  700. << "lcomp:setOuterAngle(" << outer << ")\n"
  701. << "lcomp:setDistance(" << dist << ")\n";
  702. direction = light.mDirection;
  703. break;
  704. }
  705. default:
  706. assert(0);
  707. break;
  708. }
  709. // Transform
  710. const aiNode* node =
  711. findNodeWithName(light.mName.C_Str(), m_scene->mRootNode);
  712. if(node == nullptr)
  713. {
  714. ERROR("Couldn't find node for light %s", light.mName.C_Str());
  715. }
  716. aiMatrix4x4 rot;
  717. aiMatrix4x4::RotationX(-3.1415 / 2.0, rot);
  718. writeNodeTransform("node", node->mTransformation * rot);
  719. // Extra
  720. if(light.mProperties.find("shadow") != light.mProperties.end())
  721. {
  722. if(light.mProperties.at("shadow") == "true")
  723. {
  724. file << "lcomp:setShadowEnabled(1)\n";
  725. }
  726. else
  727. {
  728. file << "lcomp:setShadowEnabled(0)\n";
  729. }
  730. }
  731. if(light.mProperties.find("lens_flare") != light.mProperties.end())
  732. {
  733. file << "node:loadLensFlare(\"" << light.mProperties.at("lens_flare")
  734. << "\")\n";
  735. }
  736. bool lfCompRetrieved = false;
  737. if(light.mProperties.find("lens_flare_first_sprite_size")
  738. != light.mProperties.end())
  739. {
  740. if(!lfCompRetrieved)
  741. {
  742. file << "lfcomp = node:getSceneNodeBase():"
  743. << "getLensFlareComponent()\n";
  744. lfCompRetrieved = true;
  745. }
  746. aiVector3D vec;
  747. stringToFloatArray<2>(
  748. light.mProperties.at("lens_flare_first_sprite_size"), vec);
  749. file << "lfcomp:setFirstFlareSize(Vec2.new("
  750. << vec[0] << ", " << vec[1] << "))\n";
  751. }
  752. if(light.mProperties.find("lens_flare_color") != light.mProperties.end())
  753. {
  754. if(!lfCompRetrieved)
  755. {
  756. file << "lfcomp = node:getSceneNodeBase():"
  757. << "getLensFlareComponent()\n";
  758. lfCompRetrieved = true;
  759. }
  760. aiVector3D vec;
  761. stringToFloatArray<4>(light.mProperties.at("lens_flare_color"), vec);
  762. file << "lfcomp:setColorMultiplier(Vec4.new("
  763. << vec[0] << ", "
  764. << vec[1] << ", "
  765. << vec[2] << ", "
  766. << vec[3] << "))\n";
  767. }
  768. }
  769. //==============================================================================
  770. void Exporter::exportAnimation(
  771. const aiAnimation& anim,
  772. unsigned index)
  773. {
  774. // Get name
  775. std::string name = anim.mName.C_Str();
  776. if(name.size() == 0)
  777. {
  778. name = std::string("unnamed_") + std::to_string(index);
  779. }
  780. // Find if it's skeleton animation
  781. /*bool isSkeletalAnimation = false;
  782. for(uint32_t i = 0; i < scene.mNumMeshes; i++)
  783. {
  784. const aiMesh& mesh = *scene.mMeshes[i];
  785. if(mesh.HasBones())
  786. {
  787. }
  788. }*/
  789. std::fstream file;
  790. LOGI("Exporting animation %s", name.c_str());
  791. file.open(m_outputDirectory + name + ".ankianim", std::ios::out);
  792. file << XML_HEADER << "\n";
  793. file << "<animation>\n";
  794. file << "\t<channels>\n";
  795. for(uint32_t i = 0; i < anim.mNumChannels; i++)
  796. {
  797. const aiNodeAnim& nAnim = *anim.mChannels[i];
  798. file << "\t\t<channel>\n";
  799. // Name
  800. file << "\t\t\t<name>" << nAnim.mNodeName.C_Str() << "</name>\n";
  801. // Positions
  802. file << "\t\t\t<positionKeys>\n";
  803. for(uint32_t j = 0; j < nAnim.mNumPositionKeys; j++)
  804. {
  805. const aiVectorKey& key = nAnim.mPositionKeys[j];
  806. if(m_flipyz)
  807. {
  808. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  809. << "<value>" << key.mValue[0] << " "
  810. << key.mValue[2] << " " << -key.mValue[1]
  811. << "</value></key>\n";
  812. }
  813. else
  814. {
  815. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  816. << "<value>" << key.mValue[0] << " "
  817. << key.mValue[1] << " " << key.mValue[2]
  818. << "</value></key>\n";
  819. }
  820. }
  821. file << "\t\t\t</positionKeys>\n";
  822. // Rotations
  823. file << "\t\t\t<rotationKeys>\n";
  824. for(uint32_t j = 0; j < nAnim.mNumRotationKeys; j++)
  825. {
  826. const aiQuatKey& key = nAnim.mRotationKeys[j];
  827. aiMatrix3x3 mat = toAnkiMatrix(key.mValue.GetMatrix());
  828. aiQuaternion quat(mat);
  829. //aiQuaternion quat(key.mValue);
  830. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  831. << "<value>" << quat.x << " " << quat.y
  832. << " " << quat.z << " "
  833. << quat.w << "</value></key>\n";
  834. }
  835. file << "\t\t\t</rotationKeys>\n";
  836. // Scale
  837. file << "\t\t\t<scalingKeys>\n";
  838. for(uint32_t j = 0; j < nAnim.mNumScalingKeys; j++)
  839. {
  840. const aiVectorKey& key = nAnim.mScalingKeys[j];
  841. // Note: only uniform scale
  842. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  843. << "<value>"
  844. << ((key.mValue[0] + key.mValue[1] + key.mValue[2]) / 3.0)
  845. << "</value></key>\n";
  846. }
  847. file << "\t\t\t</scalingKeys>\n";
  848. file << "\t\t</channel>\n";
  849. }
  850. file << "\t</channels>\n";
  851. file << "</animation>\n";
  852. }
  853. //==============================================================================
  854. void Exporter::load()
  855. {
  856. LOGI("Loading file %s", &m_inputFilename[0]);
  857. //Assimp::DefaultLogger::create("", Logger::VERBOSE);
  858. m_importer.SetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE, 170);
  859. const aiScene* scene = m_importer.ReadFile(m_inputFilename, 0
  860. //| aiProcess_FindInstances
  861. | aiProcess_Triangulate
  862. | aiProcess_JoinIdenticalVertices
  863. //| aiProcess_SortByPType
  864. | aiProcess_ImproveCacheLocality
  865. | aiProcess_OptimizeMeshes
  866. | aiProcess_RemoveRedundantMaterials
  867. | aiProcess_CalcTangentSpace
  868. | aiProcess_GenSmoothNormals
  869. );
  870. if(!scene)
  871. {
  872. ERROR("%s", m_importer.GetErrorString());
  873. }
  874. m_scene = scene;
  875. }
  876. //==============================================================================
  877. void Exporter::visitNode(const aiNode* ainode)
  878. {
  879. if(ainode == nullptr)
  880. {
  881. return;
  882. }
  883. // For every mesh of this node
  884. for(unsigned i = 0; i < ainode->mNumMeshes; i++)
  885. {
  886. unsigned meshIndex = ainode->mMeshes[i];
  887. unsigned mtlIndex = m_scene->mMeshes[meshIndex]->mMaterialIndex;
  888. // Check properties
  889. std::string lod1MeshName;
  890. bool special = false;
  891. for(const auto& prop : m_scene->mMeshes[meshIndex]->mProperties)
  892. {
  893. if(prop.first == "particles")
  894. {
  895. ParticleEmitter p;
  896. p.m_filename = prop.second;
  897. p.m_transform = ainode->mTransformation;
  898. m_particleEmitters.push_back(p);
  899. special = true;
  900. }
  901. else if(prop.first == "collision" && prop.second == "true")
  902. {
  903. StaticCollisionNode n;
  904. n.m_meshIndex = meshIndex;
  905. n.m_transform = ainode->mTransformation;
  906. m_staticCollisionNodes.push_back(n);
  907. special = true;
  908. }
  909. else if(prop.first == "portal" && prop.second == "true")
  910. {
  911. Portal portal;
  912. portal.m_meshIndex = meshIndex;
  913. portal.m_transform = ainode->mTransformation;
  914. m_portals.push_back(portal);
  915. special = true;
  916. }
  917. else if(prop.first == "sector" && prop.second == "true")
  918. {
  919. Sector sector;
  920. sector.m_meshIndex = meshIndex;
  921. sector.m_transform = ainode->mTransformation;
  922. m_sectors.push_back(sector);
  923. special = true;
  924. }
  925. else if(prop.first == "lod1")
  926. {
  927. lod1MeshName = prop.second;
  928. special = false;
  929. }
  930. }
  931. if(special)
  932. {
  933. continue;
  934. }
  935. // Find if there is another node with the same mesh-material-group pair
  936. std::vector<Node>::iterator it;
  937. for(it = m_nodes.begin(); it != m_nodes.end(); ++it)
  938. {
  939. const Node& node = *it;
  940. const Model& model = m_models[node.m_modelIndex];
  941. if(model.m_meshIndex == meshIndex
  942. && model.m_materialIndex == mtlIndex
  943. && node.m_group == ainode->mGroup.C_Str()
  944. && node.m_group != "none")
  945. {
  946. break;
  947. }
  948. }
  949. if(it != m_nodes.end())
  950. {
  951. // A node with the same model exists. It's instanced
  952. Node& node = *it;
  953. Model& model = m_models[node.m_modelIndex];
  954. assert(node.m_transforms.size() > 0);
  955. node.m_transforms.push_back(ainode->mTransformation);
  956. ++model.m_instancesCount;
  957. break;
  958. }
  959. // Create new model
  960. Model mdl;
  961. mdl.m_meshIndex = meshIndex;
  962. mdl.m_materialIndex = mtlIndex;
  963. mdl.m_lod1MeshName = lod1MeshName;
  964. m_models.push_back(mdl);
  965. // Create new node
  966. Node node;
  967. node.m_modelIndex = m_models.size() - 1;
  968. node.m_transforms.push_back(ainode->mTransformation);
  969. node.m_group = ainode->mGroup.C_Str();
  970. m_nodes.push_back(node);
  971. }
  972. // Go to children
  973. for(uint32_t i = 0; i < ainode->mNumChildren; i++)
  974. {
  975. visitNode(ainode->mChildren[i]);
  976. }
  977. }
  978. //==============================================================================
  979. void Exporter::exportCollisionMesh(uint32_t meshIdx)
  980. {
  981. std::string name = getMeshName(getMeshAt(meshIdx));
  982. std::fstream file;
  983. file.open(m_outputDirectory + name + ".ankicl", std::ios::out);
  984. file << XML_HEADER << '\n';
  985. // Write collision mesh
  986. file << "<collisionShape>\n\t<type>staticMesh</type>\n\t<value>"
  987. << m_rpath << name
  988. << ".ankimesh</value>\n</collisionShape>\n";
  989. }
  990. //==============================================================================
  991. void Exporter::exportAll()
  992. {
  993. LOGI("Exporting scene to %s", &m_outputDirectory[0]);
  994. //
  995. // Open scene file
  996. //
  997. m_sceneFile.open(m_outputDirectory + "scene.lua");
  998. std::ofstream& file = m_sceneFile;
  999. file << "local scene = getSceneGraph()\n"
  1000. << "local rot\n"
  1001. << "local node\n"
  1002. << "local inst\n"
  1003. << "local lcomp\n";
  1004. //
  1005. // Get all node/model data
  1006. //
  1007. visitNode(m_scene->mRootNode);
  1008. //
  1009. // Export collision meshes
  1010. //
  1011. for(auto n : m_staticCollisionNodes)
  1012. {
  1013. exportMesh(*m_scene->mMeshes[n.m_meshIndex], nullptr);
  1014. exportCollisionMesh(n.m_meshIndex);
  1015. file << "\n";
  1016. writeTransform(n.m_transform);
  1017. std::string name = getMeshName(getMeshAt(n.m_meshIndex));
  1018. std::string fname = m_rpath + name + ".ankicl";
  1019. file << "node = scene:newStaticCollisionNode(\""
  1020. << name << "\", \"" << fname << "\", trf)\n";
  1021. }
  1022. //
  1023. // Export portals
  1024. //
  1025. unsigned i = 0;
  1026. for(const Portal& portal : m_portals)
  1027. {
  1028. uint32_t meshIndex = portal.m_meshIndex;
  1029. exportMesh(*m_scene->mMeshes[meshIndex], nullptr);
  1030. std::string name = getMeshName(getMeshAt(meshIndex));
  1031. std::string fname = m_rpath + name + ".ankimesh";
  1032. file << "\nnode = scene:newPortal(\""
  1033. << name << i << "\", \"" << fname << "\")\n";
  1034. writeNodeTransform("node", portal.m_transform);
  1035. ++i;
  1036. }
  1037. //
  1038. // Export sectors
  1039. //
  1040. i = 0;
  1041. for(const Sector& sector : m_sectors)
  1042. {
  1043. uint32_t meshIndex = sector.m_meshIndex;
  1044. exportMesh(*m_scene->mMeshes[meshIndex], nullptr);
  1045. std::string name = getMeshName(getMeshAt(meshIndex));
  1046. std::string fname = m_rpath + name + ".ankimesh";
  1047. file << "\nnode = scene:newSector(\""
  1048. << name << i << "\", \"" << fname << "\")\n";
  1049. writeNodeTransform("node", sector.m_transform);
  1050. ++i;
  1051. }
  1052. //
  1053. // Export particle emitters
  1054. //
  1055. i = 0;
  1056. for(const ParticleEmitter& p : m_particleEmitters)
  1057. {
  1058. std::string name = "particles" + std::to_string(i);
  1059. file << "\nnode = scene:newParticleEmitter(\"" << name << "\", \""
  1060. << p.m_filename << "\")\n";
  1061. writeNodeTransform("node", p.m_transform);
  1062. ++i;
  1063. }
  1064. //
  1065. // Export nodes and models.
  1066. //
  1067. for(uint32_t i = 0; i < m_nodes.size(); i++)
  1068. {
  1069. Node& node = m_nodes[i];
  1070. Model& model = m_models[node.m_modelIndex];
  1071. // TODO If not instanced bake transform
  1072. exportMesh(*m_scene->mMeshes[model.m_meshIndex], nullptr);
  1073. exportMaterial(*m_scene->mMaterials[model.m_materialIndex],
  1074. model.m_instancesCount);
  1075. exportModel(model);
  1076. std::string modelName = getModelName(model);
  1077. std::string nodeName = modelName + node.m_group + std::to_string(i);
  1078. // Write the main node
  1079. file << "\nnode = scene:newModelNode(\""
  1080. << nodeName << "\", \""
  1081. << m_rpath << modelName << ".ankimdl" << "\")\n";
  1082. writeNodeTransform("node", node.m_transforms[0]);
  1083. // Write instance nodes
  1084. for(unsigned j = 1; j < node.m_transforms.size(); j++)
  1085. {
  1086. file << "\ninst = scene:newInstanceNode(\""
  1087. << nodeName << "_inst" << (j - 1) << "\")\n"
  1088. << "node:getSceneNodeBase():addChild("
  1089. << "inst:getSceneNodeBase())\n";
  1090. writeNodeTransform("inst", node.m_transforms[j]);
  1091. }
  1092. }
  1093. //
  1094. // Lights
  1095. //
  1096. for(unsigned i = 0; i < m_scene->mNumLights; i++)
  1097. {
  1098. exportLight(*m_scene->mLights[i]);
  1099. }
  1100. //
  1101. // Animations
  1102. //
  1103. for(unsigned i = 0; i < m_scene->mNumAnimations; i++)
  1104. {
  1105. exportAnimation(*m_scene->mAnimations[i], i);
  1106. }
  1107. LOGI("Done exporting scene!");
  1108. }