Exporter.cpp 28 KB

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