X3DExporter.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  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, 1, 1})) attr_list.push_back({"scale", Vector2String(scale)});
  195. if(translate.Length() > 0) attr_list.push_back({"translation", Vector2String(translate)});
  196. }
  197. // Begin node if need.
  198. if(transform)
  199. NodeHelper_OpenNode("Transform", pTabLevel, false, attr_list);
  200. else
  201. NodeHelper_OpenNode("Group", pTabLevel);
  202. // Export metadata
  203. if(pNode->mMetaData != nullptr)
  204. {
  205. for(size_t idx_prop = 0; idx_prop < pNode->mMetaData->mNumProperties; idx_prop++)
  206. {
  207. const aiString* key;
  208. const aiMetadataEntry* entry;
  209. if(pNode->mMetaData->Get(idx_prop, key, entry))
  210. {
  211. switch(entry->mType)
  212. {
  213. case AI_BOOL:
  214. Export_MetadataBoolean(*key, *static_cast<bool*>(entry->mData), pTabLevel + 1);
  215. break;
  216. case AI_DOUBLE:
  217. Export_MetadataDouble(*key, *static_cast<double*>(entry->mData), pTabLevel + 1);
  218. break;
  219. case AI_FLOAT:
  220. Export_MetadataFloat(*key, *static_cast<float*>(entry->mData), pTabLevel + 1);
  221. break;
  222. case AI_INT32:
  223. Export_MetadataInteger(*key, *static_cast<int32_t*>(entry->mData), pTabLevel + 1);
  224. break;
  225. case AI_AISTRING:
  226. Export_MetadataString(*key, *static_cast<aiString*>(entry->mData), pTabLevel + 1);
  227. break;
  228. default:
  229. LogError("Unsupported metadata type: " + to_string(entry->mType));
  230. break;
  231. }// switch(entry->mType)
  232. }
  233. }
  234. }// if(pNode->mMetaData != nullptr)
  235. // Export meshes.
  236. for(size_t idx_mesh = 0; idx_mesh < pNode->mNumMeshes; idx_mesh++) Export_Mesh(pNode->mMeshes[idx_mesh], pTabLevel + 1);
  237. // Export children.
  238. for(size_t idx_node = 0; idx_node < pNode->mNumChildren; idx_node++) Export_Node(pNode->mChildren[idx_node], pTabLevel + 1);
  239. // End node if need.
  240. if(transform)
  241. NodeHelper_CloseNode("Transform", pTabLevel);
  242. else
  243. NodeHelper_CloseNode("Group", pTabLevel);
  244. }
  245. void X3DExporter::Export_Mesh(const size_t pIdxMesh, const size_t pTabLevel)
  246. {
  247. const char* NodeName_IFS = "IndexedFaceSet";
  248. const char* NodeName_Shape = "Shape";
  249. list<SAttribute> attr_list;
  250. aiMesh& mesh = *mScene->mMeshes[pIdxMesh];// create alias for conveniance.
  251. // Check if mesh already defined early.
  252. if(mDEF_Map_Mesh.find(pIdxMesh) != mDEF_Map_Mesh.end())
  253. {
  254. // Mesh already defined, just refer to it
  255. attr_list.push_back({"USE", mDEF_Map_Mesh.at(pIdxMesh)});
  256. NodeHelper_OpenNode(NodeName_Shape, pTabLevel, true, attr_list);
  257. return;
  258. }
  259. string mesh_name(mesh.mName.C_Str() + string("_IDX_") + to_string(pIdxMesh));// Create mesh name
  260. // Define mesh name.
  261. attr_list.push_back({"DEF", mesh_name});
  262. mDEF_Map_Mesh[pIdxMesh] = mesh_name;
  263. //
  264. // "Shape" node.
  265. //
  266. NodeHelper_OpenNode(NodeName_Shape, pTabLevel, false, attr_list);
  267. attr_list.clear();
  268. //
  269. // "Appearance" node.
  270. //
  271. Export_Material(mesh.mMaterialIndex, pTabLevel + 1);
  272. //
  273. // "IndexedFaceSet" node.
  274. //
  275. // Fill attributes which differ from default. In Assimp for colors, vertices and normals used one indices set. So, only "coordIndex" must be set.
  276. string coordIndex;
  277. // fill coordinates index.
  278. coordIndex.reserve(mesh.mNumVertices * 4);// Index + space + Face delimiter
  279. for(size_t idx_face = 0; idx_face < mesh.mNumFaces; idx_face++)
  280. {
  281. const aiFace& face_cur = mesh.mFaces[idx_face];
  282. for(size_t idx_vert = 0; idx_vert < face_cur.mNumIndices; idx_vert++)
  283. {
  284. coordIndex.append(to_string(face_cur.mIndices[idx_vert]) + " ");
  285. }
  286. coordIndex.append("-1 ");// face delimiter.
  287. }
  288. // remove last space symbol.
  289. coordIndex.resize(coordIndex.length() - 1);
  290. attr_list.push_back({"coordIndex", coordIndex});
  291. // create node
  292. NodeHelper_OpenNode(NodeName_IFS, pTabLevel + 1, false, attr_list);
  293. attr_list.clear();
  294. // Child nodes for "IndexedFaceSet" needed when used colors, textures or normals.
  295. string attr_value;
  296. // Export <Coordinate>
  297. AttrHelper_Vec3DArrToString(mesh.mVertices, mesh.mNumVertices, attr_value);
  298. attr_list.push_back({"point", attr_value});
  299. NodeHelper_OpenNode("Coordinate", pTabLevel + 2, true, attr_list);
  300. attr_list.clear();
  301. // Export <ColorRGBA>
  302. if(mesh.HasVertexColors(0))
  303. {
  304. AttrHelper_Col4DArrToString(mesh.mColors[0], mesh.mNumVertices, attr_value);
  305. attr_list.push_back({"color", attr_value});
  306. NodeHelper_OpenNode("ColorRGBA", pTabLevel + 2, true, attr_list);
  307. attr_list.clear();
  308. }
  309. // Export <TextureCoordinate>
  310. if(mesh.HasTextureCoords(0))
  311. {
  312. AttrHelper_Vec3DAsVec2fArrToString(mesh.mTextureCoords[0], mesh.mNumVertices, attr_value);
  313. attr_list.push_back({"point", attr_value});
  314. NodeHelper_OpenNode("TextureCoordinate", pTabLevel + 2, true, attr_list);
  315. attr_list.clear();
  316. }
  317. // Export <Normal>
  318. if(mesh.HasNormals())
  319. {
  320. AttrHelper_Vec3DArrToString(mesh.mNormals, mesh.mNumVertices, attr_value);
  321. attr_list.push_back({"vector", attr_value});
  322. NodeHelper_OpenNode("Normal", pTabLevel + 2, true, attr_list);
  323. attr_list.clear();
  324. }
  325. //
  326. // Close opened nodes.
  327. //
  328. NodeHelper_CloseNode(NodeName_IFS, pTabLevel + 1);
  329. NodeHelper_CloseNode(NodeName_Shape, pTabLevel);
  330. }
  331. void X3DExporter::Export_Material(const size_t pIdxMaterial, const size_t pTabLevel)
  332. {
  333. const char* NodeName_A = "Appearance";
  334. list<SAttribute> attr_list;
  335. aiMaterial& material = *mScene->mMaterials[pIdxMaterial];// create alias for conveniance.
  336. // Check if material already defined early.
  337. if(mDEF_Map_Material.find(pIdxMaterial) != mDEF_Map_Material.end())
  338. {
  339. // Material already defined, just refer to it
  340. attr_list.push_back({"USE", mDEF_Map_Material.at(pIdxMaterial)});
  341. NodeHelper_OpenNode(NodeName_A, pTabLevel, true, attr_list);
  342. return;
  343. }
  344. string material_name(string("_IDX_") + to_string(pIdxMaterial));// Create material name
  345. aiString ai_mat_name;
  346. if(material.Get(AI_MATKEY_NAME, ai_mat_name) == AI_SUCCESS) material_name.insert(0, ai_mat_name.C_Str());
  347. // Define material name.
  348. attr_list.push_back({"DEF", material_name});
  349. mDEF_Map_Material[pIdxMaterial] = material_name;
  350. //
  351. // "Appearance" node.
  352. //
  353. NodeHelper_OpenNode(NodeName_A, pTabLevel, false, attr_list);
  354. attr_list.clear();
  355. //
  356. // "Material" node.
  357. //
  358. {
  359. auto Color4ToAttrList = [&](const string& pAttrName, const aiColor4D& pAttrValue, const aiColor3D& pAttrDefaultValue)
  360. {
  361. string tstr;
  362. if(aiColor3D(pAttrValue.r, pAttrValue.g, pAttrValue.b) != pAttrDefaultValue)
  363. {
  364. AttrHelper_Col4DArrToString(&pAttrValue, 1, tstr);
  365. attr_list.push_back({pAttrName, tstr});
  366. }
  367. };
  368. float tvalf;
  369. aiColor3D color3;
  370. aiColor4D color4;
  371. // ambientIntensity="0.2" SFFloat [inputOutput]
  372. if(material.Get(AI_MATKEY_COLOR_AMBIENT, color3) == AI_SUCCESS)
  373. AttrHelper_FloatToAttrList(attr_list, "ambientIntensity", (color3.r + color3.g + color3.b) / 3.0f, 0.2f);
  374. else if(material.Get(AI_MATKEY_COLOR_AMBIENT, color4) == AI_SUCCESS)
  375. AttrHelper_FloatToAttrList(attr_list, "ambientIntensity", (color4.r + color4.g + color4.b) / 3.0f, 0.2f);
  376. // diffuseColor="0.8 0.8 0.8" SFColor [inputOutput]
  377. if(material.Get(AI_MATKEY_COLOR_DIFFUSE, color3) == AI_SUCCESS)
  378. AttrHelper_Color3ToAttrList(attr_list, "diffuseColor", color3, aiColor3D(0.8f, 0.8f, 0.8f));
  379. else if(material.Get(AI_MATKEY_COLOR_DIFFUSE, color4) == AI_SUCCESS)
  380. Color4ToAttrList("diffuseColor", color4, aiColor3D(0.8f, 0.8f, 0.8f));
  381. // emissiveColor="0 0 0" SFColor [inputOutput]
  382. if(material.Get(AI_MATKEY_COLOR_EMISSIVE, color3) == AI_SUCCESS)
  383. AttrHelper_Color3ToAttrList(attr_list, "emissiveColor", color3, aiColor3D(0, 0, 0));
  384. else if(material.Get(AI_MATKEY_COLOR_EMISSIVE, color4) == AI_SUCCESS)
  385. Color4ToAttrList("emissiveColor", color4, aiColor3D(0, 0, 0));
  386. // shininess="0.2" SFFloat [inputOutput]
  387. if(material.Get(AI_MATKEY_SHININESS, tvalf) == AI_SUCCESS) AttrHelper_FloatToAttrList(attr_list, "shininess", tvalf, 0.2f);
  388. // specularColor="0 0 0" SFColor [inputOutput]
  389. if(material.Get(AI_MATKEY_COLOR_SPECULAR, color3) == AI_SUCCESS)
  390. AttrHelper_Color3ToAttrList(attr_list, "specularColor", color3, aiColor3D(0, 0, 0));
  391. else if(material.Get(AI_MATKEY_COLOR_SPECULAR, color4) == AI_SUCCESS)
  392. Color4ToAttrList("specularColor", color4, aiColor3D(0, 0, 0));
  393. // transparency="0" SFFloat [inputOutput]
  394. if(material.Get(AI_MATKEY_OPACITY, tvalf) == AI_SUCCESS)
  395. {
  396. if(tvalf > 1) tvalf = 1;
  397. tvalf = 1.0f - tvalf;
  398. AttrHelper_FloatToAttrList(attr_list, "transparency", tvalf, 0);
  399. }
  400. NodeHelper_OpenNode("Material", pTabLevel + 1, true, attr_list);
  401. attr_list.clear();
  402. }// "Material" node. END.
  403. //
  404. // "ImageTexture" node.
  405. //
  406. {
  407. auto RepeatToAttrList = [&](const string& pAttrName, const bool pAttrValue)
  408. {
  409. if(!pAttrValue) attr_list.push_back({pAttrName, "false"});
  410. };
  411. bool tvalb;
  412. aiString tstring;
  413. // url="" MFString
  414. if(material.Get(AI_MATKEY_TEXTURE_DIFFUSE(0), tstring) == AI_SUCCESS)
  415. {
  416. if(strncmp(tstring.C_Str(), AI_EMBEDDED_TEXNAME_PREFIX, strlen(AI_EMBEDDED_TEXNAME_PREFIX)) == 0)
  417. LogError("Embedded texture is not supported");
  418. else
  419. attr_list.push_back({"url", string("\"") + tstring.C_Str() + "\""});
  420. }
  421. // repeatS="true" SFBool
  422. if(material.Get(AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0), tvalb) == AI_SUCCESS) RepeatToAttrList("repeatS", tvalb);
  423. // repeatT="true" SFBool
  424. if(material.Get(AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0), tvalb) == AI_SUCCESS) RepeatToAttrList("repeatT", tvalb);
  425. NodeHelper_OpenNode("ImageTexture", pTabLevel + 1, true, attr_list);
  426. attr_list.clear();
  427. }// "ImageTexture" node. END.
  428. //
  429. // "TextureTransform" node.
  430. //
  431. {
  432. auto Vec2ToAttrList = [&](const string& pAttrName, const aiVector2D& pAttrValue, const aiVector2D& pAttrDefaultValue)
  433. {
  434. string tstr;
  435. if(pAttrValue != pAttrDefaultValue)
  436. {
  437. AttrHelper_Vec2DArrToString(&pAttrValue, 1, tstr);
  438. attr_list.push_back({pAttrName, tstr});
  439. }
  440. };
  441. aiUVTransform transform;
  442. if(material.Get(AI_MATKEY_UVTRANSFORM_DIFFUSE(0), transform) == AI_SUCCESS)
  443. {
  444. Vec2ToAttrList("translation", transform.mTranslation, aiVector2D(0, 0));
  445. AttrHelper_FloatToAttrList(attr_list, "rotation", transform.mRotation, 0);
  446. Vec2ToAttrList("scale", transform.mScaling, aiVector2D(1, 1));
  447. NodeHelper_OpenNode("TextureTransform", pTabLevel + 1, true, attr_list);
  448. attr_list.clear();
  449. }
  450. }// "TextureTransform" node. END.
  451. //
  452. // Close opened nodes.
  453. //
  454. NodeHelper_CloseNode(NodeName_A, pTabLevel);
  455. }
  456. void X3DExporter::Export_MetadataBoolean(const aiString& pKey, const bool pValue, const size_t pTabLevel)
  457. {
  458. list<SAttribute> attr_list;
  459. attr_list.push_back({"name", pKey.C_Str()});
  460. attr_list.push_back({"value", pValue ? "true" : "false"});
  461. NodeHelper_OpenNode("MetadataBoolean", pTabLevel, true, attr_list);
  462. }
  463. void X3DExporter::Export_MetadataDouble(const aiString& pKey, const double pValue, const size_t pTabLevel)
  464. {
  465. list<SAttribute> attr_list;
  466. attr_list.push_back({"name", pKey.C_Str()});
  467. attr_list.push_back({"value", to_string(pValue)});
  468. NodeHelper_OpenNode("MetadataDouble", pTabLevel, true, attr_list);
  469. }
  470. void X3DExporter::Export_MetadataFloat(const aiString& pKey, const float pValue, const size_t pTabLevel)
  471. {
  472. list<SAttribute> attr_list;
  473. attr_list.push_back({"name", pKey.C_Str()});
  474. attr_list.push_back({"value", to_string(pValue)});
  475. NodeHelper_OpenNode("MetadataFloat", pTabLevel, true, attr_list);
  476. }
  477. void X3DExporter::Export_MetadataInteger(const aiString& pKey, const int32_t pValue, const size_t pTabLevel)
  478. {
  479. list<SAttribute> attr_list;
  480. attr_list.push_back({"name", pKey.C_Str()});
  481. attr_list.push_back({"value", to_string(pValue)});
  482. NodeHelper_OpenNode("MetadataInteger", pTabLevel, true, attr_list);
  483. }
  484. void X3DExporter::Export_MetadataString(const aiString& pKey, const aiString& pValue, const size_t pTabLevel)
  485. {
  486. list<SAttribute> attr_list;
  487. attr_list.push_back({"name", pKey.C_Str()});
  488. attr_list.push_back({"value", pValue.C_Str()});
  489. NodeHelper_OpenNode("MetadataString", pTabLevel, true, attr_list);
  490. }
  491. bool X3DExporter::CheckAndExport_Light(const aiNode& pNode, const size_t pTabLevel)
  492. {
  493. list<SAttribute> attr_list;
  494. auto Vec3ToAttrList = [&](const string& pAttrName, const aiVector3D& pAttrValue, const aiVector3D& pAttrDefaultValue)
  495. {
  496. string tstr;
  497. if(pAttrValue != pAttrDefaultValue)
  498. {
  499. AttrHelper_Vec3DArrToString(&pAttrValue, 1, tstr);
  500. attr_list.push_back({pAttrName, tstr});
  501. }
  502. };
  503. size_t idx_light;
  504. bool found = false;
  505. // Name of the light source can not be empty.
  506. if(pNode.mName.length == 0) return false;
  507. // search for light with name like node has.
  508. for(idx_light = 0; mScene->mNumLights; idx_light++)
  509. {
  510. if(pNode.mName == mScene->mLights[idx_light]->mName)
  511. {
  512. found = true;
  513. break;
  514. }
  515. }
  516. if(!found) return false;
  517. // Light source is found.
  518. const aiLight& light = *mScene->mLights[idx_light];// Alias for conveniance.
  519. aiMatrix4x4 trafo_mat = Matrix_GlobalToCurrent(pNode).Inverse();
  520. attr_list.push_back({"DEF", light.mName.C_Str()});
  521. attr_list.push_back({"global", "true"});// "false" is not supported.
  522. // ambientIntensity="0" SFFloat [inputOutput]
  523. AttrHelper_FloatToAttrList(attr_list, "ambientIntensity", aiVector3D(light.mColorAmbient.r, light.mColorAmbient.g, light.mColorAmbient.b).Length(), 0);
  524. // color="1 1 1" SFColor [inputOutput]
  525. AttrHelper_Color3ToAttrList(attr_list, "color", light.mColorDiffuse, aiColor3D(1, 1, 1));
  526. switch(light.mType)
  527. {
  528. case aiLightSource_DIRECTIONAL:
  529. {
  530. aiVector3D direction = trafo_mat * light.mDirection;
  531. Vec3ToAttrList("direction", direction, aiVector3D(0, 0, -1));
  532. NodeHelper_OpenNode("DirectionalLight", pTabLevel, true, attr_list);
  533. }
  534. break;
  535. case aiLightSource_POINT:
  536. {
  537. aiVector3D attenuation(light.mAttenuationConstant, light.mAttenuationLinear, light.mAttenuationQuadratic);
  538. aiVector3D location = trafo_mat * light.mPosition;
  539. Vec3ToAttrList("attenuation", attenuation, aiVector3D(1, 0, 0));
  540. Vec3ToAttrList("location", location, aiVector3D(0, 0, 0));
  541. NodeHelper_OpenNode("PointLight", pTabLevel, true, attr_list);
  542. }
  543. break;
  544. case aiLightSource_SPOT:
  545. {
  546. aiVector3D attenuation(light.mAttenuationConstant, light.mAttenuationLinear, light.mAttenuationQuadratic);
  547. aiVector3D location = trafo_mat * light.mPosition;
  548. aiVector3D direction = trafo_mat * light.mDirection;
  549. Vec3ToAttrList("attenuation", attenuation, aiVector3D(1, 0, 0));
  550. Vec3ToAttrList("location", location, aiVector3D(0, 0, 0));
  551. Vec3ToAttrList("direction", direction, aiVector3D(0, 0, -1));
  552. AttrHelper_FloatToAttrList(attr_list, "beamWidth", light.mAngleInnerCone, 0.7854f);
  553. AttrHelper_FloatToAttrList(attr_list, "cutOffAngle", light.mAngleOuterCone, 1.570796f);
  554. NodeHelper_OpenNode("SpotLight", pTabLevel, true, attr_list);
  555. }
  556. break;
  557. default:
  558. throw DeadlyExportError("Unknown light type: " + to_string(light.mType));
  559. }// switch(light.mType)
  560. return true;
  561. }
  562. X3DExporter::X3DExporter(const char* pFileName, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
  563. : mScene(pScene)
  564. {
  565. list<SAttribute> attr_list;
  566. mOutFile = pIOSystem->Open(pFileName, "wt");
  567. if(mOutFile == nullptr) throw DeadlyExportError("Could not open output .x3d file: " + string(pFileName));
  568. // Begin document
  569. XML_Write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  570. XML_Write("<!DOCTYPE X3D PUBLIC \"ISO//Web3D//DTD X3D 3.3//EN\" \"http://www.web3d.org/specifications/x3d-3.3.dtd\">\n");
  571. // Root node
  572. attr_list.push_back({"profile", "Interchange"});
  573. attr_list.push_back({"version", "3.3"});
  574. attr_list.push_back({"xmlns:xsd", "http://www.w3.org/2001/XMLSchema-instance"});
  575. attr_list.push_back({"xsd:noNamespaceSchemaLocation", "http://www.web3d.org/specifications/x3d-3.3.xsd"});
  576. NodeHelper_OpenNode("X3D", 0, false, attr_list);
  577. attr_list.clear();
  578. // <head>: meta data.
  579. NodeHelper_OpenNode("head", 1);
  580. XML_Write(mIndentationString + "<!-- All \"meta\" from this section tou will found in <Scene> node as MetadataString nodes. -->\n");
  581. NodeHelper_CloseNode("head", 1);
  582. // Scene node.
  583. NodeHelper_OpenNode("Scene", 1);
  584. Export_Node(mScene->mRootNode, 2);
  585. NodeHelper_CloseNode("Scene", 1);
  586. // Close Root node.
  587. NodeHelper_CloseNode("X3D", 0);
  588. // Cleanup
  589. pIOSystem->Close(mOutFile);
  590. mOutFile = nullptr;
  591. }
  592. }// namespace Assimp
  593. #endif // ASSIMP_BUILD_NO_X3D_EXPORTER
  594. #endif // ASSIMP_BUILD_NO_EXPORT