X3DExporter.cpp 23 KB

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