Exporter.cpp 28 KB

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