X3DImporter_Rendering.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992
  1. /*
  2. Open Asset Import Library (assimp)
  3. ----------------------------------------------------------------------
  4. Copyright (c) 2006-2019, assimp team
  5. All rights reserved.
  6. Redistribution and use of this software in source and binary forms,
  7. with or without modification, are permitted provided that the
  8. following conditions are met:
  9. * Redistributions of source code must retain the above
  10. copyright notice, this list of conditions and the
  11. following disclaimer.
  12. * Redistributions in binary form must reproduce the above
  13. copyright notice, this list of conditions and the
  14. following disclaimer in the documentation and/or other
  15. materials provided with the distribution.
  16. * Neither the name of the assimp team, nor the names of its
  17. contributors may be used to endorse or promote products
  18. derived from this software without specific prior
  19. written permission of the assimp team.
  20. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. ----------------------------------------------------------------------
  32. */
  33. /// \file X3DImporter_Rendering.cpp
  34. /// \brief Parsing data from nodes of "Rendering" set of X3D.
  35. /// \date 2015-2016
  36. /// \author [email protected]
  37. #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
  38. #include "X3DImporter.hpp"
  39. #include "X3DImporter_Macro.hpp"
  40. #include "X3DXmlHelper.h"
  41. namespace Assimp {
  42. // <Color
  43. // DEF="" ID
  44. // USE="" IDREF
  45. // color="" MFColor [inputOutput]
  46. // />
  47. void X3DImporter::readColor(XmlNode &node) {
  48. std::string use, def;
  49. std::list<aiColor3D> color;
  50. X3DNodeElementBase *ne(nullptr);
  51. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  52. X3DXmlHelper::getColor3DListAttribute(node, "color", color);
  53. // if "USE" defined then find already defined element.
  54. if (!use.empty()) {
  55. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Color, ne);
  56. } else {
  57. // create and if needed - define new geometry object.
  58. ne = new X3DNodeElementColor(mNodeElementCur);
  59. if (!def.empty()) ne->ID = def;
  60. ((X3DNodeElementColor *)ne)->Value = color;
  61. // check for X3DMetadataObject childs.
  62. if (!isNodeEmpty(node))
  63. childrenReadMetadata(node, ne, "Color");
  64. else
  65. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  66. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  67. } // if(!use.empty()) else
  68. }
  69. // <ColorRGBA
  70. // DEF="" ID
  71. // USE="" IDREF
  72. // color="" MFColorRGBA [inputOutput]
  73. // />
  74. void X3DImporter::readColorRGBA(XmlNode &node) {
  75. std::string use, def;
  76. std::list<aiColor4D> color;
  77. X3DNodeElementBase *ne(nullptr);
  78. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  79. X3DXmlHelper::getColor4DListAttribute(node, "color", color);
  80. // if "USE" defined then find already defined element.
  81. if (!use.empty()) {
  82. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ColorRGBA, ne);
  83. } else {
  84. // create and if needed - define new geometry object.
  85. ne = new X3DNodeElementColorRGBA(mNodeElementCur);
  86. if (!def.empty()) ne->ID = def;
  87. ((X3DNodeElementColorRGBA *)ne)->Value = color;
  88. // check for X3DMetadataObject childs.
  89. if (!isNodeEmpty(node))
  90. childrenReadMetadata(node, ne, "ColorRGBA");
  91. else
  92. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  93. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  94. } // if(!use.empty()) else
  95. }
  96. // <Coordinate
  97. // DEF="" ID
  98. // USE="" IDREF
  99. // point="" MFVec3f [inputOutput]
  100. // />
  101. void X3DImporter::readCoordinate(XmlNode &node) {
  102. std::string use, def;
  103. std::list<aiVector3D> point;
  104. X3DNodeElementBase *ne(nullptr);
  105. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  106. X3DXmlHelper::getVector3DListAttribute(node, "point", point);
  107. // if "USE" defined then find already defined element.
  108. if (!use.empty()) {
  109. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Coordinate, ne);
  110. } else {
  111. // create and if needed - define new geometry object.
  112. ne = new X3DNodeElementCoordinate(mNodeElementCur);
  113. if (!def.empty()) ne->ID = def;
  114. ((X3DNodeElementCoordinate *)ne)->Value = point;
  115. // check for X3DMetadataObject childs.
  116. if (!isNodeEmpty(node))
  117. childrenReadMetadata(node, ne, "Coordinate");
  118. else
  119. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  120. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  121. } // if(!use.empty()) else
  122. }
  123. // <IndexedLineSet
  124. // DEF="" ID
  125. // USE="" IDREF
  126. // colorIndex="" MFInt32 [initializeOnly]
  127. // colorPerVertex="true" SFBool [initializeOnly]
  128. // coordIndex="" MFInt32 [initializeOnly]
  129. // >
  130. // <!-- ColorCoordinateContentModel -->
  131. // ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
  132. // contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
  133. // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
  134. // </IndexedLineSet>
  135. void X3DImporter::readIndexedLineSet(XmlNode &node) {
  136. std::string use, def;
  137. std::vector<int32_t> colorIndex;
  138. bool colorPerVertex = true;
  139. std::vector<int32_t> coordIndex;
  140. X3DNodeElementBase *ne(nullptr);
  141. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  142. X3DXmlHelper::getInt32ArrayAttribute(node, "colorIndex", colorIndex);
  143. XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
  144. X3DXmlHelper::getInt32ArrayAttribute(node, "coordIndex", coordIndex);
  145. // if "USE" defined then find already defined element.
  146. if (!use.empty()) {
  147. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedLineSet, ne);
  148. } else {
  149. // check data
  150. if ((coordIndex.size() < 2) || ((coordIndex.back() == (-1)) && (coordIndex.size() < 3)))
  151. throw DeadlyImportError("IndexedLineSet must contain not empty \"coordIndex\" attribute.");
  152. // create and if needed - define new geometry object.
  153. ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedLineSet, mNodeElementCur);
  154. if (!def.empty()) ne->ID = def;
  155. X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne);
  156. ne_alias.ColorIndex = colorIndex;
  157. ne_alias.ColorPerVertex = colorPerVertex;
  158. ne_alias.CoordIndex = coordIndex;
  159. // check for child nodes
  160. if (!isNodeEmpty(node)) {
  161. ParseHelper_Node_Enter(ne);
  162. for (auto currentChildNode : node.children()) {
  163. const std::string &currentChildName = currentChildNode.name();
  164. // check for Color and Coordinate nodes
  165. if (currentChildName == "Color")
  166. readColor(currentChildNode);
  167. else if (currentChildName == "ColorRGBA")
  168. readColorRGBA(currentChildNode);
  169. else if (currentChildName == "Coordinate")
  170. readCoordinate(currentChildNode);
  171. // check for X3DMetadataObject
  172. else if (!checkForMetadataNode(currentChildNode))
  173. skipUnsupportedNode("IndexedLineSet", currentChildNode);
  174. }
  175. ParseHelper_Node_Exit();
  176. } // if(!isNodeEmpty(node))
  177. else {
  178. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  179. }
  180. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  181. } // if(!use.empty()) else
  182. }
  183. // <IndexedTriangleFanSet
  184. // DEF="" ID
  185. // USE="" IDREF
  186. // ccw="true" SFBool [initializeOnly]
  187. // colorPerVertex="true" SFBool [initializeOnly]
  188. // index="" MFInt32 [initializeOnly]
  189. // normalPerVertex="true" SFBool [initializeOnly]
  190. // solid="true" SFBool [initializeOnly]
  191. // >
  192. // <!-- ComposedGeometryContentModel -->
  193. // ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
  194. // Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
  195. // Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
  196. // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
  197. // </IndexedTriangleFanSet>
  198. void X3DImporter::readIndexedTriangleFanSet(XmlNode &node) {
  199. std::string use, def;
  200. bool ccw = true;
  201. bool colorPerVertex = true;
  202. std::vector<int32_t> index;
  203. bool normalPerVertex = true;
  204. bool solid = true;
  205. X3DNodeElementBase *ne(nullptr);
  206. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  207. XmlParser::getBoolAttribute(node, "ccw", ccw);
  208. XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
  209. X3DXmlHelper::getInt32ArrayAttribute(node, "index", index);
  210. XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
  211. XmlParser::getBoolAttribute(node, "solid", solid);
  212. // if "USE" defined then find already defined element.
  213. if (!use.empty()) {
  214. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleFanSet, ne);
  215. } else {
  216. // check data
  217. if (index.size() == 0) throw DeadlyImportError("IndexedTriangleFanSet must contain not empty \"index\" attribute.");
  218. // create and if needed - define new geometry object.
  219. ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleFanSet, mNodeElementCur);
  220. if (!def.empty()) ne->ID = def;
  221. X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne);
  222. ne_alias.CCW = ccw;
  223. ne_alias.ColorPerVertex = colorPerVertex;
  224. ne_alias.NormalPerVertex = normalPerVertex;
  225. ne_alias.Solid = solid;
  226. ne_alias.CoordIndex.clear();
  227. int counter = 0;
  228. int32_t idx[3];
  229. for (std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) {
  230. idx[2] = *idx_it;
  231. if (idx[2] < 0) {
  232. counter = 0;
  233. } else {
  234. if (counter >= 2) {
  235. if (ccw) {
  236. ne_alias.CoordIndex.push_back(idx[0]);
  237. ne_alias.CoordIndex.push_back(idx[1]);
  238. ne_alias.CoordIndex.push_back(idx[2]);
  239. } else {
  240. ne_alias.CoordIndex.push_back(idx[0]);
  241. ne_alias.CoordIndex.push_back(idx[2]);
  242. ne_alias.CoordIndex.push_back(idx[1]);
  243. }
  244. ne_alias.CoordIndex.push_back(-1);
  245. idx[1] = idx[2];
  246. } else {
  247. idx[counter] = idx[2];
  248. }
  249. ++counter;
  250. }
  251. } // for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++)
  252. // check for child nodes
  253. if (!isNodeEmpty(node)) {
  254. ParseHelper_Node_Enter(ne);
  255. for (auto currentChildNode : node.children()) {
  256. const std::string &currentChildName = currentChildNode.name();
  257. // check for X3DComposedGeometryNodes
  258. if (currentChildName == "Color")
  259. readColor(currentChildNode);
  260. else if (currentChildName == "ColorRGBA")
  261. readColorRGBA(currentChildNode);
  262. else if (currentChildName == "Coordinate")
  263. readCoordinate(currentChildNode);
  264. else if (currentChildName == "Normal")
  265. readNormal(currentChildNode);
  266. else if (currentChildName == "TextureCoordinate")
  267. readTextureCoordinate(currentChildNode);
  268. // check for X3DMetadataObject
  269. else if (!checkForMetadataNode(currentChildNode))
  270. skipUnsupportedNode("IndexedTriangleFanSet", currentChildNode);
  271. }
  272. ParseHelper_Node_Exit();
  273. } // if(!isNodeEmpty(node))
  274. else {
  275. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  276. }
  277. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  278. } // if(!use.empty()) else
  279. }
  280. // <IndexedTriangleSet
  281. // DEF="" ID
  282. // USE="" IDREF
  283. // ccw="true" SFBool [initializeOnly]
  284. // colorPerVertex="true" SFBool [initializeOnly]
  285. // index="" MFInt32 [initializeOnly]
  286. // normalPerVertex="true" SFBool [initializeOnly]
  287. // solid="true" SFBool [initializeOnly]
  288. // >
  289. // <!-- ComposedGeometryContentModel -->
  290. // ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
  291. // Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
  292. // Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
  293. // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
  294. // </IndexedTriangleSet>
  295. void X3DImporter::readIndexedTriangleSet(XmlNode &node) {
  296. std::string use, def;
  297. bool ccw = true;
  298. bool colorPerVertex = true;
  299. std::vector<int32_t> index;
  300. bool normalPerVertex = true;
  301. bool solid = true;
  302. X3DNodeElementBase *ne(nullptr);
  303. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  304. XmlParser::getBoolAttribute(node, "ccw", ccw);
  305. XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
  306. X3DXmlHelper::getInt32ArrayAttribute(node, "index", index);
  307. XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
  308. XmlParser::getBoolAttribute(node, "solid", solid);
  309. // if "USE" defined then find already defined element.
  310. if (!use.empty()) {
  311. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleSet, ne);
  312. } else {
  313. // check data
  314. if (index.size() == 0) throw DeadlyImportError("IndexedTriangleSet must contain not empty \"index\" attribute.");
  315. // create and if needed - define new geometry object.
  316. ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleSet, mNodeElementCur);
  317. if (!def.empty()) ne->ID = def;
  318. X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne);
  319. ne_alias.CCW = ccw;
  320. ne_alias.ColorPerVertex = colorPerVertex;
  321. ne_alias.NormalPerVertex = normalPerVertex;
  322. ne_alias.Solid = solid;
  323. ne_alias.CoordIndex.clear();
  324. int counter = 0;
  325. int32_t idx[3];
  326. for (std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) {
  327. idx[counter++] = *idx_it;
  328. if (counter > 2) {
  329. counter = 0;
  330. if (ccw) {
  331. ne_alias.CoordIndex.push_back(idx[0]);
  332. ne_alias.CoordIndex.push_back(idx[1]);
  333. ne_alias.CoordIndex.push_back(idx[2]);
  334. } else {
  335. ne_alias.CoordIndex.push_back(idx[0]);
  336. ne_alias.CoordIndex.push_back(idx[2]);
  337. ne_alias.CoordIndex.push_back(idx[1]);
  338. }
  339. ne_alias.CoordIndex.push_back(-1);
  340. }
  341. } // for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++)
  342. // check for child nodes
  343. if (!isNodeEmpty(node)) {
  344. ParseHelper_Node_Enter(ne);
  345. for (auto currentChildNode : node.children()) {
  346. const std::string &currentChildName = currentChildNode.name();
  347. // check for X3DComposedGeometryNodes
  348. if (currentChildName == "Color")
  349. readColor(currentChildNode);
  350. else if (currentChildName == "ColorRGBA")
  351. readColorRGBA(currentChildNode);
  352. else if (currentChildName == "Coordinate")
  353. readCoordinate(currentChildNode);
  354. else if (currentChildName == "Normal")
  355. readNormal(currentChildNode);
  356. else if (currentChildName == "TextureCoordinate")
  357. readTextureCoordinate(currentChildNode);
  358. // check for X3DMetadataObject
  359. else if (!checkForMetadataNode(currentChildNode))
  360. skipUnsupportedNode("IndexedTriangleSet", currentChildNode);
  361. }
  362. ParseHelper_Node_Exit();
  363. } // if(!isNodeEmpty(node))
  364. else {
  365. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  366. }
  367. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  368. } // if(!use.empty()) else
  369. }
  370. // <IndexedTriangleStripSet
  371. // DEF="" ID
  372. // USE="" IDREF
  373. // ccw="true" SFBool [initializeOnly]
  374. // colorPerVertex="true" SFBool [initializeOnly]
  375. // index="" MFInt32 [initializeOnly]
  376. // normalPerVertex="true" SFBool [initializeOnly]
  377. // solid="true" SFBool [initializeOnly]
  378. // >
  379. // <!-- ComposedGeometryContentModel -->
  380. // ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
  381. // Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
  382. // Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
  383. // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
  384. // </IndexedTriangleStripSet>
  385. void X3DImporter::readIndexedTriangleStripSet(XmlNode &node) {
  386. std::string use, def;
  387. bool ccw = true;
  388. bool colorPerVertex = true;
  389. std::vector<int32_t> index;
  390. bool normalPerVertex = true;
  391. bool solid = true;
  392. X3DNodeElementBase *ne(nullptr);
  393. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  394. XmlParser::getBoolAttribute(node, "ccw", ccw);
  395. XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
  396. X3DXmlHelper::getInt32ArrayAttribute(node, "index", index);
  397. XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
  398. XmlParser::getBoolAttribute(node, "solid", solid);
  399. // if "USE" defined then find already defined element.
  400. if (!use.empty()) {
  401. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleStripSet, ne);
  402. } else {
  403. // check data
  404. if (index.empty()) {
  405. throw DeadlyImportError("IndexedTriangleStripSet must contain not empty \"index\" attribute.");
  406. }
  407. // create and if needed - define new geometry object.
  408. ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleStripSet, mNodeElementCur);
  409. if (!def.empty()) ne->ID = def;
  410. X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne);
  411. ne_alias.CCW = ccw;
  412. ne_alias.ColorPerVertex = colorPerVertex;
  413. ne_alias.NormalPerVertex = normalPerVertex;
  414. ne_alias.Solid = solid;
  415. ne_alias.CoordIndex.clear();
  416. int counter = 0;
  417. int32_t idx[3];
  418. for (std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) {
  419. idx[2] = *idx_it;
  420. if (idx[2] < 0) {
  421. counter = 0;
  422. } else {
  423. if (counter >= 2) {
  424. if (ccw) {
  425. ne_alias.CoordIndex.push_back(idx[0]);
  426. ne_alias.CoordIndex.push_back(idx[1]);
  427. ne_alias.CoordIndex.push_back(idx[2]);
  428. } else {
  429. ne_alias.CoordIndex.push_back(idx[0]);
  430. ne_alias.CoordIndex.push_back(idx[2]);
  431. ne_alias.CoordIndex.push_back(idx[1]);
  432. }
  433. ne_alias.CoordIndex.push_back(-1);
  434. }
  435. idx[counter & 1] = idx[2];
  436. ++counter;
  437. }
  438. } // for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++)
  439. // check for child nodes
  440. if (!isNodeEmpty(node)) {
  441. ParseHelper_Node_Enter(ne);
  442. for (auto currentChildNode : node.children()) {
  443. const std::string &currentChildName = currentChildNode.name();
  444. // check for X3DComposedGeometryNodes
  445. if (currentChildName == "Color")
  446. readColor(currentChildNode);
  447. else if (currentChildName == "ColorRGBA")
  448. readColorRGBA(currentChildNode);
  449. else if (currentChildName == "Coordinate")
  450. readCoordinate(currentChildNode);
  451. else if (currentChildName == "Normal")
  452. readNormal(currentChildNode);
  453. else if (currentChildName == "TextureCoordinate")
  454. readTextureCoordinate(currentChildNode);
  455. // check for X3DMetadataObject
  456. else if (!checkForMetadataNode(currentChildNode))
  457. skipUnsupportedNode("IndexedTriangleStripSet", currentChildNode);
  458. }
  459. ParseHelper_Node_Exit();
  460. } // if(!isNodeEmpty(node))
  461. else {
  462. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  463. }
  464. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  465. } // if(!use.empty()) else
  466. }
  467. // <LineSet
  468. // DEF="" ID
  469. // USE="" IDREF
  470. // vertexCount="" MFInt32 [initializeOnly]
  471. // >
  472. // <!-- ColorCoordinateContentModel -->
  473. // ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
  474. // contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
  475. // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
  476. // </LineSet>
  477. void X3DImporter::readLineSet(XmlNode &node) {
  478. std::string use, def;
  479. std::vector<int32_t> vertexCount;
  480. X3DNodeElementBase *ne(nullptr);
  481. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  482. X3DXmlHelper::getInt32ArrayAttribute(node, "vertexCount", vertexCount);
  483. // if "USE" defined then find already defined element.
  484. if (!use.empty()) {
  485. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_LineSet, ne);
  486. } else {
  487. // check data
  488. if (vertexCount.empty()) {
  489. throw DeadlyImportError("LineSet must contain not empty \"vertexCount\" attribute.");
  490. }
  491. // create and if needed - define new geometry object.
  492. ne = new X3DNodeElementSet(X3DElemType::ENET_LineSet, mNodeElementCur);
  493. if (!def.empty()) ne->ID = def;
  494. X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne);
  495. ne_alias.VertexCount = vertexCount;
  496. // create CoordIdx
  497. size_t coord_num = 0;
  498. ne_alias.CoordIndex.clear();
  499. for (std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) {
  500. if (*vc_it < 2) throw DeadlyImportError("LineSet. vertexCount shall be greater than or equal to two.");
  501. for (int32_t i = 0; i < *vc_it; i++)
  502. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num++)); // add vertices indices
  503. ne_alias.CoordIndex.push_back(-1); // add face delimiter.
  504. }
  505. // check for child nodes
  506. if (!isNodeEmpty(node)) {
  507. ParseHelper_Node_Enter(ne);
  508. for (auto currentChildNode : node.children()) {
  509. const std::string &currentChildName = currentChildNode.name();
  510. // check for X3DComposedGeometryNodes
  511. if (currentChildName == "Color")
  512. readColor(currentChildNode);
  513. else if (currentChildName == "ColorRGBA")
  514. readColorRGBA(currentChildNode);
  515. else if (currentChildName == "Coordinate")
  516. readCoordinate(currentChildNode);
  517. // check for X3DMetadataObject
  518. else if (!checkForMetadataNode(currentChildNode))
  519. skipUnsupportedNode("LineSet", currentChildNode);
  520. }
  521. ParseHelper_Node_Exit();
  522. } // if(!isNodeEmpty(node))
  523. else {
  524. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  525. }
  526. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  527. } // if(!use.empty()) else
  528. }
  529. // <PointSet
  530. // DEF="" ID
  531. // USE="" IDREF
  532. // >
  533. // <!-- ColorCoordinateContentModel -->
  534. // ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
  535. // contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
  536. // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
  537. // </PointSet>
  538. void X3DImporter::readPointSet(XmlNode &node) {
  539. std::string use, def;
  540. X3DNodeElementBase *ne(nullptr);
  541. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  542. // if "USE" defined then find already defined element.
  543. if (!use.empty()) {
  544. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_PointSet, ne);
  545. } else {
  546. // create and if needed - define new geometry object.
  547. ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_PointSet, mNodeElementCur);
  548. if (!def.empty()) ne->ID = def;
  549. // check for child nodes
  550. if (!isNodeEmpty(node)) {
  551. ParseHelper_Node_Enter(ne);
  552. for (auto currentChildNode : node.children()) {
  553. const std::string &currentChildName = currentChildNode.name();
  554. // check for X3DComposedGeometryNodes
  555. if (currentChildName == "Color")
  556. readColor(currentChildNode);
  557. else if (currentChildName == "ColorRGBA")
  558. readColorRGBA(currentChildNode);
  559. else if (currentChildName == "Coordinate")
  560. readCoordinate(currentChildNode);
  561. // check for X3DMetadataObject
  562. else if (!checkForMetadataNode(currentChildNode))
  563. skipUnsupportedNode("PointSet", currentChildNode);
  564. }
  565. ParseHelper_Node_Exit();
  566. } // if(!isNodeEmpty(node))
  567. else {
  568. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  569. }
  570. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  571. } // if(!use.empty()) else
  572. }
  573. // <TriangleFanSet
  574. // DEF="" ID
  575. // USE="" IDREF
  576. // ccw="true" SFBool [initializeOnly]
  577. // colorPerVertex="true" SFBool [initializeOnly]
  578. // fanCount="" MFInt32 [inputOutput]
  579. // normalPerVertex="true" SFBool [initializeOnly]
  580. // solid="true" SFBool [initializeOnly]
  581. // >
  582. // <!-- ComposedGeometryContentModel -->
  583. // ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
  584. // Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
  585. // Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
  586. // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
  587. // </TriangleFanSet>
  588. void X3DImporter::readTriangleFanSet(XmlNode &node) {
  589. std::string use, def;
  590. bool ccw = true;
  591. bool colorPerVertex = true;
  592. std::vector<int32_t> fanCount;
  593. bool normalPerVertex = true;
  594. bool solid = true;
  595. X3DNodeElementBase *ne(nullptr);
  596. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  597. XmlParser::getBoolAttribute(node, "ccw", ccw);
  598. XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
  599. X3DXmlHelper::getInt32ArrayAttribute(node, "fanCount", fanCount);
  600. XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
  601. XmlParser::getBoolAttribute(node, "solid", solid);
  602. // if "USE" defined then find already defined element.
  603. if (!use.empty()) {
  604. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleFanSet, ne);
  605. } else {
  606. // check data
  607. if (fanCount.empty()) {
  608. throw DeadlyImportError("TriangleFanSet must contain not empty \"fanCount\" attribute.");
  609. }
  610. // create and if needed - define new geometry object.
  611. ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleFanSet, mNodeElementCur);
  612. if (!def.empty()) ne->ID = def;
  613. X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne);
  614. ne_alias.CCW = ccw;
  615. ne_alias.ColorPerVertex = colorPerVertex;
  616. ne_alias.VertexCount = fanCount;
  617. ne_alias.NormalPerVertex = normalPerVertex;
  618. ne_alias.Solid = solid;
  619. // create CoordIdx
  620. size_t coord_num_first, coord_num_prev;
  621. ne_alias.CoordIndex.clear();
  622. // assign indices for first triangle
  623. coord_num_first = 0;
  624. coord_num_prev = 1;
  625. for (std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) {
  626. if (*vc_it < 3) throw DeadlyImportError("TriangleFanSet. fanCount shall be greater than or equal to three.");
  627. for (int32_t vc = 2; vc < *vc_it; vc++) {
  628. if (ccw) {
  629. // 2 1
  630. // 0
  631. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_first)); // first vertex is a center and always is [0].
  632. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev++));
  633. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev));
  634. } else {
  635. // 1 2
  636. // 0
  637. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_first)); // first vertex is a center and always is [0].
  638. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev + 1));
  639. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev++));
  640. } // if(ccw) else
  641. ne_alias.CoordIndex.push_back(-1); // add face delimiter.
  642. } // for(int32_t vc = 2; vc < *vc_it; vc++)
  643. coord_num_prev++; // that index will be center of next fan
  644. coord_num_first = coord_num_prev++; // forward to next point - second point of fan
  645. } // for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
  646. // check for child nodes
  647. if (!isNodeEmpty(node)) {
  648. ParseHelper_Node_Enter(ne);
  649. for (auto currentChildNode : node.children()) {
  650. const std::string &currentChildName = currentChildNode.name();
  651. // check for X3DComposedGeometryNodes
  652. if (currentChildName == "Color")
  653. readColor(currentChildNode);
  654. else if (currentChildName == "ColorRGBA")
  655. readColorRGBA(currentChildNode);
  656. else if (currentChildName == "Coordinate")
  657. readCoordinate(currentChildNode);
  658. else if (currentChildName == "Normal")
  659. readNormal(currentChildNode);
  660. else if (currentChildName == "TextureCoordinate")
  661. readTextureCoordinate(currentChildNode);
  662. // check for X3DMetadataObject
  663. else if (!checkForMetadataNode(currentChildNode))
  664. skipUnsupportedNode("TriangleFanSet", currentChildNode);
  665. }
  666. ParseHelper_Node_Exit();
  667. } // if(!isNodeEmpty(node))
  668. else {
  669. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  670. }
  671. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  672. } // if(!use.empty()) else
  673. }
  674. // <TriangleSet
  675. // DEF="" ID
  676. // USE="" IDREF
  677. // ccw="true" SFBool [initializeOnly]
  678. // colorPerVertex="true" SFBool [initializeOnly]
  679. // normalPerVertex="true" SFBool [initializeOnly]
  680. // solid="true" SFBool [initializeOnly]
  681. // >
  682. // <!-- ComposedGeometryContentModel -->
  683. // ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
  684. // Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
  685. // Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
  686. // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
  687. // </TriangleSet>
  688. void X3DImporter::readTriangleSet(XmlNode &node) {
  689. std::string use, def;
  690. bool ccw = true;
  691. bool colorPerVertex = true;
  692. bool normalPerVertex = true;
  693. bool solid = true;
  694. X3DNodeElementBase *ne(nullptr);
  695. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  696. XmlParser::getBoolAttribute(node, "ccw", ccw);
  697. XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
  698. XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
  699. XmlParser::getBoolAttribute(node, "solid", solid);
  700. // if "USE" defined then find already defined element.
  701. if (!use.empty()) {
  702. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleSet, ne);
  703. } else {
  704. // create and if needed - define new geometry object.
  705. ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_TriangleSet, mNodeElementCur);
  706. if (!def.empty()) ne->ID = def;
  707. X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne);
  708. ne_alias.CCW = ccw;
  709. ne_alias.ColorPerVertex = colorPerVertex;
  710. ne_alias.NormalPerVertex = normalPerVertex;
  711. ne_alias.Solid = solid;
  712. // check for child nodes
  713. if (!isNodeEmpty(node)) {
  714. ParseHelper_Node_Enter(ne);
  715. for (auto currentChildNode : node.children()) {
  716. const std::string &currentChildName = currentChildNode.name();
  717. // check for X3DComposedGeometryNodes
  718. if (currentChildName == "Color")
  719. readColor(currentChildNode);
  720. else if (currentChildName == "ColorRGBA")
  721. readColorRGBA(currentChildNode);
  722. else if (currentChildName == "Coordinate")
  723. readCoordinate(currentChildNode);
  724. else if (currentChildName == "Normal")
  725. readNormal(currentChildNode);
  726. else if (currentChildName == "TextureCoordinate")
  727. readTextureCoordinate(currentChildNode);
  728. // check for X3DMetadataObject
  729. else if (!checkForMetadataNode(currentChildNode))
  730. skipUnsupportedNode("TriangleSet", currentChildNode);
  731. }
  732. ParseHelper_Node_Exit();
  733. } // if(!isNodeEmpty(node))
  734. else {
  735. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  736. }
  737. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  738. } // if(!use.empty()) else
  739. }
  740. // <TriangleStripSet
  741. // DEF="" ID
  742. // USE="" IDREF
  743. // ccw="true" SFBool [initializeOnly]
  744. // colorPerVertex="true" SFBool [initializeOnly]
  745. // normalPerVertex="true" SFBool [initializeOnly]
  746. // solid="true" SFBool [initializeOnly]
  747. // stripCount="" MFInt32 [inputOutput]
  748. // >
  749. // <!-- ComposedGeometryContentModel -->
  750. // ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
  751. // Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
  752. // Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
  753. // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
  754. // </TriangleStripSet>
  755. void X3DImporter::readTriangleStripSet(XmlNode &node) {
  756. std::string use, def;
  757. bool ccw = true;
  758. bool colorPerVertex = true;
  759. std::vector<int32_t> stripCount;
  760. bool normalPerVertex = true;
  761. bool solid = true;
  762. X3DNodeElementBase *ne(nullptr);
  763. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  764. XmlParser::getBoolAttribute(node, "ccw", ccw);
  765. XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
  766. X3DXmlHelper::getInt32ArrayAttribute(node, "stripCount", stripCount);
  767. XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
  768. XmlParser::getBoolAttribute(node, "solid", solid);
  769. // if "USE" defined then find already defined element.
  770. if (!use.empty()) {
  771. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleStripSet, ne);
  772. } else {
  773. // check data
  774. if (stripCount.size() == 0) throw DeadlyImportError("TriangleStripSet must contain not empty \"stripCount\" attribute.");
  775. // create and if needed - define new geometry object.
  776. ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleStripSet, mNodeElementCur);
  777. if (!def.empty()) ne->ID = def;
  778. X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne);
  779. ne_alias.CCW = ccw;
  780. ne_alias.ColorPerVertex = colorPerVertex;
  781. ne_alias.VertexCount = stripCount;
  782. ne_alias.NormalPerVertex = normalPerVertex;
  783. ne_alias.Solid = solid;
  784. // create CoordIdx
  785. size_t coord_num0, coord_num1, coord_num2; // indices of current triangle
  786. bool odd_tri; // sequence of current triangle
  787. size_t coord_num_sb; // index of first point of strip
  788. ne_alias.CoordIndex.clear();
  789. coord_num_sb = 0;
  790. for (std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) {
  791. if (*vc_it < 3) throw DeadlyImportError("TriangleStripSet. stripCount shall be greater than or equal to three.");
  792. // set initial values for first triangle
  793. coord_num0 = coord_num_sb;
  794. coord_num1 = coord_num_sb + 1;
  795. coord_num2 = coord_num_sb + 2;
  796. odd_tri = true;
  797. for (int32_t vc = 2; vc < *vc_it; vc++) {
  798. if (ccw) {
  799. // 0 2
  800. // 1
  801. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num0));
  802. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num1));
  803. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num2));
  804. } else {
  805. // 0 1
  806. // 2
  807. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num0));
  808. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num2));
  809. ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num1));
  810. } // if(ccw) else
  811. ne_alias.CoordIndex.push_back(-1); // add face delimiter.
  812. // prepare values for next triangle
  813. if (odd_tri) {
  814. coord_num0 = coord_num2;
  815. coord_num2++;
  816. } else {
  817. coord_num1 = coord_num2;
  818. coord_num2 = coord_num1 + 1;
  819. }
  820. odd_tri = !odd_tri;
  821. coord_num_sb = coord_num2; // that index will be start of next strip
  822. } // for(int32_t vc = 2; vc < *vc_it; vc++)
  823. } // for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
  824. // check for child nodes
  825. if (!isNodeEmpty(node)) {
  826. ParseHelper_Node_Enter(ne);
  827. for (auto currentChildNode : node.children()) {
  828. const std::string &currentChildName = currentChildNode.name();
  829. // check for X3DComposedGeometryNodes
  830. if (currentChildName == "Color")
  831. readColor(currentChildNode);
  832. else if (currentChildName == "ColorRGBA")
  833. readColorRGBA(currentChildNode);
  834. else if (currentChildName == "Coordinate")
  835. readCoordinate(currentChildNode);
  836. else if (currentChildName == "Normal")
  837. readNormal(currentChildNode);
  838. else if (currentChildName == "TextureCoordinate")
  839. readTextureCoordinate(currentChildNode);
  840. // check for X3DMetadataObject
  841. else if (!checkForMetadataNode(currentChildNode))
  842. skipUnsupportedNode("TriangleStripSet", currentChildNode);
  843. }
  844. ParseHelper_Node_Exit();
  845. } // if(!isNodeEmpty(node))
  846. else {
  847. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  848. }
  849. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  850. } // if(!use.empty()) else
  851. }
  852. // <Normal
  853. // DEF="" ID
  854. // USE="" IDREF
  855. // vector="" MFVec3f [inputOutput]
  856. // />
  857. void X3DImporter::readNormal(XmlNode &node) {
  858. std::string use, def;
  859. std::list<aiVector3D> vector;
  860. X3DNodeElementBase *ne=nullptr;
  861. MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
  862. X3DXmlHelper::getVector3DListAttribute(node, "vector", vector);
  863. // if "USE" defined then find already defined element.
  864. if (!use.empty()) {
  865. ne = MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Normal, ne);
  866. } else {
  867. // create and if needed - define new geometry object.
  868. ne = new X3DNodeElementNormal(mNodeElementCur);
  869. if (!def.empty()) ne->ID = def;
  870. ((X3DNodeElementNormal *)ne)->Value = vector;
  871. // check for X3DMetadataObject childs.
  872. if (!isNodeEmpty(node))
  873. childrenReadMetadata(node, ne, "Normal");
  874. else
  875. mNodeElementCur->Children.push_back(ne); // add made object as child to current element
  876. NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
  877. } // if(!use.empty()) else
  878. }
  879. } // namespace Assimp
  880. #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER