AMFImporter.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. /// \file AMFImporter.cpp
  2. /// \brief AMF-format files importer for Assimp: main algorithm implementation.
  3. /// \date 2016
  4. /// \author [email protected]
  5. #ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
  6. // Header files, Assimp.
  7. #include "AMFImporter.hpp"
  8. #include "AMFImporter_Macro.hpp"
  9. #include "fast_atof.h"
  10. #include "DefaultIOSystem.h"
  11. // Header files, stdlib.
  12. #include <memory>
  13. #include <string>
  14. namespace Assimp
  15. {
  16. /// \var aiImporterDesc AMFImporter::Description
  17. /// Conastant which hold importer description
  18. const aiImporterDesc AMFImporter::Description = {
  19. "Additive manufacturing file format(AMF) Importer",
  20. "smalcom",
  21. "",
  22. "See documentation in source code. Chapter: Limitations.",
  23. aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
  24. 0,
  25. 0,
  26. 0,
  27. 0,
  28. "amf"
  29. };
  30. void AMFImporter::Clear()
  31. {
  32. mNodeElement_Cur = nullptr;
  33. mUnit.clear();
  34. mMaterial_Converted.clear();
  35. mTexture_Converted.clear();
  36. // Delete all elements
  37. if(mNodeElement_List.size())
  38. {
  39. for(CAMFImporter_NodeElement* ne: mNodeElement_List) { delete ne; }
  40. mNodeElement_List.clear();
  41. }
  42. }
  43. AMFImporter::~AMFImporter()
  44. {
  45. if(mReader != nullptr) delete mReader;
  46. // Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
  47. Clear();
  48. }
  49. /*********************************************************************************************************************************************/
  50. /************************************************************ Functions: find set ************************************************************/
  51. /*********************************************************************************************************************************************/
  52. bool AMFImporter::Find_NodeElement(const std::string& pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement** pNodeElement) const
  53. {
  54. for(CAMFImporter_NodeElement* ne: mNodeElement_List)
  55. {
  56. if((ne->ID == pID) && (ne->Type == pType))
  57. {
  58. if(pNodeElement != nullptr) *pNodeElement = ne;
  59. return true;
  60. }
  61. }// for(CAMFImporter_NodeElement* ne: mNodeElement_List)
  62. return false;
  63. }
  64. bool AMFImporter::Find_ConvertedNode(const std::string& pID, std::list<aiNode*>& pNodeList, aiNode** pNode) const
  65. {
  66. aiString node_name(pID.c_str());
  67. for(aiNode* node: pNodeList)
  68. {
  69. if(node->mName == node_name)
  70. {
  71. if(pNode != nullptr) *pNode = node;
  72. return true;
  73. }
  74. }// for(aiNode* node: pNodeList)
  75. return false;
  76. }
  77. bool AMFImporter::Find_ConvertedMaterial(const std::string& pID, const SPP_Material** pConvertedMaterial) const
  78. {
  79. for(const SPP_Material& mat: mMaterial_Converted)
  80. {
  81. if(mat.ID == pID)
  82. {
  83. if(pConvertedMaterial != nullptr) *pConvertedMaterial = &mat;
  84. return true;
  85. }
  86. }// for(const SPP_Material& mat: mMaterial_Converted)
  87. return false;
  88. }
  89. /*********************************************************************************************************************************************/
  90. /************************************************************ Functions: throw set ***********************************************************/
  91. /*********************************************************************************************************************************************/
  92. void AMFImporter::Throw_CloseNotFound(const std::string& pNode)
  93. {
  94. throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
  95. }
  96. void AMFImporter::Throw_IncorrectAttr(const std::string& pAttrName)
  97. {
  98. throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
  99. }
  100. void AMFImporter::Throw_IncorrectAttrValue(const std::string& pAttrName)
  101. {
  102. throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
  103. }
  104. void AMFImporter::Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription)
  105. {
  106. throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
  107. }
  108. void AMFImporter::Throw_ID_NotFound(const std::string& pID) const
  109. {
  110. throw DeadlyImportError("Not found node with name \"" + pID + "\".");
  111. }
  112. /*********************************************************************************************************************************************/
  113. /************************************************************* Functions: XML set ************************************************************/
  114. /*********************************************************************************************************************************************/
  115. void AMFImporter::XML_CheckNode_MustHaveChildren()
  116. {
  117. if(mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must have children.");
  118. }
  119. void AMFImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
  120. {
  121. const size_t Uns_Skip_Len = 3;
  122. const char* Uns_Skip[Uns_Skip_Len] = { "composite", "edge", "normal" };
  123. static bool skipped_before[Uns_Skip_Len] = { false, false, false };
  124. std::string nn(mReader->getNodeName());
  125. bool found = false;
  126. bool close_found = false;
  127. size_t sk_idx;
  128. for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
  129. {
  130. if(nn != Uns_Skip[sk_idx]) continue;
  131. found = true;
  132. if(mReader->isEmptyElement())
  133. {
  134. close_found = true;
  135. goto casu_cres;
  136. }
  137. while(mReader->read())
  138. {
  139. if((mReader->getNodeType() == irr::io::EXN_ELEMENT_END) && (nn == mReader->getNodeName()))
  140. {
  141. close_found = true;
  142. goto casu_cres;
  143. }
  144. }
  145. }// for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
  146. casu_cres:
  147. if(!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
  148. if(!close_found) Throw_CloseNotFound(nn);
  149. if(!skipped_before[sk_idx])
  150. {
  151. skipped_before[sk_idx] = true;
  152. LogWarning("Skipping node \"" + nn + "\" in " + pParentNodeName + ".");
  153. }
  154. }
  155. bool AMFImporter::XML_SearchNode(const std::string& pNodeName)
  156. {
  157. while(mReader->read())
  158. {
  159. if((mReader->getNodeType() == irr::io::EXN_ELEMENT) && XML_CheckNode_NameEqual(pNodeName)) return true;
  160. }
  161. return false;
  162. }
  163. bool AMFImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
  164. {
  165. std::string val(mReader->getAttributeValue(pAttrIdx));
  166. if((val == "false") || (val == "0"))
  167. return false;
  168. else if((val == "true") || (val == "1"))
  169. return true;
  170. else
  171. throw DeadlyImportError("Bool attribute value can contain \"false\"/\"0\" or \"true\"/\"1\" not the \"" + val + "\"");
  172. }
  173. float AMFImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
  174. {
  175. std::string val;
  176. float tvalf;
  177. ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
  178. fast_atoreal_move(val.c_str(), tvalf, false);
  179. return tvalf;
  180. }
  181. uint32_t AMFImporter::XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx)
  182. {
  183. return strtoul10(mReader->getAttributeValue(pAttrIdx));
  184. }
  185. float AMFImporter::XML_ReadNode_GetVal_AsFloat()
  186. {
  187. std::string val;
  188. float tvalf;
  189. if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. No data, seems file is corrupt.");
  190. if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. Invalid type of XML element, seems file is corrupt.");
  191. ParseHelper_FixTruncatedFloatString(mReader->getNodeData(), val);
  192. fast_atoreal_move(val.c_str(), tvalf, false);
  193. return tvalf;
  194. }
  195. uint32_t AMFImporter::XML_ReadNode_GetVal_AsU32()
  196. {
  197. if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. No data, seems file is corrupt.");
  198. if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. Invalid type of XML element, seems file is corrupt.");
  199. return strtoul10(mReader->getNodeData());
  200. }
  201. void AMFImporter::XML_ReadNode_GetVal_AsString(std::string& pValue)
  202. {
  203. if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsString. No data, seems file is corrupt.");
  204. if(mReader->getNodeType() != irr::io::EXN_TEXT)
  205. throw DeadlyImportError("XML_ReadNode_GetVal_AsString. Invalid type of XML element, seems file is corrupt.");
  206. pValue = mReader->getNodeData();
  207. }
  208. /*********************************************************************************************************************************************/
  209. /************************************************************ Functions: parse set ***********************************************************/
  210. /*********************************************************************************************************************************************/
  211. void AMFImporter::ParseHelper_Node_Enter(CAMFImporter_NodeElement* pNode)
  212. {
  213. mNodeElement_Cur->Child.push_back(pNode);// add new element to current element child list.
  214. mNodeElement_Cur = pNode;// switch current element to new one.
  215. }
  216. void AMFImporter::ParseHelper_Node_Exit()
  217. {
  218. // check if we can walk up.
  219. if(mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
  220. }
  221. void AMFImporter::ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
  222. {
  223. size_t instr_len;
  224. pOutString.clear();
  225. instr_len = strlen(pInStr);
  226. if(!instr_len) return;
  227. pOutString.reserve(instr_len * 3 / 2);
  228. // check and correct floats in format ".x". Must be "x.y".
  229. if(pInStr[0] == '.') pOutString.push_back('0');
  230. pOutString.push_back(pInStr[0]);
  231. for(size_t ci = 1; ci < instr_len; ci++)
  232. {
  233. if((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t')))
  234. {
  235. pOutString.push_back('0');
  236. pOutString.push_back('.');
  237. }
  238. else
  239. {
  240. pOutString.push_back(pInStr[ci]);
  241. }
  242. }
  243. }
  244. static bool ParseHelper_Decode_Base64_IsBase64(const char pChar)
  245. {
  246. return (isalnum(pChar) || (pChar == '+') || (pChar == '/'));
  247. }
  248. void AMFImporter::ParseHelper_Decode_Base64(const std::string& pInputBase64, std::vector<uint8_t>& pOutputData) const
  249. {
  250. // With help from
  251. // RenИ Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html
  252. const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  253. uint8_t tidx = 0;
  254. uint8_t arr4[4], arr3[3];
  255. // check input data
  256. if(pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
  257. // prepare output place
  258. pOutputData.clear();
  259. pOutputData.reserve(pInputBase64.size() / 4 * 3);
  260. for(size_t in_len = pInputBase64.size(), in_idx = 0; (in_len > 0) && (pInputBase64[in_idx] != '='); in_len--)
  261. {
  262. if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
  263. {
  264. arr4[tidx++] = pInputBase64[in_idx++];
  265. if(tidx == 4)
  266. {
  267. for(tidx = 0; tidx < 4; tidx++) arr4[tidx] = (uint8_t)base64_chars.find(arr4[tidx]);
  268. arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
  269. arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
  270. arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
  271. for(tidx = 0; tidx < 3; tidx++) pOutputData.push_back(arr3[tidx]);
  272. tidx = 0;
  273. }// if(tidx == 4)
  274. }// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
  275. else
  276. {
  277. in_idx++;
  278. }// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) else
  279. }
  280. if(tidx)
  281. {
  282. for(uint8_t i = tidx; i < 4; i++) arr4[i] = 0;
  283. for(uint8_t i = 0; i < 4; i++) arr4[i] = (uint8_t)(base64_chars.find(arr4[i]));
  284. arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
  285. arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
  286. arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
  287. for(uint8_t i = 0; i < (tidx - 1); i++) pOutputData.push_back(arr3[i]);
  288. }
  289. }
  290. void AMFImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
  291. {
  292. irr::io::IrrXMLReader* OldReader = mReader;// store current XMLreader.
  293. std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
  294. // Check whether we can read from the file
  295. if(file.get() == NULL) throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
  296. // generate a XML reader for it
  297. std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
  298. mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
  299. if(!mReader) throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
  300. //
  301. // start reading
  302. // search for root tag <amf>
  303. if(XML_SearchNode("amf"))
  304. ParseNode_Root();
  305. else
  306. throw DeadlyImportError("Root node \"amf\" not found.");
  307. delete mReader;
  308. // restore old XMLreader
  309. mReader = OldReader;
  310. }
  311. // <amf
  312. // unit="" - The units to be used. May be "inch", "millimeter", "meter", "feet", or "micron".
  313. // version="" - Version of file format.
  314. // >
  315. // </amf>
  316. // Root XML element.
  317. // Multi elements - No.
  318. void AMFImporter::ParseNode_Root()
  319. {
  320. std::string unit, version;
  321. CAMFImporter_NodeElement* ne;
  322. // Read attributes for node <amf>.
  323. MACRO_ATTRREAD_LOOPBEG;
  324. MACRO_ATTRREAD_CHECK_RET("unit", unit, mReader->getAttributeValue);
  325. MACRO_ATTRREAD_CHECK_RET("version", version, mReader->getAttributeValue);
  326. MACRO_ATTRREAD_LOOPEND_WSKIP;
  327. // Check attributes
  328. if(!mUnit.empty())
  329. {
  330. if((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) Throw_IncorrectAttrValue("unit");
  331. }
  332. // create root node element.
  333. ne = new CAMFImporter_NodeElement_Root(nullptr);
  334. mNodeElement_Cur = ne;// set first "current" element
  335. // and assign attribute's values
  336. ((CAMFImporter_NodeElement_Root*)ne)->Unit = unit;
  337. ((CAMFImporter_NodeElement_Root*)ne)->Version = version;
  338. // Check for child nodes
  339. if(!mReader->isEmptyElement())
  340. {
  341. MACRO_NODECHECK_LOOPBEGIN("amf");
  342. if(XML_CheckNode_NameEqual("object")) { ParseNode_Object(); continue; }
  343. if(XML_CheckNode_NameEqual("material")) { ParseNode_Material(); continue; }
  344. if(XML_CheckNode_NameEqual("texture")) { ParseNode_Texture(); continue; }
  345. if(XML_CheckNode_NameEqual("constellation")) { ParseNode_Constellation(); continue; }
  346. if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
  347. MACRO_NODECHECK_LOOPEND("amf");
  348. mNodeElement_Cur = ne;// force restore "current" element
  349. }// if(!mReader->isEmptyElement())
  350. mNodeElement_List.push_back(ne);// add to node element list because its a new object in graph.
  351. }
  352. // <constellation
  353. // id="" - The Object ID of the new constellation being defined.
  354. // >
  355. // </constellation>
  356. // A collection of objects or constellations with specific relative locations.
  357. // Multi elements - Yes.
  358. // Parent element - <amf>.
  359. void AMFImporter::ParseNode_Constellation()
  360. {
  361. std::string id;
  362. CAMFImporter_NodeElement* ne;
  363. // Read attributes for node <constellation>.
  364. MACRO_ATTRREAD_LOOPBEG;
  365. MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
  366. MACRO_ATTRREAD_LOOPEND;
  367. // create and if needed - define new grouping object.
  368. ne = new CAMFImporter_NodeElement_Constellation(mNodeElement_Cur);
  369. CAMFImporter_NodeElement_Constellation& als = *((CAMFImporter_NodeElement_Constellation*)ne);// alias for convenience
  370. if(!id.empty()) als.ID = id;
  371. // Check for child nodes
  372. if(!mReader->isEmptyElement())
  373. {
  374. ParseHelper_Node_Enter(ne);
  375. MACRO_NODECHECK_LOOPBEGIN("constellation");
  376. if(XML_CheckNode_NameEqual("instance")) { ParseNode_Instance(); continue; }
  377. if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
  378. MACRO_NODECHECK_LOOPEND("constellation");
  379. ParseHelper_Node_Exit();
  380. }// if(!mReader->isEmptyElement())
  381. else
  382. {
  383. mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
  384. }// if(!mReader->isEmptyElement()) else
  385. mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
  386. }
  387. // <instance
  388. // objectid="" - The Object ID of the new constellation being defined.
  389. // >
  390. // </instance>
  391. // A collection of objects or constellations with specific relative locations.
  392. // Multi elements - Yes.
  393. // Parent element - <amf>.
  394. void AMFImporter::ParseNode_Instance()
  395. {
  396. std::string objectid;
  397. CAMFImporter_NodeElement* ne;
  398. // Read attributes for node <constellation>.
  399. MACRO_ATTRREAD_LOOPBEG;
  400. MACRO_ATTRREAD_CHECK_RET("objectid", objectid, mReader->getAttributeValue);
  401. MACRO_ATTRREAD_LOOPEND;
  402. // used object id must be defined, check that.
  403. if(objectid.empty()) throw DeadlyImportError("\"objectid\" in <instance> must be defined.");
  404. // create and define new grouping object.
  405. ne = new CAMFImporter_NodeElement_Instance(mNodeElement_Cur);
  406. CAMFImporter_NodeElement_Instance& als = *((CAMFImporter_NodeElement_Instance*)ne);// alias for convenience
  407. als.ObjectID = objectid;
  408. // Check for child nodes
  409. if(!mReader->isEmptyElement())
  410. {
  411. bool read_flag[6] = { false, false, false, false, false, false };
  412. als.Delta.Set(0, 0, 0);
  413. als.Rotation.Set(0, 0, 0);
  414. ParseHelper_Node_Enter(ne);
  415. MACRO_NODECHECK_LOOPBEGIN("instance");
  416. MACRO_NODECHECK_READCOMP_F("deltax", read_flag[0], als.Delta.x);
  417. MACRO_NODECHECK_READCOMP_F("deltay", read_flag[1], als.Delta.y);
  418. MACRO_NODECHECK_READCOMP_F("deltaz", read_flag[2], als.Delta.z);
  419. MACRO_NODECHECK_READCOMP_F("rx", read_flag[3], als.Rotation.x);
  420. MACRO_NODECHECK_READCOMP_F("ry", read_flag[4], als.Rotation.y);
  421. MACRO_NODECHECK_READCOMP_F("rz", read_flag[5], als.Rotation.z);
  422. MACRO_NODECHECK_LOOPEND("instance");
  423. ParseHelper_Node_Exit();
  424. // also convert degrees to radians.
  425. als.Rotation.x = AI_MATH_PI_F * als.Rotation.x / 180.0f;
  426. als.Rotation.y = AI_MATH_PI_F * als.Rotation.y / 180.0f;
  427. als.Rotation.z = AI_MATH_PI_F * als.Rotation.z / 180.0f;
  428. }// if(!mReader->isEmptyElement())
  429. else
  430. {
  431. mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
  432. }// if(!mReader->isEmptyElement()) else
  433. mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
  434. }
  435. // <object
  436. // id="" - A unique ObjectID for the new object being defined.
  437. // >
  438. // </object>
  439. // An object definition.
  440. // Multi elements - Yes.
  441. // Parent element - <amf>.
  442. void AMFImporter::ParseNode_Object()
  443. {
  444. std::string id;
  445. CAMFImporter_NodeElement* ne;
  446. // Read attributes for node <object>.
  447. MACRO_ATTRREAD_LOOPBEG;
  448. MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
  449. MACRO_ATTRREAD_LOOPEND;
  450. // create and if needed - define new geometry object.
  451. ne = new CAMFImporter_NodeElement_Object(mNodeElement_Cur);
  452. CAMFImporter_NodeElement_Object& als = *((CAMFImporter_NodeElement_Object*)ne);// alias for convenience
  453. if(!id.empty()) als.ID = id;
  454. // Check for child nodes
  455. if(!mReader->isEmptyElement())
  456. {
  457. bool col_read = false;
  458. ParseHelper_Node_Enter(ne);
  459. MACRO_NODECHECK_LOOPBEGIN("object");
  460. if(XML_CheckNode_NameEqual("color"))
  461. {
  462. // Check if color already defined for object.
  463. if(col_read) Throw_MoreThanOnceDefined("color", "Only one color can be defined for <object>.");
  464. // read data and set flag about it
  465. ParseNode_Color();
  466. col_read = true;
  467. continue;
  468. }
  469. if(XML_CheckNode_NameEqual("mesh")) { ParseNode_Mesh(); continue; }
  470. if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
  471. MACRO_NODECHECK_LOOPEND("object");
  472. ParseHelper_Node_Exit();
  473. }// if(!mReader->isEmptyElement())
  474. else
  475. {
  476. mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
  477. }// if(!mReader->isEmptyElement()) else
  478. mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
  479. }
  480. // <metadata
  481. // type="" - The type of the attribute.
  482. // >
  483. // </metadata>
  484. // Specify additional information about an entity.
  485. // Multi elements - Yes.
  486. // Parent element - <amf>, <object>, <volume>, <material>, <vertex>.
  487. //
  488. // Reserved types are:
  489. // "Name" - The alphanumeric label of the entity, to be used by the interpreter if interacting with the user.
  490. // "Description" - A description of the content of the entity
  491. // "URL" - A link to an external resource relating to the entity
  492. // "Author" - Specifies the name(s) of the author(s) of the entity
  493. // "Company" - Specifying the company generating the entity
  494. // "CAD" - specifies the name of the originating CAD software and version
  495. // "Revision" - specifies the revision of the entity
  496. // "Tolerance" - specifies the desired manufacturing tolerance of the entity in entity's unit system
  497. // "Volume" - specifies the total volume of the entity, in the entity's unit system, to be used for verification (object and volume only)
  498. void AMFImporter::ParseNode_Metadata()
  499. {
  500. std::string type, value;
  501. CAMFImporter_NodeElement* ne;
  502. // read attribute
  503. MACRO_ATTRREAD_LOOPBEG;
  504. MACRO_ATTRREAD_CHECK_RET("type", type, mReader->getAttributeValue);
  505. MACRO_ATTRREAD_LOOPEND;
  506. // and value of node.
  507. value = mReader->getNodeData();
  508. // Create node element and assign read data.
  509. ne = new CAMFImporter_NodeElement_Metadata(mNodeElement_Cur);
  510. ((CAMFImporter_NodeElement_Metadata*)ne)->Type = type;
  511. ((CAMFImporter_NodeElement_Metadata*)ne)->Value = value;
  512. mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
  513. mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
  514. }
  515. /*********************************************************************************************************************************************/
  516. /******************************************************** Functions: BaseImporter set ********************************************************/
  517. /*********************************************************************************************************************************************/
  518. bool AMFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const
  519. {
  520. const std::string extension = GetExtension(pFile);
  521. if(extension == "amf") return true;
  522. if(!extension.length() || pCheckSig)
  523. {
  524. const char* tokens[] = { "<?xml", "<amf" };
  525. return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2);
  526. }
  527. return false;
  528. }
  529. void AMFImporter::GetExtensionList(std::set<std::string>& pExtensionList)
  530. {
  531. pExtensionList.insert("amf");
  532. }
  533. const aiImporterDesc* AMFImporter::GetInfo () const
  534. {
  535. return &Description;
  536. }
  537. void AMFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
  538. {
  539. Clear();// delete old graph.
  540. ParseFile(pFile, pIOHandler);
  541. Postprocess_BuildScene(pScene);
  542. // scene graph is ready, exit.
  543. }
  544. }// namespace Assimp
  545. #endif // !ASSIMP_BUILD_NO_AMF_IMPORTER