AMFImporter_Postprocess.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. /// \file AMFImporter_Postprocess.cpp
  2. /// \brief Convert built scenegraph and objects to Assimp scenegraph.
  3. /// \date 2016
  4. /// \author [email protected]
  5. #ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
  6. #include "AMFImporter.hpp"
  7. // Header files, Assimp.
  8. #include "SceneCombiner.h"
  9. #include "StandardShapes.h"
  10. // Header files, stdlib.
  11. #include <algorithm>
  12. #include <iterator>
  13. namespace Assimp
  14. {
  15. aiColor4D AMFImporter::SPP_Material::GetColor(const float pX, const float pY, const float pZ) const
  16. {
  17. aiColor4D tcol;
  18. // Check if stored data are supported.
  19. if(Composition.size() != 0)
  20. {
  21. throw DeadlyImportError("IME. GetColor for composition");
  22. }
  23. else if(Color->Composed)
  24. {
  25. throw DeadlyImportError("IME. GetColor, composed color");
  26. }
  27. else
  28. {
  29. tcol = Color->Color;
  30. }
  31. // Check if default color must be used
  32. if((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0))
  33. {
  34. tcol.r = 0.5f;
  35. tcol.g = 0.5f;
  36. tcol.b = 0.5f;
  37. tcol.a = 1;
  38. }
  39. return tcol;
  40. }
  41. void AMFImporter::PostprocessHelper_CreateMeshDataArray(const CAMFImporter_NodeElement_Mesh& pNodeElement, std::vector<aiVector3D>& pVertexCoordinateArray,
  42. std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray) const
  43. {
  44. CAMFImporter_NodeElement_Vertices* vn = nullptr;
  45. size_t col_idx;
  46. // All data stored in "vertices", search for it.
  47. for(CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
  48. {
  49. if(ne_child->Type == CAMFImporter_NodeElement::ENET_Vertices) vn = (CAMFImporter_NodeElement_Vertices*)ne_child;
  50. }
  51. // If "vertices" not found then no work for us.
  52. if(vn == nullptr) return;
  53. pVertexCoordinateArray.reserve(vn->Child.size());// all coordinates stored as child and we need to reserve space for future push_back's.
  54. pVertexColorArray.resize(vn->Child.size());// colors count equal vertices count.
  55. col_idx = 0;
  56. // Inside vertices collect all data and place to arrays
  57. for(CAMFImporter_NodeElement* vn_child: vn->Child)
  58. {
  59. // vertices, colors
  60. if(vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex)
  61. {
  62. // by default clear color for current vertex
  63. pVertexColorArray[col_idx] = nullptr;
  64. for(CAMFImporter_NodeElement* vtx: vn_child->Child)
  65. {
  66. if(vtx->Type == CAMFImporter_NodeElement::ENET_Coordinates)
  67. {
  68. pVertexCoordinateArray.push_back(((CAMFImporter_NodeElement_Coordinates*)vtx)->Coordinate);
  69. continue;
  70. }
  71. if(vtx->Type == CAMFImporter_NodeElement::ENET_Color)
  72. {
  73. pVertexColorArray[col_idx] = (CAMFImporter_NodeElement_Color*)vtx;
  74. continue;
  75. }
  76. }// for(CAMFImporter_NodeElement* vtx: vn_child->Child)
  77. col_idx++;
  78. }// if(vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex)
  79. }// for(CAMFImporter_NodeElement* vn_child: vn->Child)
  80. }
  81. size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string& pID_R, const std::string& pID_G, const std::string& pID_B,
  82. const std::string& pID_A)
  83. {
  84. size_t TextureConverted_Index;
  85. std::string TextureConverted_ID;
  86. // check input data
  87. if(pID_R.empty() && pID_G.empty() && pID_B.empty() && pID_A.empty())
  88. throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. At least one texture ID must be defined.");
  89. // Create ID
  90. TextureConverted_ID = pID_R + "_" + pID_G + "_" + pID_B + "_" + pID_A;
  91. // Check if texture specified by set of IDs is converted already.
  92. TextureConverted_Index = 0;
  93. for(const SPP_Texture& tex_convd: mTexture_Converted)
  94. {
  95. if(tex_convd.ID == TextureConverted_ID)
  96. return TextureConverted_Index;
  97. else
  98. TextureConverted_Index++;
  99. }
  100. //
  101. // Converted texture not found, create it.
  102. //
  103. CAMFImporter_NodeElement_Texture* src_texture[4]{nullptr};
  104. std::vector<CAMFImporter_NodeElement_Texture*> src_texture_4check;
  105. SPP_Texture converted_texture;
  106. {// find all specified source textures
  107. CAMFImporter_NodeElement* t_tex;
  108. // R
  109. if(!pID_R.empty())
  110. {
  111. if(!Find_NodeElement(pID_R, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_R);
  112. src_texture[0] = (CAMFImporter_NodeElement_Texture*)t_tex;
  113. src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
  114. }
  115. else
  116. {
  117. src_texture[0] = nullptr;
  118. }
  119. // G
  120. if(!pID_G.empty())
  121. {
  122. if(!Find_NodeElement(pID_G, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_G);
  123. src_texture[1] = (CAMFImporter_NodeElement_Texture*)t_tex;
  124. src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
  125. }
  126. else
  127. {
  128. src_texture[1] = nullptr;
  129. }
  130. // B
  131. if(!pID_B.empty())
  132. {
  133. if(!Find_NodeElement(pID_B, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_B);
  134. src_texture[2] = (CAMFImporter_NodeElement_Texture*)t_tex;
  135. src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
  136. }
  137. else
  138. {
  139. src_texture[2] = nullptr;
  140. }
  141. // A
  142. if(!pID_A.empty())
  143. {
  144. if(!Find_NodeElement(pID_A, CAMFImporter_NodeElement::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_A);
  145. src_texture[3] = (CAMFImporter_NodeElement_Texture*)t_tex;
  146. src_texture_4check.push_back((CAMFImporter_NodeElement_Texture*)t_tex);
  147. }
  148. else
  149. {
  150. src_texture[3] = nullptr;
  151. }
  152. }// END: find all specified source textures
  153. // check that all textures has same size
  154. if(src_texture_4check.size() > 1)
  155. {
  156. for(uint8_t i = 0, i_e = (src_texture_4check.size() - 1); i < i_e; i++)
  157. {
  158. if((src_texture_4check[i]->Width != src_texture_4check[i + 1]->Width) || (src_texture_4check[i]->Height != src_texture_4check[i + 1]->Height) ||
  159. (src_texture_4check[i]->Depth != src_texture_4check[i + 1]->Depth))
  160. {
  161. throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. Source texture must has the same size.");
  162. }
  163. }
  164. }// if(src_texture_4check.size() > 1)
  165. // set texture attributes
  166. converted_texture.Width = src_texture_4check[0]->Width;
  167. converted_texture.Height = src_texture_4check[0]->Height;
  168. converted_texture.Depth = src_texture_4check[0]->Depth;
  169. // if one of source texture is tiled then converted texture is tiled too.
  170. converted_texture.Tiled = false;
  171. for(uint8_t i = 0; i < src_texture_4check.size(); i++) converted_texture.Tiled |= src_texture_4check[i]->Tiled;
  172. // Create format hint.
  173. strcpy(converted_texture.FormatHint, "rgba0000");// copy initial string.
  174. if(!pID_R.empty()) converted_texture.FormatHint[4] = '8';
  175. if(!pID_G.empty()) converted_texture.FormatHint[5] = '8';
  176. if(!pID_B.empty()) converted_texture.FormatHint[6] = '8';
  177. if(!pID_A.empty()) converted_texture.FormatHint[7] = '8';
  178. //
  179. // Сopy data of textures.
  180. //
  181. size_t tex_size = 0;
  182. size_t step = 0;
  183. size_t off_g = 0;
  184. size_t off_b = 0;
  185. // Calculate size of the target array and rule how data will be copied.
  186. if(!pID_R.empty()) { tex_size += src_texture[0]->Data.size(); step++, off_g++, off_b++; }
  187. if(!pID_G.empty()) { tex_size += src_texture[1]->Data.size(); step++, off_b++; }
  188. if(!pID_B.empty()) { tex_size += src_texture[2]->Data.size(); step++; }
  189. if(!pID_A.empty()) { tex_size += src_texture[3]->Data.size(); step++; }
  190. // Create target array.
  191. converted_texture.Data = new uint8_t[tex_size];
  192. // And copy data
  193. auto CopyTextureData = [&](const std::string& pID, const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void
  194. {
  195. if(!pID.empty())
  196. {
  197. for(size_t idx_target = pOffset, idx_src = 0; idx_target < tex_size; idx_target += pStep, idx_src++)
  198. converted_texture.Data[idx_target] = src_texture[pSrcTexNum]->Data.at(idx_src);
  199. }
  200. };// auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void
  201. CopyTextureData(pID_R, 0, step, 0);
  202. CopyTextureData(pID_G, off_g, step, 1);
  203. CopyTextureData(pID_B, off_b, step, 2);
  204. CopyTextureData(pID_A, step - 1, step, 3);
  205. // Store new converted texture ID
  206. converted_texture.ID = TextureConverted_ID;
  207. // Store new converted texture
  208. mTexture_Converted.push_back(converted_texture);
  209. return TextureConverted_Index;
  210. }
  211. void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace>& pInputList, std::list<std::list<SComplexFace> >& pOutputList_Separated)
  212. {
  213. auto texmap_is_equal = [](const CAMFImporter_NodeElement_TexMap* pTexMap1, const CAMFImporter_NodeElement_TexMap* pTexMap2) -> bool
  214. {
  215. if((pTexMap1 == nullptr) && (pTexMap2 == nullptr)) return true;
  216. if(pTexMap1 == nullptr) return false;
  217. if(pTexMap2 == nullptr) return false;
  218. if(pTexMap1->TextureID_R != pTexMap2->TextureID_R) return false;
  219. if(pTexMap1->TextureID_G != pTexMap2->TextureID_G) return false;
  220. if(pTexMap1->TextureID_B != pTexMap2->TextureID_B) return false;
  221. if(pTexMap1->TextureID_A != pTexMap2->TextureID_A) return false;
  222. return true;
  223. };
  224. pOutputList_Separated.clear();
  225. if(pInputList.size() == 0) return;
  226. do
  227. {
  228. SComplexFace face_start = pInputList.front();
  229. std::list<SComplexFace> face_list_cur;
  230. for(std::list<SComplexFace>::const_iterator it = pInputList.cbegin(), it_end = pInputList.cend(); it != it_end;)
  231. {
  232. if(texmap_is_equal(face_start.TexMap, it->TexMap))
  233. {
  234. auto it_old = it;
  235. it++;
  236. face_list_cur.push_back(*it_old);
  237. pInputList.erase(it_old);
  238. }
  239. else
  240. {
  241. it++;
  242. }
  243. }
  244. if(face_list_cur.size() > 0) pOutputList_Separated.push_back(face_list_cur);
  245. } while(pInputList.size() > 0);
  246. }
  247. void AMFImporter::Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata*>& pMetadataList, aiNode& pSceneNode) const
  248. {
  249. if(pMetadataList.size() > 0)
  250. {
  251. if(pSceneNode.mMetaData != nullptr) throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong.");
  252. // copy collected metadata to output node.
  253. pSceneNode.mMetaData = new aiMetadata();
  254. pSceneNode.mMetaData->mNumProperties = pMetadataList.size();
  255. pSceneNode.mMetaData->mKeys = new aiString[pSceneNode.mMetaData->mNumProperties];
  256. pSceneNode.mMetaData->mValues = new aiMetadataEntry[pSceneNode.mMetaData->mNumProperties];
  257. size_t meta_idx = 0;
  258. for(const CAMFImporter_NodeElement_Metadata& metadata: pMetadataList)
  259. {
  260. pSceneNode.mMetaData->Set(meta_idx++, metadata.Type, metadata.Value.c_str());
  261. }
  262. }// if(pMetadataList.size() > 0)
  263. }
  264. void AMFImporter::Postprocess_BuildNodeAndObject(const CAMFImporter_NodeElement_Object& pNodeElement, std::list<aiMesh*>& pMeshList, aiNode** pSceneNode)
  265. {
  266. CAMFImporter_NodeElement_Color* object_color = nullptr;
  267. // create new aiNode and set name as <object> has.
  268. *pSceneNode = new aiNode;
  269. (*pSceneNode)->mName = pNodeElement.ID;
  270. // read mesh and color
  271. for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
  272. {
  273. std::vector<aiVector3D> vertex_arr;
  274. std::vector<CAMFImporter_NodeElement_Color*> color_arr;
  275. // color for object
  276. if(ne_child->Type == CAMFImporter_NodeElement::ENET_Color) object_color = (CAMFImporter_NodeElement_Color*)ne_child;
  277. if(ne_child->Type == CAMFImporter_NodeElement::ENET_Mesh)
  278. {
  279. // Create arrays from children of mesh: vertices.
  280. PostprocessHelper_CreateMeshDataArray(*((CAMFImporter_NodeElement_Mesh*)ne_child), vertex_arr, color_arr);
  281. // Use this arrays as a source when creating every aiMesh
  282. Postprocess_BuildMeshSet(*((CAMFImporter_NodeElement_Mesh*)ne_child), vertex_arr, color_arr, object_color, pMeshList, **pSceneNode);
  283. }
  284. }// for(const CAMFImporter_NodeElement* ne_child: pNodeElement)
  285. }
  286. void AMFImporter::Postprocess_BuildMeshSet(const CAMFImporter_NodeElement_Mesh& pNodeElement, const std::vector<aiVector3D>& pVertexCoordinateArray,
  287. const std::vector<CAMFImporter_NodeElement_Color*>& pVertexColorArray,
  288. const CAMFImporter_NodeElement_Color* pObjectColor, std::list<aiMesh*>& pMeshList, aiNode& pSceneNode)
  289. {
  290. std::list<unsigned int> mesh_idx;
  291. // all data stored in "volume", search for it.
  292. for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
  293. {
  294. const CAMFImporter_NodeElement_Color* ne_volume_color = nullptr;
  295. const SPP_Material* cur_mat = nullptr;
  296. if(ne_child->Type == CAMFImporter_NodeElement::ENET_Volume)
  297. {
  298. /******************* Get faces *******************/
  299. const CAMFImporter_NodeElement_Volume* ne_volume = reinterpret_cast<const CAMFImporter_NodeElement_Volume*>(ne_child);
  300. std::list<SComplexFace> complex_faces_list;// List of the faces of the volume.
  301. std::list<std::list<SComplexFace> > complex_faces_toplist;// List of the face list for every mesh.
  302. // check if volume use material
  303. if(!ne_volume->MaterialID.empty())
  304. {
  305. if(!Find_ConvertedMaterial(ne_volume->MaterialID, &cur_mat)) Throw_ID_NotFound(ne_volume->MaterialID);
  306. }
  307. // inside "volume" collect all data and place to arrays or create new objects
  308. for(const CAMFImporter_NodeElement* ne_volume_child: ne_volume->Child)
  309. {
  310. // color for volume
  311. if(ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Color)
  312. {
  313. ne_volume_color = reinterpret_cast<const CAMFImporter_NodeElement_Color*>(ne_volume_child);
  314. }
  315. else if(ne_volume_child->Type == CAMFImporter_NodeElement::ENET_Triangle)// triangles, triangles colors
  316. {
  317. const CAMFImporter_NodeElement_Triangle& tri_al = *reinterpret_cast<const CAMFImporter_NodeElement_Triangle*>(ne_volume_child);
  318. SComplexFace complex_face;
  319. // initialize pointers
  320. complex_face.Color = nullptr;
  321. complex_face.TexMap = nullptr;
  322. // get data from triangle children: color, texture coordinates.
  323. if(tri_al.Child.size())
  324. {
  325. for(const CAMFImporter_NodeElement* ne_triangle_child: tri_al.Child)
  326. {
  327. if(ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_Color)
  328. complex_face.Color = reinterpret_cast<const CAMFImporter_NodeElement_Color*>(ne_triangle_child);
  329. else if(ne_triangle_child->Type == CAMFImporter_NodeElement::ENET_TexMap)
  330. complex_face.TexMap = reinterpret_cast<const CAMFImporter_NodeElement_TexMap*>(ne_triangle_child);
  331. }
  332. }// if(tri_al.Child.size())
  333. // create new face and store it.
  334. complex_face.Face.mNumIndices = 3;
  335. complex_face.Face.mIndices = new unsigned int[3];
  336. complex_face.Face.mIndices[0] = tri_al.V[0];
  337. complex_face.Face.mIndices[1] = tri_al.V[1];
  338. complex_face.Face.mIndices[2] = tri_al.V[2];
  339. complex_faces_list.push_back(complex_face);
  340. }
  341. }// for(const CAMFImporter_NodeElement* ne_volume_child: ne_volume->Child)
  342. /**** Split faces list: one list per mesh ****/
  343. PostprocessHelper_SplitFacesByTextureID(complex_faces_list, complex_faces_toplist);
  344. /***** Create mesh for every faces list ******/
  345. for(std::list<SComplexFace>& face_list_cur: complex_faces_toplist)
  346. {
  347. auto VertexIndex_GetMinimal = [](const std::list<SComplexFace>& pFaceList, const size_t* pBiggerThan) -> size_t
  348. {
  349. size_t rv;
  350. if(pBiggerThan != nullptr)
  351. {
  352. bool found = false;
  353. for(const SComplexFace& face: pFaceList)
  354. {
  355. for(size_t idx_vert = 0; idx_vert < face.Face.mNumIndices; idx_vert++)
  356. {
  357. if(face.Face.mIndices[idx_vert] > *pBiggerThan)
  358. {
  359. rv = face.Face.mIndices[idx_vert];
  360. found = true;
  361. break;
  362. }
  363. }
  364. if(found) break;
  365. }
  366. if(!found) return *pBiggerThan;
  367. }
  368. else
  369. {
  370. rv = pFaceList.front().Face.mIndices[0];
  371. }// if(pBiggerThan != nullptr) else
  372. for(const SComplexFace& face: pFaceList)
  373. {
  374. for(size_t vi = 0; vi < face.Face.mNumIndices; vi++)
  375. {
  376. if(face.Face.mIndices[vi] < rv)
  377. {
  378. if(pBiggerThan != nullptr)
  379. {
  380. if(face.Face.mIndices[vi] > *pBiggerThan) rv = face.Face.mIndices[vi];
  381. }
  382. else
  383. {
  384. rv = face.Face.mIndices[vi];
  385. }
  386. }
  387. }
  388. }// for(const SComplexFace& face: pFaceList)
  389. return rv;
  390. };// auto VertexIndex_GetMinimal = [](const std::list<SComplexFace>& pFaceList, const size_t* pBiggerThan) -> size_t
  391. auto VertexIndex_Replace = [](std::list<SComplexFace>& pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void
  392. {
  393. for(const SComplexFace& face: pFaceList)
  394. {
  395. for(size_t vi = 0; vi < face.Face.mNumIndices; vi++)
  396. {
  397. if(face.Face.mIndices[vi] == pIdx_From) face.Face.mIndices[vi] = pIdx_To;
  398. }
  399. }
  400. };// auto VertexIndex_Replace = [](std::list<SComplexFace>& pFaceList, const size_t pIdx_From, const size_t pIdx_To) -> void
  401. auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D
  402. {
  403. // Color priorities(In descending order):
  404. // 1. triangle color;
  405. // 2. vertex color;
  406. // 3. volume color;
  407. // 4. object color;
  408. // 5. material;
  409. // 6. default - invisible coat.
  410. //
  411. // Fill vertices colors in color priority list above that's points from 1 to 6.
  412. if((pIdx < pVertexColorArray.size()) && (pVertexColorArray[pIdx] != nullptr))// check for vertex color
  413. {
  414. if(pVertexColorArray[pIdx]->Composed)
  415. throw DeadlyImportError("IME: vertex color composed");
  416. else
  417. return pVertexColorArray[pIdx]->Color;
  418. }
  419. else if(ne_volume_color != nullptr)// check for volume color
  420. {
  421. if(ne_volume_color->Composed)
  422. throw DeadlyImportError("IME: volume color composed");
  423. else
  424. return ne_volume_color->Color;
  425. }
  426. else if(pObjectColor != nullptr)// check for object color
  427. {
  428. if(pObjectColor->Composed)
  429. throw DeadlyImportError("IME: object color composed");
  430. else
  431. return pObjectColor->Color;
  432. }
  433. else if(cur_mat != nullptr)// check for material
  434. {
  435. return cur_mat->GetColor(pVertexCoordinateArray.at(pIdx).x, pVertexCoordinateArray.at(pIdx).y, pVertexCoordinateArray.at(pIdx).z);
  436. }
  437. else// set default color.
  438. {
  439. return {0, 0, 0, 0};
  440. }// if((vi < pVertexColorArray.size()) && (pVertexColorArray[vi] != nullptr)) else
  441. };// auto Vertex_CalculateColor = [&](const size_t pIdx) -> aiColor4D
  442. aiMesh* tmesh = new aiMesh;
  443. tmesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;// Only triangles is supported by AMF.
  444. //
  445. // set geometry and colors (vertices)
  446. //
  447. // copy faces/triangles
  448. tmesh->mNumFaces = face_list_cur.size();
  449. tmesh->mFaces = new aiFace[tmesh->mNumFaces];
  450. // Create vertices list and optimize indices. Optimisation mean following.In AMF all volumes use one big list of vertices. And one volume
  451. // can use only part of vertices list, for example: vertices list contain few thousands of vertices and volume use vertices 1, 3, 10.
  452. // Do you need all this thousands of garbage? Of course no. So, optimisation step transformate sparse indices set to continuous.
  453. const size_t VertexCount_Max = tmesh->mNumFaces * 3;// 3 - triangles.
  454. std::vector<aiVector3D> vert_arr, texcoord_arr;
  455. std::vector<aiColor4D> col_arr;
  456. vert_arr.reserve(VertexCount_Max * 2);// "* 2" - see below TODO.
  457. col_arr.reserve(VertexCount_Max * 2);
  458. {// fill arrays
  459. size_t vert_idx_from, vert_idx_to;
  460. // first iteration.
  461. vert_idx_to = 0;
  462. vert_idx_from = VertexIndex_GetMinimal(face_list_cur, nullptr);
  463. vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
  464. col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
  465. if(vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
  466. // rest iterations
  467. do
  468. {
  469. vert_idx_from = VertexIndex_GetMinimal(face_list_cur, &vert_idx_to);
  470. if(vert_idx_from == vert_idx_to) break;// all indices are transfered,
  471. vert_arr.push_back(pVertexCoordinateArray.at(vert_idx_from));
  472. col_arr.push_back(Vertex_CalculateColor(vert_idx_from));
  473. vert_idx_to++;
  474. if(vert_idx_from != vert_idx_to) VertexIndex_Replace(face_list_cur, vert_idx_from, vert_idx_to);
  475. } while(true);
  476. }// fill arrays. END.
  477. //
  478. // check if triangle colors are used and create additional faces if needed.
  479. //
  480. for(const SComplexFace& face_cur: face_list_cur)
  481. {
  482. if(face_cur.Color != nullptr)
  483. {
  484. aiColor4D face_color;
  485. size_t vert_idx_new = vert_arr.size();
  486. if(face_cur.Color->Composed)
  487. throw DeadlyImportError("IME: face color composed");
  488. else
  489. face_color = face_cur.Color->Color;
  490. for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
  491. {
  492. vert_arr.push_back(vert_arr.at(face_cur.Face.mIndices[idx_ind]));
  493. col_arr.push_back(face_color);
  494. face_cur.Face.mIndices[idx_ind] = vert_idx_new++;
  495. }
  496. }// if(face_cur.Color != nullptr)
  497. }// for(const SComplexFace& face_cur: face_list_cur)
  498. //
  499. // if texture is used then copy texture coordinates too.
  500. //
  501. if(face_list_cur.front().TexMap != nullptr)
  502. {
  503. size_t idx_vert_new = vert_arr.size();
  504. ///TODO: clean unused vertices. "* 2": in certain cases - mesh full of triangle colors - vert_arr will contain duplicated vertices for
  505. /// colored triangles and initial vertices (for colored vertices) which in real became unused. This part need more thinking about
  506. /// optimisation.
  507. bool idx_vert_used[VertexCount_Max * 2]{false};
  508. // This ID's will be used when set materials ID in scene.
  509. tmesh->mMaterialIndex = PostprocessHelper_GetTextureID_Or_Create(face_list_cur.front().TexMap->TextureID_R,
  510. face_list_cur.front().TexMap->TextureID_G,
  511. face_list_cur.front().TexMap->TextureID_B,
  512. face_list_cur.front().TexMap->TextureID_A);
  513. texcoord_arr.resize(VertexCount_Max * 2);
  514. for(const SComplexFace& face_cur: face_list_cur)
  515. {
  516. for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
  517. {
  518. const size_t idx_vert = face_cur.Face.mIndices[idx_ind];
  519. if(!idx_vert_used[idx_vert])
  520. {
  521. texcoord_arr.at(idx_vert) = face_cur.TexMap->TextureCoordinate[idx_ind];
  522. idx_vert_used[idx_vert] = true;
  523. }
  524. else if(texcoord_arr.at(idx_vert) != face_cur.TexMap->TextureCoordinate[idx_ind])
  525. {
  526. // in that case one vertex is shared with many texture coordinates. We need to duplicate vertex with another texture
  527. // coordinates.
  528. vert_arr.push_back(vert_arr.at(idx_vert));
  529. col_arr.push_back(col_arr.at(idx_vert));
  530. texcoord_arr.at(idx_vert_new) = face_cur.TexMap->TextureCoordinate[idx_ind];
  531. face_cur.Face.mIndices[idx_ind] = idx_vert_new++;
  532. }
  533. }// for(size_t idx_ind = 0; idx_ind < face_cur.Face.mNumIndices; idx_ind++)
  534. }// for(const SComplexFace& face_cur: face_list_cur)
  535. // shrink array
  536. texcoord_arr.resize(idx_vert_new);
  537. }// if(face_list_cur.front().TexMap != nullptr)
  538. //
  539. // copy collected data to mesh
  540. //
  541. tmesh->mNumVertices = vert_arr.size();
  542. tmesh->mVertices = new aiVector3D[tmesh->mNumVertices];
  543. tmesh->mColors[0] = new aiColor4D[tmesh->mNumVertices];
  544. tmesh->mFaces = new aiFace[face_list_cur.size()];
  545. memcpy(tmesh->mVertices, vert_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
  546. memcpy(tmesh->mColors[0], col_arr.data(), tmesh->mNumVertices * sizeof(aiColor4D));
  547. if(texcoord_arr.size() > 0)
  548. {
  549. tmesh->mTextureCoords[0] = new aiVector3D[tmesh->mNumVertices];
  550. memcpy(tmesh->mTextureCoords[0], texcoord_arr.data(), tmesh->mNumVertices * sizeof(aiVector3D));
  551. tmesh->mNumUVComponents[0] = 2;// U and V stored in "x", "y" of aiVector3D.
  552. }
  553. size_t idx_face = 0;
  554. for(const SComplexFace& face_cur: face_list_cur) tmesh->mFaces[idx_face++] = face_cur.Face;
  555. // store new aiMesh
  556. mesh_idx.push_back(pMeshList.size());
  557. pMeshList.push_back(tmesh);
  558. }// for(const std::list<SComplexFace>& face_list_cur: complex_faces_toplist)
  559. }// if(ne_child->Type == CAMFImporter_NodeElement::ENET_Volume)
  560. }// for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
  561. // if meshes was created then assign new indices with current aiNode
  562. if(mesh_idx.size() > 0)
  563. {
  564. std::list<unsigned int>::const_iterator mit = mesh_idx.begin();
  565. pSceneNode.mNumMeshes = mesh_idx.size();
  566. pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes];
  567. for(size_t i = 0; i < pSceneNode.mNumMeshes; i++) pSceneNode.mMeshes[i] = *mit++;
  568. }// if(mesh_idx.size() > 0)
  569. }
  570. void AMFImporter::Postprocess_BuildMaterial(const CAMFImporter_NodeElement_Material& pMaterial)
  571. {
  572. SPP_Material new_mat;
  573. new_mat.ID = pMaterial.ID;
  574. for(const CAMFImporter_NodeElement* mat_child: pMaterial.Child)
  575. {
  576. if(mat_child->Type == CAMFImporter_NodeElement::ENET_Color)
  577. {
  578. new_mat.Color = (CAMFImporter_NodeElement_Color*)mat_child;
  579. }
  580. else if(mat_child->Type == CAMFImporter_NodeElement::ENET_Metadata)
  581. {
  582. new_mat.Metadata.push_back((CAMFImporter_NodeElement_Metadata*)mat_child);
  583. }
  584. }// for(const CAMFImporter_NodeElement* mat_child; pMaterial.Child)
  585. // place converted material to special list
  586. mMaterial_Converted.push_back(new_mat);
  587. }
  588. void AMFImporter::Postprocess_BuildConstellation(CAMFImporter_NodeElement_Constellation& pConstellation, std::list<aiNode*>& pNodeList) const
  589. {
  590. aiNode* con_node;
  591. std::list<aiNode*> ch_node;
  592. // We will build next hierarchy:
  593. // aiNode as parent (<constellation>) for set of nodes as a children
  594. // |- aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
  595. // ...
  596. // \_ aiNode for transformation (<instance> -> <delta...>, <r...>) - aiNode for pointing to object ("objectid")
  597. con_node = new aiNode;
  598. con_node->mName = pConstellation.ID;
  599. // Walk thru children and search for instances of another objects, constellations.
  600. for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
  601. {
  602. aiMatrix4x4 tmat;
  603. aiNode* t_node;
  604. aiNode* found_node;
  605. if(ne->Type == CAMFImporter_NodeElement::ENET_Metadata) continue;
  606. if(ne->Type != CAMFImporter_NodeElement::ENET_Instance) throw DeadlyImportError("Only <instance> nodes can be in <constellation>.");
  607. // create alias for conveniance
  608. CAMFImporter_NodeElement_Instance& als = *((CAMFImporter_NodeElement_Instance*)ne);
  609. // find referenced object
  610. if(!Find_ConvertedNode(als.ObjectID, pNodeList, &found_node)) Throw_ID_NotFound(als.ObjectID);
  611. // create node for apllying transformation
  612. t_node = new aiNode;
  613. t_node->mParent = con_node;
  614. // apply transformation
  615. aiMatrix4x4::Translation(als.Delta, tmat), t_node->mTransformation *= tmat;
  616. aiMatrix4x4::RotationX(als.Rotation.x, tmat), t_node->mTransformation *= tmat;
  617. aiMatrix4x4::RotationY(als.Rotation.y, tmat), t_node->mTransformation *= tmat;
  618. aiMatrix4x4::RotationZ(als.Rotation.z, tmat), t_node->mTransformation *= tmat;
  619. // create array for one child node
  620. t_node->mNumChildren = 1;
  621. t_node->mChildren = new aiNode*[t_node->mNumChildren];
  622. SceneCombiner::Copy(&t_node->mChildren[0], found_node);
  623. t_node->mChildren[0]->mParent = t_node;
  624. ch_node.push_back(t_node);
  625. }// for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
  626. // copy found aiNode's as children
  627. if(ch_node.size() == 0) throw DeadlyImportError("<constellation> must have at least one <instance>.");
  628. size_t ch_idx = 0;
  629. con_node->mNumChildren = ch_node.size();
  630. con_node->mChildren = new aiNode*[con_node->mNumChildren];
  631. for(aiNode* node: ch_node) con_node->mChildren[ch_idx++] = node;
  632. // and place "root" of <constellation> node to node list
  633. pNodeList.push_back(con_node);
  634. }
  635. void AMFImporter::Postprocess_BuildScene(aiScene* pScene)
  636. {
  637. std::list<aiNode*> node_list;
  638. std::list<aiMesh*> mesh_list;
  639. std::list<CAMFImporter_NodeElement_Metadata*> meta_list;
  640. //
  641. // Because for AMF "material" is just complex colors mixing so aiMaterial will not be used.
  642. // For building aiScene we are must to do few steps:
  643. // at first creating root node for aiScene.
  644. pScene->mRootNode = new aiNode;
  645. pScene->mRootNode->mParent = nullptr;
  646. pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
  647. // search for root(<amf>) element
  648. CAMFImporter_NodeElement* root_el = nullptr;
  649. for(CAMFImporter_NodeElement* ne: mNodeElement_List)
  650. {
  651. if(ne->Type != CAMFImporter_NodeElement::ENET_Root) continue;
  652. root_el = ne;
  653. break;
  654. }// for(const CAMFImporter_NodeElement* ne: mNodeElement_List)
  655. // Check if root element are found.
  656. if(root_el == nullptr) throw DeadlyImportError("Root(<amf>) element not found.");
  657. // after that walk thru children of root and collect data. Five types of nodes can be placed at top level - in <amf>: <object>, <material>, <texture>,
  658. // <constellation> and <metadata>. But at first we must read <material> and <texture> because they will be used in <object>. <metadata> can be read
  659. // at any moment.
  660. //
  661. // 1. <material>
  662. // 2. <texture> will be converted later when processing triangles list. \sa Postprocess_BuildMeshSet
  663. for(const CAMFImporter_NodeElement* root_child: root_el->Child)
  664. {
  665. if(root_child->Type == CAMFImporter_NodeElement::ENET_Material) Postprocess_BuildMaterial(*((CAMFImporter_NodeElement_Material*)root_child));
  666. }
  667. // After "appearance" nodes we must read <object> because it will be used in <constellation> -> <instance>.
  668. //
  669. // 3. <object>
  670. for(const CAMFImporter_NodeElement* root_child: root_el->Child)
  671. {
  672. if(root_child->Type == CAMFImporter_NodeElement::ENET_Object)
  673. {
  674. aiNode* tnode = nullptr;
  675. // for <object> mesh and node must be built: object ID assigned to aiNode name and will be used in future for <instance>
  676. Postprocess_BuildNodeAndObject(*((CAMFImporter_NodeElement_Object*)root_child), mesh_list, &tnode);
  677. if(tnode != nullptr) node_list.push_back(tnode);
  678. }
  679. }// for(const CAMFImporter_NodeElement* root_child: root_el->Child)
  680. // And finally read rest of nodes.
  681. //
  682. for(const CAMFImporter_NodeElement* root_child: root_el->Child)
  683. {
  684. // 4. <constellation>
  685. if(root_child->Type == CAMFImporter_NodeElement::ENET_Constellation)
  686. {
  687. // <object> and <constellation> at top of self abstraction use aiNode. So we can use only aiNode list for creating new aiNode's.
  688. Postprocess_BuildConstellation(*((CAMFImporter_NodeElement_Constellation*)root_child), node_list);
  689. }
  690. // 5, <metadata>
  691. if(root_child->Type == CAMFImporter_NodeElement::ENET_Metadata) meta_list.push_back((CAMFImporter_NodeElement_Metadata*)root_child);
  692. }// for(const CAMFImporter_NodeElement* root_child: root_el->Child)
  693. // at now we can add collected metadata to root node
  694. Postprocess_AddMetadata(meta_list, *pScene->mRootNode);
  695. //
  696. // Check constellation children
  697. //
  698. // As said in specification:
  699. // "When multiple objects and constellations are defined in a single file, only the top level objects and constellations are available for printing."
  700. // What that means? For example: if some object is used in constellation then you must show only constellation but not original object.
  701. // And at this step we are checking that relations.
  702. nl_clean_loop:
  703. if(node_list.size() > 1)
  704. {
  705. // walk thru all nodes
  706. for(std::list<aiNode*>::iterator nl_it = node_list.begin(); nl_it != node_list.end(); nl_it++)
  707. {
  708. // and try to find them in another top nodes.
  709. std::list<aiNode*>::const_iterator next_it = nl_it;
  710. next_it++;
  711. for(; next_it != node_list.end(); next_it++)
  712. {
  713. if((*next_it)->FindNode((*nl_it)->mName) != nullptr)
  714. {
  715. // if current top node(nl_it) found in another top node then erase it from node_list and restart search loop.
  716. node_list.erase(nl_it);
  717. goto nl_clean_loop;
  718. }
  719. }// for(; next_it != node_list.end(); next_it++)
  720. }// for(std::list<aiNode*>::const_iterator nl_it = node_list.begin(); nl_it != node_list.end(); nl_it++)
  721. }
  722. //
  723. // move created objects to aiScene
  724. //
  725. //
  726. // Nodes
  727. if(node_list.size() > 0)
  728. {
  729. std::list<aiNode*>::const_iterator nl_it = node_list.begin();
  730. pScene->mRootNode->mNumChildren = node_list.size();
  731. pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
  732. for(size_t i = 0; i < pScene->mRootNode->mNumChildren; i++)
  733. {
  734. // Objects and constellation that must be showed placed at top of hierarchy in <amf> node. So all aiNode's in node_list must have
  735. // mRootNode only as parent.
  736. (*nl_it)->mParent = pScene->mRootNode;
  737. pScene->mRootNode->mChildren[i] = *nl_it++;
  738. }
  739. }// if(node_list.size() > 0)
  740. //
  741. // Meshes
  742. if(mesh_list.size() > 0)
  743. {
  744. std::list<aiMesh*>::const_iterator ml_it = mesh_list.begin();
  745. pScene->mNumMeshes = mesh_list.size();
  746. pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
  747. for(size_t i = 0; i < pScene->mNumMeshes; i++) pScene->mMeshes[i] = *ml_it++;
  748. }// if(mesh_list.size() > 0)
  749. //
  750. // Textures
  751. pScene->mNumTextures = mTexture_Converted.size();
  752. if(pScene->mNumTextures > 0)
  753. {
  754. size_t idx;
  755. idx = 0;
  756. pScene->mTextures = new aiTexture*[pScene->mNumTextures];
  757. for(const SPP_Texture& tex_convd: mTexture_Converted)
  758. {
  759. pScene->mTextures[idx] = new aiTexture;
  760. pScene->mTextures[idx]->mWidth = tex_convd.Width;
  761. pScene->mTextures[idx]->mHeight = tex_convd.Height;
  762. pScene->mTextures[idx]->pcData = (aiTexel*)tex_convd.Data;
  763. // texture format description.
  764. strcpy(pScene->mTextures[idx]->achFormatHint, tex_convd.FormatHint);
  765. idx++;
  766. }// for(const SPP_Texture& tex_convd: mTexture_Converted)
  767. // Create materials for embedded textures.
  768. idx = 0;
  769. pScene->mNumMaterials = mTexture_Converted.size();
  770. pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
  771. for(const SPP_Texture& tex_convd: mTexture_Converted)
  772. {
  773. const aiString texture_id(AI_EMBEDDED_TEXNAME_PREFIX + std::to_string(idx));
  774. const int mode = aiTextureOp_Multiply;
  775. const int repeat = tex_convd.Tiled ? 1 : 0;
  776. pScene->mMaterials[idx] = new aiMaterial;
  777. pScene->mMaterials[idx]->AddProperty(&texture_id, AI_MATKEY_TEXTURE_DIFFUSE(0));
  778. pScene->mMaterials[idx]->AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0));
  779. pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
  780. pScene->mMaterials[idx]->AddProperty(&repeat, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
  781. idx++;
  782. }
  783. }// if(pScene->mNumTextures > 0)
  784. }// END: after that walk thru children of root and collect data
  785. }// namespace Assimp
  786. #endif // !ASSIMP_BUILD_NO_AMF_IMPORTER