Exporter.cpp 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162
  1. // Copyright (C) 2009-2016, Panagiotis Christopoulos Charitos and contributors.
  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. static std::string getMeshName(const aiMesh& mesh)
  45. {
  46. return std::string(mesh.mName.C_Str());
  47. }
  48. //==============================================================================
  49. /// Walk the node hierarchy and find the node.
  50. static const aiNode* findNodeWithName(
  51. const std::string& name, const aiNode* node)
  52. {
  53. if(node == nullptr || node->mName.C_Str() == name)
  54. {
  55. return node;
  56. }
  57. const aiNode* out = nullptr;
  58. // Go to children
  59. for(uint32_t i = 0; i < node->mNumChildren; i++)
  60. {
  61. out = findNodeWithName(name, node->mChildren[i]);
  62. if(out)
  63. {
  64. break;
  65. }
  66. }
  67. return out;
  68. }
  69. //==============================================================================
  70. static std::vector<std::string> tokenize(const std::string& source)
  71. {
  72. const char* delimiter = " ";
  73. bool keepEmpty = false;
  74. std::vector<std::string> results;
  75. size_t prev = 0;
  76. size_t next = 0;
  77. while((next = source.find_first_of(delimiter, prev)) != std::string::npos)
  78. {
  79. if(keepEmpty || (next - prev != 0))
  80. {
  81. results.push_back(source.substr(prev, next - prev));
  82. }
  83. prev = next + 1;
  84. }
  85. if(prev < source.size())
  86. {
  87. results.push_back(source.substr(prev));
  88. }
  89. return results;
  90. }
  91. //==============================================================================
  92. template<int N, typename Arr>
  93. static void stringToFloatArray(const std::string& in, Arr& out)
  94. {
  95. std::vector<std::string> tokens = tokenize(in);
  96. if(tokens.size() != N)
  97. {
  98. ERROR("Failed to parse %s", in.c_str());
  99. }
  100. int count = 0;
  101. for(const std::string& s : tokens)
  102. {
  103. out[count] = std::stof(s);
  104. ++count;
  105. }
  106. }
  107. //==============================================================================
  108. static void removeScale(aiMatrix4x4& m)
  109. {
  110. aiVector3D xAxis(m.a1, m.b1, m.c1);
  111. aiVector3D yAxis(m.a2, m.b2, m.c2);
  112. aiVector3D zAxis(m.a3, m.b3, m.c3);
  113. float scale = xAxis.Length();
  114. m.a1 /= scale;
  115. m.b1 /= scale;
  116. m.c1 /= scale;
  117. scale = yAxis.Length();
  118. m.a2 /= scale;
  119. m.b2 /= scale;
  120. m.c2 /= scale;
  121. scale = zAxis.Length();
  122. m.a3 /= scale;
  123. m.b3 /= scale;
  124. m.c3 /= scale;
  125. }
  126. //==============================================================================
  127. static float getUniformScale(const aiMatrix4x4& m)
  128. {
  129. const float SCALE_THRESHOLD = 0.01; // 1 cm
  130. aiVector3D xAxis(m.a1, m.b1, m.c1);
  131. aiVector3D yAxis(m.a2, m.b2, m.c2);
  132. aiVector3D zAxis(m.a3, m.b3, m.c3);
  133. float scale = xAxis.Length();
  134. if(std::abs(scale - yAxis.Length()) > SCALE_THRESHOLD
  135. || std::abs(scale - zAxis.Length()) > SCALE_THRESHOLD)
  136. {
  137. ERROR("No uniform scale in the matrix");
  138. }
  139. return scale;
  140. }
  141. //==============================================================================
  142. // Exporter =
  143. //==============================================================================
  144. //==============================================================================
  145. std::string Exporter::getMaterialName(const aiMaterial& mtl)
  146. {
  147. aiString ainame;
  148. std::string name;
  149. if(mtl.Get(AI_MATKEY_NAME, ainame) == AI_SUCCESS)
  150. {
  151. name = ainame.C_Str();
  152. }
  153. else
  154. {
  155. ERROR("Material's name is missing");
  156. }
  157. return name;
  158. }
  159. //==============================================================================
  160. aiMatrix4x4 Exporter::toAnkiMatrix(const aiMatrix4x4& in) const
  161. {
  162. static const aiMatrix4x4 toLeftHanded(
  163. 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1);
  164. static const aiMatrix4x4 toLeftHandedInv(
  165. 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1);
  166. if(m_flipyz)
  167. {
  168. return toLeftHanded * in * toLeftHandedInv;
  169. }
  170. else
  171. {
  172. return in;
  173. }
  174. }
  175. //==============================================================================
  176. aiMatrix3x3 Exporter::toAnkiMatrix(const aiMatrix3x3& in) const
  177. {
  178. static const aiMatrix3x3 toLeftHanded(1, 0, 0, 0, 0, 1, 0, -1, 0);
  179. static const aiMatrix3x3 toLeftHandedInv(1, 0, 0, 0, 0, -1, 0, 1, 0);
  180. if(m_flipyz)
  181. {
  182. return toLeftHanded * in;
  183. }
  184. else
  185. {
  186. return in;
  187. }
  188. }
  189. //==============================================================================
  190. void Exporter::writeTransform(const aiMatrix4x4& inmat)
  191. {
  192. aiMatrix4x4 mat = inmat;
  193. std::ofstream& file = m_sceneFile;
  194. float pos[3];
  195. pos[0] = mat[0][3];
  196. pos[1] = mat[1][3];
  197. pos[2] = mat[2][3];
  198. file << "trf = Transform.new()\n";
  199. file << "trf:setOrigin(Vec4.new(" << pos[0] << ", " << pos[1] << ", "
  200. << pos[2] << ", 0))\n";
  201. float scale = getUniformScale(mat);
  202. removeScale(mat);
  203. file << "rot = Mat3x4.new()\n";
  204. file << "rot:setAll(";
  205. for(unsigned j = 0; j < 3; j++)
  206. {
  207. for(unsigned i = 0; i < 4; i++)
  208. {
  209. if(i == 3)
  210. {
  211. file << "0";
  212. }
  213. else
  214. {
  215. file << mat[j][i];
  216. }
  217. if(!(i == 3 && j == 2))
  218. {
  219. file << ", ";
  220. }
  221. }
  222. }
  223. file << ")\n";
  224. file << "trf:setRotation(rot)\n";
  225. file << "trf:setScale(" << scale << ")\n";
  226. }
  227. //==============================================================================
  228. void Exporter::writeNodeTransform(
  229. const std::string& node, const aiMatrix4x4& mat)
  230. {
  231. std::ofstream& file = m_sceneFile;
  232. writeTransform(mat);
  233. file << node
  234. << ":getSceneNodeBase():getMoveComponent():setLocalTransform(trf)\n";
  235. }
  236. //==============================================================================
  237. const aiMesh& Exporter::getMeshAt(unsigned index) const
  238. {
  239. assert(index < m_scene->mNumMeshes);
  240. return *m_scene->mMeshes[index];
  241. }
  242. //==============================================================================
  243. const aiMaterial& Exporter::getMaterialAt(unsigned index) const
  244. {
  245. assert(index < m_scene->mNumMaterials);
  246. return *m_scene->mMaterials[index];
  247. }
  248. //==============================================================================
  249. std::string Exporter::getModelName(const Model& model) const
  250. {
  251. std::string name = getMeshName(getMeshAt(model.m_meshIndex));
  252. name += getMaterialName(getMaterialAt(model.m_materialIndex));
  253. return name;
  254. }
  255. //==============================================================================
  256. void Exporter::exportSkeleton(const aiMesh& mesh) const
  257. {
  258. assert(mesh.HasBones());
  259. std::string name = mesh.mName.C_Str();
  260. std::fstream file;
  261. LOGI("Exporting skeleton %s", name.c_str());
  262. // Open file
  263. file.open(m_outputDirectory + name + ".skel", std::ios::out);
  264. file << XML_HEADER << "\n";
  265. file << "<skeleton>\n";
  266. file << "\t<bones>\n";
  267. bool rootBoneFound = false;
  268. for(uint32_t i = 0; i < mesh.mNumBones; i++)
  269. {
  270. const aiBone& bone = *mesh.mBones[i];
  271. file << "\t\t<bone>\n";
  272. // <name>
  273. file << "\t\t\t<name>" << bone.mName.C_Str() << "</name>\n";
  274. if(strcmp(bone.mName.C_Str(), "root") == 0)
  275. {
  276. rootBoneFound = true;
  277. }
  278. // <transform>
  279. file << "\t\t\t<transform>";
  280. for(uint32_t j = 0; j < 16; j++)
  281. {
  282. file << bone.mOffsetMatrix[j] << " ";
  283. }
  284. file << "</transform>\n";
  285. file << "\t\t</bone>\n";
  286. }
  287. if(!rootBoneFound)
  288. {
  289. ERROR("There should be one bone named \"root\"");
  290. }
  291. file << "\t</bones>\n";
  292. file << "</skeleton>\n";
  293. }
  294. //==============================================================================
  295. void Exporter::exportModel(const Model& model) const
  296. {
  297. std::string name = getModelName(model);
  298. LOGI("Exporting model %s", name.c_str());
  299. std::fstream file;
  300. file.open(m_outputDirectory + name + ".ankimdl", std::ios::out);
  301. file << XML_HEADER << '\n';
  302. file << "<model>\n";
  303. file << "\t<modelPatches>\n";
  304. // Start patches
  305. file << "\t\t<modelPatch>\n";
  306. // Write mesh
  307. file << "\t\t\t<mesh>" << m_rpath
  308. << getMeshName(getMeshAt(model.m_meshIndex)) << ".ankimesh</mesh>\n";
  309. // Write mesh1
  310. if(!model.m_lod1MeshName.empty())
  311. {
  312. bool found = false;
  313. for(unsigned i = 0; i < m_scene->mNumMeshes; i++)
  314. {
  315. if(m_scene->mMeshes[i]->mName.C_Str() == model.m_lod1MeshName)
  316. {
  317. file << "\t\t\t<mesh1>" << m_rpath << getMeshName(getMeshAt(i))
  318. << ".ankimesh</mesh1>\n";
  319. found = true;
  320. break;
  321. }
  322. }
  323. if(!found)
  324. {
  325. ERROR("Couldn't find the LOD1 %s", model.m_lod1MeshName.c_str());
  326. }
  327. }
  328. // Write material
  329. const aiMaterial& mtl = *m_scene->mMaterials[model.m_materialIndex];
  330. if(mtl.mAnKiProperties.find("material_override")
  331. == mtl.mAnKiProperties.end())
  332. {
  333. file << "\t\t\t<material>" << m_rpath
  334. << getMaterialName(getMaterialAt(model.m_materialIndex))
  335. << ".ankimtl</material>\n";
  336. }
  337. else
  338. {
  339. file << "\t\t\t<material>"
  340. << mtl.mAnKiProperties.at("material_override") << "</material>\n";
  341. }
  342. // End patches
  343. file << "\t\t</modelPatch>\n";
  344. file << "\t</modelPatches>\n";
  345. file << "</model>\n";
  346. }
  347. //==============================================================================
  348. void Exporter::exportLight(const aiLight& light)
  349. {
  350. std::ofstream& file = m_sceneFile;
  351. LOGI("Exporting light %s", light.mName.C_Str());
  352. if(light.mType != aiLightSource_POINT && light.mType != aiLightSource_SPOT)
  353. {
  354. LOGW("Skipping light %s. Unsupported type (0x%x)",
  355. light.mName.C_Str(),
  356. light.mType);
  357. return;
  358. }
  359. if(light.mAttenuationLinear != 0.0)
  360. {
  361. LOGW("Skipping light %s. Linear attenuation is not 0.0",
  362. light.mName.C_Str());
  363. return;
  364. }
  365. file << "\nnode = scene:new"
  366. << ((light.mType == aiLightSource_POINT) ? "Point" : "Spot")
  367. << "Light(\"" << light.mName.C_Str() << "\")\n";
  368. file << "lcomp = node:getSceneNodeBase():getLightComponent()\n";
  369. // Colors
  370. // aiColor3D linear = computeLightColor(light.mColorDiffuse);
  371. aiVector3D linear(
  372. light.mColorDiffuse[0], light.mColorDiffuse[1], light.mColorDiffuse[2]);
  373. file << "lcomp:setDiffuseColor(Vec4.new(" << linear[0] << ", " << linear[1]
  374. << ", " << linear[2] << ", "
  375. << "1))\n";
  376. // linear = computeLightColor(light.mColorSpecular);
  377. if(light.mProperties.find("specular_color") != light.mProperties.end())
  378. {
  379. stringToFloatArray<3>(light.mProperties.at("specular_color"), linear);
  380. }
  381. file << "lcomp:setSpecularColor(Vec4.new(" << linear[0] << ", " << linear[1]
  382. << ", " << linear[2] << ", "
  383. << "1))\n";
  384. // Geometry
  385. aiVector3D direction(0.0, 0.0, 1.0);
  386. switch(light.mType)
  387. {
  388. case aiLightSource_POINT:
  389. {
  390. // At this point I want the radius and have the attenuation factors
  391. // att = Ac + Al*d + Aq*d^2. When d = r then att = 0.0. Also if we
  392. // assume that Al is 0 then:
  393. // 0 = Ac + Aq*r^2. Solving by r is easy
  394. float r =
  395. sqrt(light.mAttenuationConstant / light.mAttenuationQuadratic);
  396. file << "lcomp:setRadius(" << r << ")\n";
  397. }
  398. break;
  399. case aiLightSource_SPOT:
  400. {
  401. float dist =
  402. sqrt(light.mAttenuationConstant / light.mAttenuationQuadratic);
  403. float outer = light.mAngleOuterCone;
  404. float inner = light.mAngleInnerCone;
  405. if(outer == inner)
  406. {
  407. inner = outer / 2.0;
  408. }
  409. file << "lcomp:setInnerAngle(" << inner << ")\n"
  410. << "lcomp:setOuterAngle(" << outer << ")\n"
  411. << "lcomp:setDistance(" << dist << ")\n";
  412. direction = light.mDirection;
  413. break;
  414. }
  415. default:
  416. assert(0);
  417. break;
  418. }
  419. // Transform
  420. const aiNode* node =
  421. findNodeWithName(light.mName.C_Str(), m_scene->mRootNode);
  422. if(node == nullptr)
  423. {
  424. ERROR("Couldn't find node for light %s", light.mName.C_Str());
  425. }
  426. aiMatrix4x4 rot;
  427. aiMatrix4x4::RotationX(-3.1415 / 2.0, rot);
  428. writeNodeTransform("node", toAnkiMatrix(node->mTransformation * rot));
  429. // Extra
  430. if(light.mProperties.find("shadow") != light.mProperties.end())
  431. {
  432. if(light.mProperties.at("shadow") == "true")
  433. {
  434. file << "lcomp:setShadowEnabled(1)\n";
  435. }
  436. else
  437. {
  438. file << "lcomp:setShadowEnabled(0)\n";
  439. }
  440. }
  441. if(light.mProperties.find("lens_flare") != light.mProperties.end())
  442. {
  443. file << "node:loadLensFlare(\"" << light.mProperties.at("lens_flare")
  444. << "\")\n";
  445. }
  446. bool lfCompRetrieved = false;
  447. if(light.mProperties.find("lens_flare_first_sprite_size")
  448. != light.mProperties.end())
  449. {
  450. if(!lfCompRetrieved)
  451. {
  452. file << "lfcomp = node:getSceneNodeBase():"
  453. << "getLensFlareComponent()\n";
  454. lfCompRetrieved = true;
  455. }
  456. aiVector3D vec;
  457. stringToFloatArray<2>(
  458. light.mProperties.at("lens_flare_first_sprite_size"), vec);
  459. file << "lfcomp:setFirstFlareSize(Vec2.new(" << vec[0] << ", " << vec[1]
  460. << "))\n";
  461. }
  462. if(light.mProperties.find("lens_flare_color") != light.mProperties.end())
  463. {
  464. if(!lfCompRetrieved)
  465. {
  466. file << "lfcomp = node:getSceneNodeBase():"
  467. << "getLensFlareComponent()\n";
  468. lfCompRetrieved = true;
  469. }
  470. aiVector3D vec;
  471. stringToFloatArray<4>(light.mProperties.at("lens_flare_color"), vec);
  472. file << "lfcomp:setColorMultiplier(Vec4.new(" << vec[0] << ", "
  473. << vec[1] << ", " << vec[2] << ", " << vec[3] << "))\n";
  474. }
  475. bool eventCreated = false;
  476. if(light.mProperties.find("light_event_intensity")
  477. != light.mProperties.end())
  478. {
  479. if(!eventCreated)
  480. {
  481. file << "event = events:newLightEvent(0.0, -1.0, "
  482. "node:getSceneNodeBase())\n";
  483. eventCreated = true;
  484. }
  485. aiVector3D vec;
  486. stringToFloatArray<4>(
  487. light.mProperties.at("light_event_intensity"), vec);
  488. file << "event:setIntensityMultiplier(Vec4.new(" << vec[0] << ", "
  489. << vec[1] << ", " << vec[2] << ", " << vec[3] << "))\n";
  490. }
  491. if(light.mProperties.find("light_event_frequency")
  492. != light.mProperties.end())
  493. {
  494. if(!eventCreated)
  495. {
  496. file << "event = events:newLightEvent(0.0, -1.0, "
  497. "node:getSceneNodeBase())\n";
  498. eventCreated = true;
  499. }
  500. float vec[2];
  501. stringToFloatArray<2>(
  502. light.mProperties.at("light_event_frequency"), vec);
  503. file << "event:setFrequency(" << vec[0] << ", " << vec[1] << ")\n";
  504. }
  505. }
  506. //==============================================================================
  507. void Exporter::exportAnimation(const aiAnimation& anim, unsigned index)
  508. {
  509. // Get name
  510. std::string name = anim.mName.C_Str();
  511. if(name.size() == 0)
  512. {
  513. name = std::string("unnamed_") + std::to_string(index);
  514. }
  515. // Find if it's skeleton animation
  516. /*bool isSkeletalAnimation = false;
  517. for(uint32_t i = 0; i < scene.mNumMeshes; i++)
  518. {
  519. const aiMesh& mesh = *scene.mMeshes[i];
  520. if(mesh.HasBones())
  521. {
  522. }
  523. }*/
  524. std::fstream file;
  525. LOGI("Exporting animation %s", name.c_str());
  526. file.open(m_outputDirectory + name + ".ankianim", std::ios::out);
  527. file << XML_HEADER << "\n";
  528. file << "<animation>\n";
  529. file << "\t<channels>\n";
  530. for(uint32_t i = 0; i < anim.mNumChannels; i++)
  531. {
  532. const aiNodeAnim& nAnim = *anim.mChannels[i];
  533. file << "\t\t<channel>\n";
  534. // Name
  535. file << "\t\t\t<name>" << nAnim.mNodeName.C_Str() << "</name>\n";
  536. // Positions
  537. file << "\t\t\t<positionKeys>\n";
  538. for(uint32_t j = 0; j < nAnim.mNumPositionKeys; j++)
  539. {
  540. const aiVectorKey& key = nAnim.mPositionKeys[j];
  541. if(m_flipyz)
  542. {
  543. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  544. << "<value>" << key.mValue[0] << " " << key.mValue[2]
  545. << " " << -key.mValue[1] << "</value></key>\n";
  546. }
  547. else
  548. {
  549. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  550. << "<value>" << key.mValue[0] << " " << key.mValue[1]
  551. << " " << key.mValue[2] << "</value></key>\n";
  552. }
  553. }
  554. file << "\t\t\t</positionKeys>\n";
  555. // Rotations
  556. file << "\t\t\t<rotationKeys>\n";
  557. for(uint32_t j = 0; j < nAnim.mNumRotationKeys; j++)
  558. {
  559. const aiQuatKey& key = nAnim.mRotationKeys[j];
  560. aiMatrix3x3 mat = toAnkiMatrix(key.mValue.GetMatrix());
  561. aiQuaternion quat(mat);
  562. // aiQuaternion quat(key.mValue);
  563. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  564. << "<value>" << quat.x << " " << quat.y << " " << quat.z << " "
  565. << quat.w << "</value></key>\n";
  566. }
  567. file << "\t\t\t</rotationKeys>\n";
  568. // Scale
  569. file << "\t\t\t<scalingKeys>\n";
  570. for(uint32_t j = 0; j < nAnim.mNumScalingKeys; j++)
  571. {
  572. const aiVectorKey& key = nAnim.mScalingKeys[j];
  573. // Note: only uniform scale
  574. file << "\t\t\t\t<key><time>" << key.mTime << "</time>"
  575. << "<value>"
  576. << ((key.mValue[0] + key.mValue[1] + key.mValue[2]) / 3.0)
  577. << "</value></key>\n";
  578. }
  579. file << "\t\t\t</scalingKeys>\n";
  580. file << "\t\t</channel>\n";
  581. }
  582. file << "\t</channels>\n";
  583. file << "</animation>\n";
  584. }
  585. //==============================================================================
  586. void Exporter::exportCamera(const aiCamera& cam)
  587. {
  588. std::ofstream& file = m_sceneFile;
  589. LOGI("Exporting camera %s", cam.mName.C_Str());
  590. // Write the main node
  591. file << "\nnode = scene:newPerspectiveCamera(\"" << cam.mName.C_Str()
  592. << "\")\n";
  593. file << "scene:setActiveCamera(node:getSceneNodeBase())\n";
  594. file << "node:setAll(" << cam.mHorizontalFOV << ", "
  595. << "1.0 / getMainRenderer():getAspectRatio() * " << cam.mHorizontalFOV
  596. << ", " << cam.mClipPlaneNear << ", " << cam.mClipPlaneFar << ")\n";
  597. // Find the node
  598. const aiNode* node =
  599. findNodeWithName(cam.mName.C_Str(), m_scene->mRootNode);
  600. if(node == nullptr)
  601. {
  602. ERROR("Couldn't find node for camera %s", cam.mName.C_Str());
  603. }
  604. aiMatrix4x4 rot;
  605. aiMatrix4x4::RotationX(-3.1415 / 2.0, rot);
  606. writeNodeTransform("node", toAnkiMatrix(node->mTransformation * rot));
  607. }
  608. //==============================================================================
  609. void Exporter::load()
  610. {
  611. LOGI("Loading file %s", &m_inputFilename[0]);
  612. const int smoothAngle = 170;
  613. m_importer.SetPropertyFloat(
  614. AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE, smoothAngle);
  615. unsigned flags = 0
  616. //| aiProcess_FindInstances
  617. | aiProcess_JoinIdenticalVertices
  618. //| aiProcess_SortByPType
  619. | aiProcess_ImproveCacheLocality | aiProcess_OptimizeMeshes
  620. | aiProcess_RemoveRedundantMaterials | aiProcess_CalcTangentSpace
  621. | aiProcess_GenSmoothNormals;
  622. const aiScene* scene =
  623. m_importer.ReadFile(m_inputFilename, flags | aiProcess_Triangulate);
  624. if(!scene)
  625. {
  626. ERROR("%s", m_importer.GetErrorString());
  627. }
  628. m_scene = scene;
  629. // Load without triangulation
  630. m_importerNoTriangles.SetPropertyFloat(
  631. AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE, smoothAngle);
  632. scene = m_importerNoTriangles.ReadFile(m_inputFilename, flags);
  633. if(!scene)
  634. {
  635. ERROR("%s", m_importerNoTriangles.GetErrorString());
  636. }
  637. m_sceneNoTriangles = scene;
  638. }
  639. //==============================================================================
  640. void Exporter::visitNode(const aiNode* ainode)
  641. {
  642. if(ainode == nullptr)
  643. {
  644. return;
  645. }
  646. // For every mesh of this node
  647. for(unsigned i = 0; i < ainode->mNumMeshes; i++)
  648. {
  649. unsigned meshIndex = ainode->mMeshes[i];
  650. unsigned mtlIndex = m_scene->mMeshes[meshIndex]->mMaterialIndex;
  651. // Check properties
  652. std::string lod1MeshName;
  653. std::string collisionMesh;
  654. bool special = false;
  655. for(const auto& prop : m_scene->mMeshes[meshIndex]->mProperties)
  656. {
  657. if(prop.first == "particles")
  658. {
  659. ParticleEmitter p;
  660. p.m_filename = prop.second;
  661. p.m_transform = toAnkiMatrix(ainode->mTransformation);
  662. m_particleEmitters.push_back(p);
  663. special = true;
  664. }
  665. else if(prop.first == "collision" && prop.second == "true")
  666. {
  667. StaticCollisionNode n;
  668. n.m_meshIndex = meshIndex;
  669. n.m_transform = toAnkiMatrix(ainode->mTransformation);
  670. m_staticCollisionNodes.push_back(n);
  671. special = true;
  672. }
  673. else if(prop.first == "portal" && prop.second == "true")
  674. {
  675. Portal portal;
  676. portal.m_meshIndex = meshIndex;
  677. portal.m_transform = toAnkiMatrix(ainode->mTransformation);
  678. m_portals.push_back(portal);
  679. special = true;
  680. }
  681. else if(prop.first == "sector" && prop.second == "true")
  682. {
  683. Sector sector;
  684. sector.m_meshIndex = meshIndex;
  685. sector.m_transform = toAnkiMatrix(ainode->mTransformation);
  686. m_sectors.push_back(sector);
  687. special = true;
  688. }
  689. else if(prop.first == "lod1")
  690. {
  691. lod1MeshName = prop.second;
  692. special = false;
  693. }
  694. else if(prop.first == "reflection_probe" && prop.second == "true")
  695. {
  696. ReflectionProbe probe;
  697. aiMatrix4x4 trf = toAnkiMatrix(ainode->mTransformation);
  698. probe.m_position = aiVector3D(trf.a4, trf.b4, trf.c4);
  699. aiVector3D zAxis(trf.a3, trf.b3, trf.c3);
  700. float scale = zAxis.Length();
  701. probe.m_radius = scale;
  702. m_reflectionProbes.push_back(probe);
  703. special = true;
  704. }
  705. else if(prop.first == "reflection_proxy" && prop.second == "true")
  706. {
  707. ReflectionProxy proxy;
  708. // Find proxy in the other scene
  709. proxy.m_meshIndex = 0xFFFFFFFF;
  710. for(unsigned i = 0; i < m_sceneNoTriangles->mNumMeshes; ++i)
  711. {
  712. if(m_sceneNoTriangles->mMeshes[i]->mName
  713. == m_scene->mMeshes[meshIndex]->mName)
  714. {
  715. // Found
  716. proxy.m_meshIndex = i;
  717. break;
  718. }
  719. }
  720. if(proxy.m_meshIndex == 0xFFFFFFFF)
  721. {
  722. ERROR("Reflection proxy mesh not found");
  723. }
  724. proxy.m_transform = toAnkiMatrix(ainode->mTransformation);
  725. m_reflectionProxies.push_back(proxy);
  726. special = true;
  727. }
  728. else if(prop.first == "occluder" && prop.second == "true")
  729. {
  730. OccluderNode occluder;
  731. occluder.m_meshIndex = meshIndex;
  732. occluder.m_transform = toAnkiMatrix(ainode->mTransformation);
  733. m_occluders.push_back(occluder);
  734. special = true;
  735. }
  736. else if(prop.first == "collision_mesh")
  737. {
  738. collisionMesh = prop.second;
  739. special = false;
  740. }
  741. }
  742. if(special)
  743. {
  744. continue;
  745. }
  746. // Create new model
  747. Model mdl;
  748. mdl.m_meshIndex = meshIndex;
  749. mdl.m_materialIndex = mtlIndex;
  750. mdl.m_lod1MeshName = lod1MeshName;
  751. m_models.push_back(mdl);
  752. // Create new node
  753. Node node;
  754. node.m_modelIndex = m_models.size() - 1;
  755. node.m_transform = toAnkiMatrix(ainode->mTransformation);
  756. node.m_group = ainode->mGroup.C_Str();
  757. node.m_collisionMesh = collisionMesh;
  758. m_nodes.push_back(node);
  759. }
  760. // Go to children
  761. for(uint32_t i = 0; i < ainode->mNumChildren; i++)
  762. {
  763. visitNode(ainode->mChildren[i]);
  764. }
  765. }
  766. //==============================================================================
  767. void Exporter::exportCollisionMesh(uint32_t meshIdx)
  768. {
  769. std::string name = getMeshName(getMeshAt(meshIdx));
  770. std::fstream file;
  771. file.open(m_outputDirectory + name + ".ankicl", std::ios::out);
  772. file << XML_HEADER << '\n';
  773. // Write collision mesh
  774. file << "<collisionShape>\n\t<type>staticMesh</type>\n\t<value>" << m_rpath
  775. << name << ".ankimesh</value>\n</collisionShape>\n";
  776. }
  777. //==============================================================================
  778. void Exporter::exportAll()
  779. {
  780. LOGI("Exporting scene to %s", &m_outputDirectory[0]);
  781. //
  782. // Open scene file
  783. //
  784. m_sceneFile.open(m_outputDirectory + "scene.lua");
  785. std::ofstream& file = m_sceneFile;
  786. file << "local scene = getSceneGraph()\n"
  787. << "local events = getEventManager()\n"
  788. << "local rot\n"
  789. << "local node\n"
  790. << "local inst\n"
  791. << "local lcomp\n";
  792. //
  793. // Get all node/model data
  794. //
  795. visitNode(m_scene->mRootNode);
  796. //
  797. // Export collision meshes
  798. //
  799. for(auto n : m_staticCollisionNodes)
  800. {
  801. exportMesh(*m_scene->mMeshes[n.m_meshIndex], nullptr, 3);
  802. exportCollisionMesh(n.m_meshIndex);
  803. file << "\n";
  804. writeTransform(n.m_transform);
  805. std::string name = getMeshName(getMeshAt(n.m_meshIndex));
  806. std::string fname = m_rpath + name + ".ankicl";
  807. file << "node = scene:newStaticCollisionNode(\"" << name << "\", \""
  808. << fname << "\", trf)\n";
  809. }
  810. //
  811. // Export portals
  812. //
  813. unsigned i = 0;
  814. for(const Portal& portal : m_portals)
  815. {
  816. uint32_t meshIndex = portal.m_meshIndex;
  817. exportMesh(*m_scene->mMeshes[meshIndex], nullptr, 3);
  818. std::string name = getMeshName(getMeshAt(meshIndex));
  819. std::string fname = m_rpath + name + ".ankimesh";
  820. file << "\nnode = scene:newPortal(\"" << name << i << "\", \"" << fname
  821. << "\")\n";
  822. writeNodeTransform("node", portal.m_transform);
  823. ++i;
  824. }
  825. //
  826. // Export sectors
  827. //
  828. i = 0;
  829. for(const Sector& sector : m_sectors)
  830. {
  831. uint32_t meshIndex = sector.m_meshIndex;
  832. exportMesh(*m_scene->mMeshes[meshIndex], nullptr, 3);
  833. std::string name = getMeshName(getMeshAt(meshIndex));
  834. std::string fname = m_rpath + name + ".ankimesh";
  835. file << "\nnode = scene:newSector(\"" << name << i << "\", \"" << fname
  836. << "\")\n";
  837. writeNodeTransform("node", sector.m_transform);
  838. ++i;
  839. }
  840. //
  841. // Export particle emitters
  842. //
  843. i = 0;
  844. for(const ParticleEmitter& p : m_particleEmitters)
  845. {
  846. std::string name = "particles" + std::to_string(i);
  847. file << "\nnode = scene:newParticleEmitter(\"" << name << "\", \""
  848. << p.m_filename << "\")\n";
  849. writeNodeTransform("node", p.m_transform);
  850. ++i;
  851. }
  852. //
  853. // Export probes
  854. //
  855. i = 0;
  856. for(const ReflectionProbe& probe : m_reflectionProbes)
  857. {
  858. std::string name = "reflprobe" + std::to_string(i);
  859. file << "\nnode = scene:newReflectionProbe(\"" << name << "\", "
  860. << probe.m_radius << ")\n";
  861. aiMatrix4x4 trf;
  862. aiMatrix4x4::Translation(probe.m_position, trf);
  863. writeNodeTransform("node", trf);
  864. ++i;
  865. }
  866. //
  867. // Export proxies
  868. //
  869. i = 0;
  870. for(const ReflectionProxy& proxy : m_reflectionProxies)
  871. {
  872. const aiMesh& mesh = *m_sceneNoTriangles->mMeshes[proxy.m_meshIndex];
  873. exportMesh(mesh, nullptr, 4);
  874. std::string name = "reflproxy" + std::to_string(i);
  875. file << "\nnode = scene:newReflectionProxy(\"" << name << "\", \""
  876. << m_rpath << mesh.mName.C_Str() << ".ankimesh\")\n";
  877. writeNodeTransform("node", proxy.m_transform);
  878. ++i;
  879. }
  880. //
  881. // Export occluders
  882. //
  883. i = 0;
  884. for(const OccluderNode& occluder : m_occluders)
  885. {
  886. const aiMesh& mesh = *m_scene->mMeshes[occluder.m_meshIndex];
  887. exportMesh(mesh, nullptr, 3);
  888. std::string name = "occluder" + std::to_string(i);
  889. file << "\nnode = scene:newOccluderNode(\"" << name << "\", \""
  890. << m_rpath << mesh.mName.C_Str() << ".ankimesh\")\n";
  891. writeNodeTransform("node", occluder.m_transform);
  892. ++i;
  893. }
  894. //
  895. // Export nodes and models.
  896. //
  897. for(uint32_t i = 0; i < m_nodes.size(); i++)
  898. {
  899. Node& node = m_nodes[i];
  900. Model& model = m_models[node.m_modelIndex];
  901. // TODO If static bake transform
  902. exportMesh(*m_scene->mMeshes[model.m_meshIndex], nullptr, 3);
  903. exportMaterial(*m_scene->mMaterials[model.m_materialIndex]);
  904. exportModel(model);
  905. std::string modelName = getModelName(model);
  906. std::string nodeName = modelName + node.m_group + std::to_string(i);
  907. // Write the main node
  908. file << "\nnode = scene:newModelNode(\"" << nodeName << "\", \""
  909. << m_rpath << modelName << ".ankimdl"
  910. << "\")\n";
  911. writeNodeTransform("node", node.m_transform);
  912. // Write the collision node
  913. if(!node.m_collisionMesh.empty())
  914. {
  915. bool found = false;
  916. unsigned i = 0;
  917. for(; i < m_scene->mNumMeshes; i++)
  918. {
  919. if(m_scene->mMeshes[i]->mName.C_Str() == node.m_collisionMesh)
  920. {
  921. found = true;
  922. break;
  923. }
  924. }
  925. if(found)
  926. {
  927. exportCollisionMesh(i);
  928. std::string fname = m_rpath + node.m_collisionMesh + ".ankicl";
  929. file << "node = scene:newStaticCollisionNode(\"" << nodeName
  930. << "_cl"
  931. << "\", \"" << fname << "\", trf)\n";
  932. }
  933. else
  934. {
  935. ERROR("Couldn't find the collision_mesh %s",
  936. node.m_collisionMesh.c_str());
  937. }
  938. }
  939. }
  940. //
  941. // Lights
  942. //
  943. for(unsigned i = 0; i < m_scene->mNumLights; i++)
  944. {
  945. exportLight(*m_scene->mLights[i]);
  946. }
  947. //
  948. // Animations
  949. //
  950. for(unsigned i = 0; i < m_scene->mNumAnimations; i++)
  951. {
  952. exportAnimation(*m_scene->mAnimations[i], i);
  953. }
  954. //
  955. // Cameras
  956. //
  957. for(unsigned i = 0; i < m_scene->mNumCameras; i++)
  958. {
  959. exportCamera(*m_scene->mCameras[i]);
  960. }
  961. LOGI("Done exporting scene!");
  962. }