X3DExporter.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. /// \file X3DExporter.cpp
  2. /// \brief X3D-format files exporter for Assimp. Implementation.
  3. /// \date 2016
  4. /// \author [email protected]
  5. #ifndef ASSIMP_BUILD_NO_EXPORT
  6. #ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
  7. #include "X3DExporter.hpp"
  8. // Header files, Assimp.
  9. #include "Exceptional.h"
  10. #include "StringUtils.h"
  11. #include <assimp/Exporter.hpp>
  12. #include <assimp/IOSystem.hpp>
  13. using namespace std;
  14. namespace Assimp
  15. {
  16. void ExportSceneX3D(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
  17. {
  18. X3DExporter exporter(pFile, pIOSystem, pScene, pProperties);
  19. }
  20. }// namespace Assimp
  21. namespace Assimp
  22. {
  23. void X3DExporter::IndentationStringSet(const size_t pNewLevel)
  24. {
  25. if(pNewLevel > mIndentationString.size())
  26. {
  27. if(pNewLevel > mIndentationString.capacity()) mIndentationString.reserve(pNewLevel + 1);
  28. for(size_t i = 0, i_e = pNewLevel - mIndentationString.size(); i < i_e; i++) mIndentationString.push_back('\t');
  29. }
  30. else if(pNewLevel < mIndentationString.size())
  31. {
  32. mIndentationString.resize(pNewLevel);
  33. }
  34. }
  35. void X3DExporter::XML_Write(const string& pData)
  36. {
  37. if(pData.size() == 0) return;
  38. if(mOutFile->Write((void*)pData.data(), pData.length(), 1) != 1) throw DeadlyExportError("Failed to write scene data!");
  39. }
  40. aiMatrix4x4 X3DExporter::Matrix_GlobalToCurrent(const aiNode& pNode) const
  41. {
  42. aiNode* cur_node;
  43. std::list<aiMatrix4x4> matr;
  44. aiMatrix4x4 out_matr;
  45. // starting walk from current element to root
  46. matr.push_back(pNode.mTransformation);
  47. cur_node = pNode.mParent;
  48. if(cur_node != nullptr)
  49. {
  50. do
  51. {
  52. matr.push_back(cur_node->mTransformation);
  53. cur_node = cur_node->mParent;
  54. } while(cur_node != nullptr);
  55. }
  56. // multiplicate all matrices in reverse order
  57. for(std::list<aiMatrix4x4>::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); rit++) out_matr = out_matr * (*rit);
  58. return out_matr;
  59. }
  60. void X3DExporter::AttrHelper_FloatToString(const float pValue, std::string& pTargetString)
  61. {
  62. pTargetString = to_string(pValue);
  63. AttrHelper_CommaToPoint(pTargetString);
  64. }
  65. void X3DExporter::AttrHelper_Vec3DArrToString(const aiVector3D* pArray, const size_t pArray_Size, string& pTargetString)
  66. {
  67. pTargetString.clear();
  68. pTargetString.reserve(pArray_Size * 6);// (Number + space) * 3.
  69. for(size_t idx = 0; idx < pArray_Size; idx++)
  70. pTargetString.append(to_string(pArray[idx].x) + " " + to_string(pArray[idx].y) + " " + to_string(pArray[idx].z) + " ");
  71. // remove last space symbol.
  72. pTargetString.resize(pTargetString.length() - 1);
  73. AttrHelper_CommaToPoint(pTargetString);
  74. }
  75. void X3DExporter::AttrHelper_Vec2DArrToString(const aiVector2D* pArray, const size_t pArray_Size, std::string& pTargetString)
  76. {
  77. pTargetString.clear();
  78. pTargetString.reserve(pArray_Size * 4);// (Number + space) * 2.
  79. for(size_t idx = 0; idx < pArray_Size; idx++)
  80. pTargetString.append(to_string(pArray[idx].x) + " " + to_string(pArray[idx].y) + " ");
  81. // remove last space symbol.
  82. pTargetString.resize(pTargetString.length() - 1);
  83. AttrHelper_CommaToPoint(pTargetString);
  84. }
  85. void X3DExporter::AttrHelper_Vec3DAsVec2fArrToString(const aiVector3D* pArray, const size_t pArray_Size, string& pTargetString)
  86. {
  87. pTargetString.clear();
  88. pTargetString.reserve(pArray_Size * 4);// (Number + space) * 2.
  89. for(size_t idx = 0; idx < pArray_Size; idx++)
  90. pTargetString.append(to_string(pArray[idx].x) + " " + to_string(pArray[idx].y) + " ");
  91. // remove last space symbol.
  92. pTargetString.resize(pTargetString.length() - 1);
  93. AttrHelper_CommaToPoint(pTargetString);
  94. }
  95. void X3DExporter::AttrHelper_Col4DArrToString(const aiColor4D* pArray, const size_t pArray_Size, string& pTargetString)
  96. {
  97. pTargetString.clear();
  98. pTargetString.reserve(pArray_Size * 8);// (Number + space) * 4.
  99. for(size_t idx = 0; idx < pArray_Size; idx++)
  100. pTargetString.append(to_string(pArray[idx].r) + " " + to_string(pArray[idx].g) + " " + to_string(pArray[idx].b) + " " +
  101. to_string(pArray[idx].a) + " ");
  102. // remove last space symbol.
  103. pTargetString.resize(pTargetString.length() - 1);
  104. AttrHelper_CommaToPoint(pTargetString);
  105. }
  106. void X3DExporter::AttrHelper_Col3DArrToString(const aiColor3D* pArray, const size_t pArray_Size, std::string& pTargetString)
  107. {
  108. pTargetString.clear();
  109. pTargetString.reserve(pArray_Size * 6);// (Number + space) * 3.
  110. for(size_t idx = 0; idx < pArray_Size; idx++)
  111. pTargetString.append(to_string(pArray[idx].r) + " " + to_string(pArray[idx].g) + " " + to_string(pArray[idx].b) + " ");
  112. // remove last space symbol.
  113. pTargetString.resize(pTargetString.length() - 1);
  114. AttrHelper_CommaToPoint(pTargetString);
  115. }
  116. void X3DExporter::AttrHelper_Color3ToAttrList(std::list<SAttribute>& pList, const std::string& pName, const aiColor3D& pValue, const aiColor3D& pDefaultValue)
  117. {
  118. string tstr;
  119. if(pValue == pDefaultValue) return;
  120. AttrHelper_Col3DArrToString(&pValue, 1, tstr);
  121. pList.push_back({pName, tstr});
  122. }
  123. void X3DExporter::AttrHelper_FloatToAttrList(std::list<SAttribute>& pList, const string& pName, const float pValue, const float pDefaultValue)
  124. {
  125. string tstr;
  126. if(pValue == pDefaultValue) return;
  127. AttrHelper_FloatToString(pValue, tstr);
  128. pList.push_back({pName, tstr});
  129. }
  130. void X3DExporter::NodeHelper_OpenNode(const string& pNodeName, const size_t pTabLevel, const bool pEmptyElement, const list<SAttribute>& pAttrList)
  131. {
  132. // Write indentation.
  133. IndentationStringSet(pTabLevel);
  134. XML_Write(mIndentationString);
  135. // Begin of the element
  136. XML_Write("<" + pNodeName);
  137. // Write attributes
  138. for(const SAttribute& attr: pAttrList) { XML_Write(" " + attr.Name + "='" + attr.Value + "'"); }
  139. // End of the element
  140. if(pEmptyElement)
  141. {
  142. XML_Write("/>\n");
  143. }
  144. else
  145. {
  146. XML_Write(">\n");
  147. }
  148. }
  149. void X3DExporter::NodeHelper_OpenNode(const string& pNodeName, const size_t pTabLevel, const bool pEmptyElement)
  150. {
  151. const list<SAttribute> attr_list;
  152. NodeHelper_OpenNode(pNodeName, pTabLevel, pEmptyElement, attr_list);
  153. }
  154. void X3DExporter::NodeHelper_CloseNode(const string& pNodeName, const size_t pTabLevel)
  155. {
  156. // Write indentation.
  157. IndentationStringSet(pTabLevel);
  158. XML_Write(mIndentationString);
  159. // Write element
  160. XML_Write("</" + pNodeName + ">\n");
  161. }
  162. void X3DExporter::Export_Node(const aiNode *pNode, const size_t pTabLevel)
  163. {
  164. bool transform = false;
  165. list<SAttribute> attr_list;
  166. // In Assimp lights is stored in next way: light source store in mScene->mLights and in node tree must present aiNode with name same as
  167. // light source has. Considering it we must compare every aiNode name with light sources names. Why not to look where ligths is present
  168. // and save them to fili? Because corresponding aiNode can be already written to file and we can only add information to file not to edit.
  169. if(CheckAndExport_Light(*pNode, pTabLevel)) return;
  170. // Check if need DEF.
  171. if(pNode->mName.length) attr_list.push_back({"DEF", pNode->mName.C_Str()});
  172. // Check if need <Transformation> node against <Group>.
  173. if(!pNode->mTransformation.IsIdentity())
  174. {
  175. auto Vector2String = [this](const aiVector3D pVector) -> string
  176. {
  177. string tstr = to_string(pVector.x) + " " + to_string(pVector.y) + " " + to_string(pVector.z);
  178. AttrHelper_CommaToPoint(tstr);
  179. return tstr;
  180. };
  181. auto Rotation2String = [this](const aiVector3D pAxis, const ai_real pAngle) -> string
  182. {
  183. string tstr = to_string(pAxis.x) + " " + to_string(pAxis.y) + " " + to_string(pAxis.z) + " " + to_string(pAngle);
  184. AttrHelper_CommaToPoint(tstr);
  185. return tstr;
  186. };
  187. aiVector3D scale, translate, rotate_axis;
  188. ai_real rotate_angle;
  189. transform = true;
  190. pNode->mTransformation.Decompose(scale, rotate_axis, rotate_angle, translate);
  191. // Check if values different from default
  192. if((rotate_angle != 0) && (rotate_axis.Length() > 0))
  193. attr_list.push_back({"rotation", Rotation2String(rotate_axis, rotate_angle)});
  194. if(!scale.Equal({1.0,1.0,1.0})) {
  195. attr_list.push_back({"scale", Vector2String(scale)});
  196. }
  197. if(translate.Length() > 0) {
  198. attr_list.push_back({"translation", Vector2String(translate)});
  199. }
  200. }
  201. // Begin node if need.
  202. if(transform)
  203. NodeHelper_OpenNode("Transform", pTabLevel, false, attr_list);
  204. else
  205. NodeHelper_OpenNode("Group", pTabLevel);
  206. // Export metadata
  207. if(pNode->mMetaData != nullptr)
  208. {
  209. for(size_t idx_prop = 0; idx_prop < pNode->mMetaData->mNumProperties; idx_prop++)
  210. {
  211. const aiString* key;
  212. const aiMetadataEntry* entry;
  213. if(pNode->mMetaData->Get(idx_prop, key, entry))
  214. {
  215. switch(entry->mType)
  216. {
  217. case AI_BOOL:
  218. Export_MetadataBoolean(*key, *static_cast<bool*>(entry->mData), pTabLevel + 1);
  219. break;
  220. case AI_DOUBLE:
  221. Export_MetadataDouble(*key, *static_cast<double*>(entry->mData), pTabLevel + 1);
  222. break;
  223. case AI_FLOAT:
  224. Export_MetadataFloat(*key, *static_cast<float*>(entry->mData), pTabLevel + 1);
  225. break;
  226. case AI_INT32:
  227. Export_MetadataInteger(*key, *static_cast<int32_t*>(entry->mData), pTabLevel + 1);
  228. break;
  229. case AI_AISTRING:
  230. Export_MetadataString(*key, *static_cast<aiString*>(entry->mData), pTabLevel + 1);
  231. break;
  232. default:
  233. LogError("Unsupported metadata type: " + to_string(entry->mType));
  234. break;
  235. }// switch(entry->mType)
  236. }
  237. }
  238. }// if(pNode->mMetaData != nullptr)
  239. // Export meshes.
  240. for(size_t idx_mesh = 0; idx_mesh < pNode->mNumMeshes; idx_mesh++) Export_Mesh(pNode->mMeshes[idx_mesh], pTabLevel + 1);
  241. // Export children.
  242. for(size_t idx_node = 0; idx_node < pNode->mNumChildren; idx_node++) Export_Node(pNode->mChildren[idx_node], pTabLevel + 1);
  243. // End node if need.
  244. if(transform)
  245. NodeHelper_CloseNode("Transform", pTabLevel);
  246. else
  247. NodeHelper_CloseNode("Group", pTabLevel);
  248. }
  249. void X3DExporter::Export_Mesh(const size_t pIdxMesh, const size_t pTabLevel)
  250. {
  251. const char* NodeName_IFS = "IndexedFaceSet";
  252. const char* NodeName_Shape = "Shape";
  253. list<SAttribute> attr_list;
  254. aiMesh& mesh = *mScene->mMeshes[pIdxMesh];// create alias for conveniance.
  255. // Check if mesh already defined early.
  256. if(mDEF_Map_Mesh.find(pIdxMesh) != mDEF_Map_Mesh.end())
  257. {
  258. // Mesh already defined, just refer to it
  259. attr_list.push_back({"USE", mDEF_Map_Mesh.at(pIdxMesh)});
  260. NodeHelper_OpenNode(NodeName_Shape, pTabLevel, true, attr_list);
  261. return;
  262. }
  263. string mesh_name(mesh.mName.C_Str() + string("_IDX_") + to_string(pIdxMesh));// Create mesh name
  264. // Define mesh name.
  265. attr_list.push_back({"DEF", mesh_name});
  266. mDEF_Map_Mesh[pIdxMesh] = mesh_name;
  267. //
  268. // "Shape" node.
  269. //
  270. NodeHelper_OpenNode(NodeName_Shape, pTabLevel, false, attr_list);
  271. attr_list.clear();
  272. //
  273. // "Appearance" node.
  274. //
  275. Export_Material(mesh.mMaterialIndex, pTabLevel + 1);
  276. //
  277. // "IndexedFaceSet" node.
  278. //
  279. // Fill attributes which differ from default. In Assimp for colors, vertices and normals used one indices set. So, only "coordIndex" must be set.
  280. string coordIndex;
  281. // fill coordinates index.
  282. coordIndex.reserve(mesh.mNumVertices * 4);// Index + space + Face delimiter
  283. for(size_t idx_face = 0; idx_face < mesh.mNumFaces; idx_face++)
  284. {
  285. const aiFace& face_cur = mesh.mFaces[idx_face];
  286. for(size_t idx_vert = 0; idx_vert < face_cur.mNumIndices; idx_vert++)
  287. {
  288. coordIndex.append(to_string(face_cur.mIndices[idx_vert]) + " ");
  289. }
  290. coordIndex.append("-1 ");// face delimiter.
  291. }
  292. // remove last space symbol.
  293. coordIndex.resize(coordIndex.length() - 1);
  294. attr_list.push_back({"coordIndex", coordIndex});
  295. // create node
  296. NodeHelper_OpenNode(NodeName_IFS, pTabLevel + 1, false, attr_list);
  297. attr_list.clear();
  298. // Child nodes for "IndexedFaceSet" needed when used colors, textures or normals.
  299. string attr_value;
  300. // Export <Coordinate>
  301. AttrHelper_Vec3DArrToString(mesh.mVertices, mesh.mNumVertices, attr_value);
  302. attr_list.push_back({"point", attr_value});
  303. NodeHelper_OpenNode("Coordinate", pTabLevel + 2, true, attr_list);
  304. attr_list.clear();
  305. // Export <ColorRGBA>
  306. if(mesh.HasVertexColors(0))
  307. {
  308. AttrHelper_Col4DArrToString(mesh.mColors[0], mesh.mNumVertices, attr_value);
  309. attr_list.push_back({"color", attr_value});
  310. NodeHelper_OpenNode("ColorRGBA", pTabLevel + 2, true, attr_list);
  311. attr_list.clear();
  312. }
  313. // Export <TextureCoordinate>
  314. if(mesh.HasTextureCoords(0))
  315. {
  316. AttrHelper_Vec3DAsVec2fArrToString(mesh.mTextureCoords[0], mesh.mNumVertices, attr_value);
  317. attr_list.push_back({"point", attr_value});
  318. NodeHelper_OpenNode("TextureCoordinate", pTabLevel + 2, true, attr_list);
  319. attr_list.clear();
  320. }
  321. // Export <Normal>
  322. if(mesh.HasNormals())
  323. {
  324. AttrHelper_Vec3DArrToString(mesh.mNormals, mesh.mNumVertices, attr_value);
  325. attr_list.push_back({"vector", attr_value});
  326. NodeHelper_OpenNode("Normal", pTabLevel + 2, true, attr_list);
  327. attr_list.clear();
  328. }
  329. //
  330. // Close opened nodes.
  331. //
  332. NodeHelper_CloseNode(NodeName_IFS, pTabLevel + 1);
  333. NodeHelper_CloseNode(NodeName_Shape, pTabLevel);
  334. }
  335. void X3DExporter::Export_Material(const size_t pIdxMaterial, const size_t pTabLevel)
  336. {
  337. const char* NodeName_A = "Appearance";
  338. list<SAttribute> attr_list;
  339. aiMaterial& material = *mScene->mMaterials[pIdxMaterial];// create alias for conveniance.
  340. // Check if material already defined early.
  341. if(mDEF_Map_Material.find(pIdxMaterial) != mDEF_Map_Material.end())
  342. {
  343. // Material already defined, just refer to it
  344. attr_list.push_back({"USE", mDEF_Map_Material.at(pIdxMaterial)});
  345. NodeHelper_OpenNode(NodeName_A, pTabLevel, true, attr_list);
  346. return;
  347. }
  348. string material_name(string("_IDX_") + to_string(pIdxMaterial));// Create material name
  349. aiString ai_mat_name;
  350. if(material.Get(AI_MATKEY_NAME, ai_mat_name) == AI_SUCCESS) material_name.insert(0, ai_mat_name.C_Str());
  351. // Define material name.
  352. attr_list.push_back({"DEF", material_name});
  353. mDEF_Map_Material[pIdxMaterial] = material_name;
  354. //
  355. // "Appearance" node.
  356. //
  357. NodeHelper_OpenNode(NodeName_A, pTabLevel, false, attr_list);
  358. attr_list.clear();
  359. //
  360. // "Material" node.
  361. //
  362. {
  363. auto Color4ToAttrList = [&](const string& pAttrName, const aiColor4D& pAttrValue, const aiColor3D& pAttrDefaultValue)
  364. {
  365. string tstr;
  366. if(aiColor3D(pAttrValue.r, pAttrValue.g, pAttrValue.b) != pAttrDefaultValue)
  367. {
  368. AttrHelper_Col4DArrToString(&pAttrValue, 1, tstr);
  369. attr_list.push_back({pAttrName, tstr});
  370. }
  371. };
  372. float tvalf;
  373. aiColor3D color3;
  374. aiColor4D color4;
  375. // ambientIntensity="0.2" SFFloat [inputOutput]
  376. if(material.Get(AI_MATKEY_COLOR_AMBIENT, color3) == AI_SUCCESS)
  377. AttrHelper_FloatToAttrList(attr_list, "ambientIntensity", (color3.r + color3.g + color3.b) / 3.0f, 0.2f);
  378. else if(material.Get(AI_MATKEY_COLOR_AMBIENT, color4) == AI_SUCCESS)
  379. AttrHelper_FloatToAttrList(attr_list, "ambientIntensity", (color4.r + color4.g + color4.b) / 3.0f, 0.2f);
  380. // diffuseColor="0.8 0.8 0.8" SFColor [inputOutput]
  381. if(material.Get(AI_MATKEY_COLOR_DIFFUSE, color3) == AI_SUCCESS)
  382. AttrHelper_Color3ToAttrList(attr_list, "diffuseColor", color3, aiColor3D(0.8f, 0.8f, 0.8f));
  383. else if(material.Get(AI_MATKEY_COLOR_DIFFUSE, color4) == AI_SUCCESS)
  384. Color4ToAttrList("diffuseColor", color4, aiColor3D(0.8f, 0.8f, 0.8f));
  385. // emissiveColor="0 0 0" SFColor [inputOutput]
  386. if(material.Get(AI_MATKEY_COLOR_EMISSIVE, color3) == AI_SUCCESS)
  387. AttrHelper_Color3ToAttrList(attr_list, "emissiveColor", color3, aiColor3D(0, 0, 0));
  388. else if(material.Get(AI_MATKEY_COLOR_EMISSIVE, color4) == AI_SUCCESS)
  389. Color4ToAttrList("emissiveColor", color4, aiColor3D(0, 0, 0));
  390. // shininess="0.2" SFFloat [inputOutput]
  391. if(material.Get(AI_MATKEY_SHININESS, tvalf) == AI_SUCCESS) AttrHelper_FloatToAttrList(attr_list, "shininess", tvalf, 0.2f);
  392. // specularColor="0 0 0" SFColor [inputOutput]
  393. if(material.Get(AI_MATKEY_COLOR_SPECULAR, color3) == AI_SUCCESS)
  394. AttrHelper_Color3ToAttrList(attr_list, "specularColor", color3, aiColor3D(0, 0, 0));
  395. else if(material.Get(AI_MATKEY_COLOR_SPECULAR, color4) == AI_SUCCESS)
  396. Color4ToAttrList("specularColor", color4, aiColor3D(0, 0, 0));
  397. // transparency="0" SFFloat [inputOutput]
  398. if(material.Get(AI_MATKEY_OPACITY, tvalf) == AI_SUCCESS)
  399. {
  400. if(tvalf > 1) tvalf = 1;
  401. tvalf = 1.0f - tvalf;
  402. AttrHelper_FloatToAttrList(attr_list, "transparency", tvalf, 0);
  403. }
  404. NodeHelper_OpenNode("Material", pTabLevel + 1, true, attr_list);
  405. attr_list.clear();
  406. }// "Material" node. END.
  407. //
  408. // "ImageTexture" node.
  409. //
  410. {
  411. auto RepeatToAttrList = [&](const string& pAttrName, const bool pAttrValue)
  412. {
  413. if(!pAttrValue) attr_list.push_back({pAttrName, "false"});
  414. };
  415. bool tvalb;
  416. aiString tstring;
  417. // url="" MFString
  418. if(material.Get(AI_MATKEY_TEXTURE_DIFFUSE(0), tstring) == AI_SUCCESS)
  419. {
  420. if(strncmp(tstring.C_Str(), AI_EMBEDDED_TEXNAME_PREFIX, strlen(AI_EMBEDDED_TEXNAME_PREFIX)) == 0)
  421. LogError("Embedded texture is not supported");
  422. else
  423. attr_list.push_back({"url", string("\"") + tstring.C_Str() + "\""});
  424. }
  425. // repeatS="true" SFBool
  426. if(material.Get(AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0), tvalb) == AI_SUCCESS) RepeatToAttrList("repeatS", tvalb);
  427. // repeatT="true" SFBool
  428. if(material.Get(AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0), tvalb) == AI_SUCCESS) RepeatToAttrList("repeatT", tvalb);
  429. NodeHelper_OpenNode("ImageTexture", pTabLevel + 1, true, attr_list);
  430. attr_list.clear();
  431. }// "ImageTexture" node. END.
  432. //
  433. // "TextureTransform" node.
  434. //
  435. {
  436. auto Vec2ToAttrList = [&](const string& pAttrName, const aiVector2D& pAttrValue, const aiVector2D& pAttrDefaultValue)
  437. {
  438. string tstr;
  439. if(pAttrValue != pAttrDefaultValue)
  440. {
  441. AttrHelper_Vec2DArrToString(&pAttrValue, 1, tstr);
  442. attr_list.push_back({pAttrName, tstr});
  443. }
  444. };
  445. aiUVTransform transform;
  446. if(material.Get(AI_MATKEY_UVTRANSFORM_DIFFUSE(0), transform) == AI_SUCCESS)
  447. {
  448. Vec2ToAttrList("translation", transform.mTranslation, aiVector2D(0, 0));
  449. AttrHelper_FloatToAttrList(attr_list, "rotation", transform.mRotation, 0);
  450. Vec2ToAttrList("scale", transform.mScaling, aiVector2D(1, 1));
  451. NodeHelper_OpenNode("TextureTransform", pTabLevel + 1, true, attr_list);
  452. attr_list.clear();
  453. }
  454. }// "TextureTransform" node. END.
  455. //
  456. // Close opened nodes.
  457. //
  458. NodeHelper_CloseNode(NodeName_A, pTabLevel);
  459. }
  460. void X3DExporter::Export_MetadataBoolean(const aiString& pKey, const bool pValue, const size_t pTabLevel)
  461. {
  462. list<SAttribute> attr_list;
  463. attr_list.push_back({"name", pKey.C_Str()});
  464. attr_list.push_back({"value", pValue ? "true" : "false"});
  465. NodeHelper_OpenNode("MetadataBoolean", pTabLevel, true, attr_list);
  466. }
  467. void X3DExporter::Export_MetadataDouble(const aiString& pKey, const double pValue, const size_t pTabLevel)
  468. {
  469. list<SAttribute> attr_list;
  470. attr_list.push_back({"name", pKey.C_Str()});
  471. attr_list.push_back({"value", to_string(pValue)});
  472. NodeHelper_OpenNode("MetadataDouble", pTabLevel, true, attr_list);
  473. }
  474. void X3DExporter::Export_MetadataFloat(const aiString& pKey, const float pValue, const size_t pTabLevel)
  475. {
  476. list<SAttribute> attr_list;
  477. attr_list.push_back({"name", pKey.C_Str()});
  478. attr_list.push_back({"value", to_string(pValue)});
  479. NodeHelper_OpenNode("MetadataFloat", pTabLevel, true, attr_list);
  480. }
  481. void X3DExporter::Export_MetadataInteger(const aiString& pKey, const int32_t pValue, const size_t pTabLevel)
  482. {
  483. list<SAttribute> attr_list;
  484. attr_list.push_back({"name", pKey.C_Str()});
  485. attr_list.push_back({"value", to_string(pValue)});
  486. NodeHelper_OpenNode("MetadataInteger", pTabLevel, true, attr_list);
  487. }
  488. void X3DExporter::Export_MetadataString(const aiString& pKey, const aiString& pValue, const size_t pTabLevel)
  489. {
  490. list<SAttribute> attr_list;
  491. attr_list.push_back({"name", pKey.C_Str()});
  492. attr_list.push_back({"value", pValue.C_Str()});
  493. NodeHelper_OpenNode("MetadataString", pTabLevel, true, attr_list);
  494. }
  495. bool X3DExporter::CheckAndExport_Light(const aiNode& pNode, const size_t pTabLevel)
  496. {
  497. list<SAttribute> attr_list;
  498. auto Vec3ToAttrList = [&](const string& pAttrName, const aiVector3D& pAttrValue, const aiVector3D& pAttrDefaultValue)
  499. {
  500. string tstr;
  501. if(pAttrValue != pAttrDefaultValue)
  502. {
  503. AttrHelper_Vec3DArrToString(&pAttrValue, 1, tstr);
  504. attr_list.push_back({pAttrName, tstr});
  505. }
  506. };
  507. size_t idx_light;
  508. bool found = false;
  509. // Name of the light source can not be empty.
  510. if(pNode.mName.length == 0) return false;
  511. // search for light with name like node has.
  512. for(idx_light = 0; mScene->mNumLights; idx_light++)
  513. {
  514. if(pNode.mName == mScene->mLights[idx_light]->mName)
  515. {
  516. found = true;
  517. break;
  518. }
  519. }
  520. if(!found) return false;
  521. // Light source is found.
  522. const aiLight& light = *mScene->mLights[idx_light];// Alias for conveniance.
  523. aiMatrix4x4 trafo_mat = Matrix_GlobalToCurrent(pNode).Inverse();
  524. attr_list.push_back({"DEF", light.mName.C_Str()});
  525. attr_list.push_back({"global", "true"});// "false" is not supported.
  526. // ambientIntensity="0" SFFloat [inputOutput]
  527. AttrHelper_FloatToAttrList(attr_list, "ambientIntensity", aiVector3D(light.mColorAmbient.r, light.mColorAmbient.g, light.mColorAmbient.b).Length(), 0);
  528. // color="1 1 1" SFColor [inputOutput]
  529. AttrHelper_Color3ToAttrList(attr_list, "color", light.mColorDiffuse, aiColor3D(1, 1, 1));
  530. switch(light.mType)
  531. {
  532. case aiLightSource_DIRECTIONAL:
  533. {
  534. aiVector3D direction = trafo_mat * light.mDirection;
  535. Vec3ToAttrList("direction", direction, aiVector3D(0, 0, -1));
  536. NodeHelper_OpenNode("DirectionalLight", pTabLevel, true, attr_list);
  537. }
  538. break;
  539. case aiLightSource_POINT:
  540. {
  541. aiVector3D attenuation(light.mAttenuationConstant, light.mAttenuationLinear, light.mAttenuationQuadratic);
  542. aiVector3D location = trafo_mat * light.mPosition;
  543. Vec3ToAttrList("attenuation", attenuation, aiVector3D(1, 0, 0));
  544. Vec3ToAttrList("location", location, aiVector3D(0, 0, 0));
  545. NodeHelper_OpenNode("PointLight", pTabLevel, true, attr_list);
  546. }
  547. break;
  548. case aiLightSource_SPOT:
  549. {
  550. aiVector3D attenuation(light.mAttenuationConstant, light.mAttenuationLinear, light.mAttenuationQuadratic);
  551. aiVector3D location = trafo_mat * light.mPosition;
  552. aiVector3D direction = trafo_mat * light.mDirection;
  553. Vec3ToAttrList("attenuation", attenuation, aiVector3D(1, 0, 0));
  554. Vec3ToAttrList("location", location, aiVector3D(0, 0, 0));
  555. Vec3ToAttrList("direction", direction, aiVector3D(0, 0, -1));
  556. AttrHelper_FloatToAttrList(attr_list, "beamWidth", light.mAngleInnerCone, 0.7854f);
  557. AttrHelper_FloatToAttrList(attr_list, "cutOffAngle", light.mAngleOuterCone, 1.570796f);
  558. NodeHelper_OpenNode("SpotLight", pTabLevel, true, attr_list);
  559. }
  560. break;
  561. default:
  562. throw DeadlyExportError("Unknown light type: " + to_string(light.mType));
  563. }// switch(light.mType)
  564. return true;
  565. }
  566. X3DExporter::X3DExporter(const char* pFileName, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
  567. : mScene(pScene)
  568. {
  569. list<SAttribute> attr_list;
  570. mOutFile = pIOSystem->Open(pFileName, "wt");
  571. if(mOutFile == nullptr) throw DeadlyExportError("Could not open output .x3d file: " + string(pFileName));
  572. // Begin document
  573. XML_Write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  574. XML_Write("<!DOCTYPE X3D PUBLIC \"ISO//Web3D//DTD X3D 3.3//EN\" \"http://www.web3d.org/specifications/x3d-3.3.dtd\">\n");
  575. // Root node
  576. attr_list.push_back({"profile", "Interchange"});
  577. attr_list.push_back({"version", "3.3"});
  578. attr_list.push_back({"xmlns:xsd", "http://www.w3.org/2001/XMLSchema-instance"});
  579. attr_list.push_back({"xsd:noNamespaceSchemaLocation", "http://www.web3d.org/specifications/x3d-3.3.xsd"});
  580. NodeHelper_OpenNode("X3D", 0, false, attr_list);
  581. attr_list.clear();
  582. // <head>: meta data.
  583. NodeHelper_OpenNode("head", 1);
  584. XML_Write(mIndentationString + "<!-- All \"meta\" from this section tou will found in <Scene> node as MetadataString nodes. -->\n");
  585. NodeHelper_CloseNode("head", 1);
  586. // Scene node.
  587. NodeHelper_OpenNode("Scene", 1);
  588. Export_Node(mScene->mRootNode, 2);
  589. NodeHelper_CloseNode("Scene", 1);
  590. // Close Root node.
  591. NodeHelper_CloseNode("X3D", 0);
  592. // Cleanup
  593. pIOSystem->Close(mOutFile);
  594. mOutFile = nullptr;
  595. }
  596. }// namespace Assimp
  597. #endif // ASSIMP_BUILD_NO_X3D_EXPORTER
  598. #endif // ASSIMP_BUILD_NO_EXPORT