Exporter.cpp 27 KB

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