Exporter.cpp 25 KB

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