AMFImporter.cpp 22 KB

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