Exporter.cpp 26 KB

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