X3DExporter.cpp 26 KB

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