X3DImporter_Geometry3D.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. /*
  2. Open Asset Import Library (assimp)
  3. ----------------------------------------------------------------------
  4. Copyright (c) 2006-2020, 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_Geometry3D.cpp
  34. /// \brief Parsing data from nodes of "Geometry3D" 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. // Header files, Assimp.
  41. #include <assimp/StandardShapes.h>
  42. namespace Assimp
  43. {
  44. // <Box
  45. // DEF="" ID
  46. // USE="" IDREF
  47. // size="2 2 2" SFVec3f [initializeOnly]
  48. // solid="true" SFBool [initializeOnly]
  49. // />
  50. // The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes.
  51. // By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes
  52. // respectively and each component value shall be greater than zero.
  53. void X3DImporter::ParseNode_Geometry3D_Box()
  54. {
  55. std::string def, use;
  56. bool solid = true;
  57. aiVector3D size(2, 2, 2);
  58. CX3DImporter_NodeElement* ne( nullptr );
  59. MACRO_ATTRREAD_LOOPBEG;
  60. MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
  61. MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec3f);
  62. MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
  63. MACRO_ATTRREAD_LOOPEND;
  64. // if "USE" defined then find already defined element.
  65. if(!use.empty())
  66. {
  67. MACRO_USE_CHECKANDAPPLY(def, use, ENET_Box, ne);
  68. }
  69. else
  70. {
  71. // create and if needed - define new geometry object.
  72. ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Box, NodeElement_Cur);
  73. if(!def.empty()) ne->ID = def;
  74. GeometryHelper_MakeQL_RectParallelepiped(size, ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices);// get quad list
  75. ((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
  76. ((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 4;
  77. // check for X3DMetadataObject childs.
  78. if(!mReader->isEmptyElement())
  79. ParseNode_Metadata(ne, "Box");
  80. else
  81. NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
  82. NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
  83. }// if(!use.empty()) else
  84. }
  85. // <Cone
  86. // DEF="" ID
  87. // USE="" IDREF
  88. // bottom="true" SFBool [initializeOnly]
  89. // bottomRadius="1" SFloat [initializeOnly]
  90. // height="2" SFloat [initializeOnly]
  91. // side="true" SFBool [initializeOnly]
  92. // solid="true" SFBool [initializeOnly]
  93. // />
  94. void X3DImporter::ParseNode_Geometry3D_Cone()
  95. {
  96. std::string use, def;
  97. bool bottom = true;
  98. float bottomRadius = 1;
  99. float height = 2;
  100. bool side = true;
  101. bool solid = true;
  102. CX3DImporter_NodeElement* ne( nullptr );
  103. MACRO_ATTRREAD_LOOPBEG;
  104. MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
  105. MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
  106. MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool);
  107. MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool);
  108. MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat);
  109. MACRO_ATTRREAD_CHECK_RET("bottomRadius", bottomRadius, XML_ReadNode_GetAttrVal_AsFloat);
  110. MACRO_ATTRREAD_LOOPEND;
  111. // if "USE" defined then find already defined element.
  112. if(!use.empty())
  113. {
  114. MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cone, ne);
  115. }
  116. else
  117. {
  118. const unsigned int tess = 30;///TODO: IME tessellation factor through ai_property
  119. std::vector<aiVector3D> tvec;// temp array for vertices.
  120. // create and if needed - define new geometry object.
  121. ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cone, NodeElement_Cur);
  122. if(!def.empty()) ne->ID = def;
  123. // make cone or parts according to flags.
  124. if(side)
  125. {
  126. StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom);
  127. }
  128. else if(bottom)
  129. {
  130. StandardShapes::MakeCircle(bottomRadius, tess, tvec);
  131. height = -(height / 2);
  132. for(std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); ++it) it->y = height;// y - because circle made in oXZ.
  133. }
  134. // copy data from temp array
  135. for(std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); ++it) ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices.push_back(*it);
  136. ((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
  137. ((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 3;
  138. // check for X3DMetadataObject childs.
  139. if(!mReader->isEmptyElement())
  140. ParseNode_Metadata(ne, "Cone");
  141. else
  142. NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
  143. NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
  144. }// if(!use.empty()) else
  145. }
  146. // <Cylinder
  147. // DEF="" ID
  148. // USE="" IDREF
  149. // bottom="true" SFBool [initializeOnly]
  150. // height="2" SFloat [initializeOnly]
  151. // radius="1" SFloat [initializeOnly]
  152. // side="true" SFBool [initializeOnly]
  153. // solid="true" SFBool [initializeOnly]
  154. // top="true" SFBool [initializeOnly]
  155. // />
  156. void X3DImporter::ParseNode_Geometry3D_Cylinder()
  157. {
  158. std::string use, def;
  159. bool bottom = true;
  160. float height = 2;
  161. float radius = 1;
  162. bool side = true;
  163. bool solid = true;
  164. bool top = true;
  165. CX3DImporter_NodeElement* ne( nullptr );
  166. MACRO_ATTRREAD_LOOPBEG;
  167. MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
  168. MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
  169. MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
  170. MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool);
  171. MACRO_ATTRREAD_CHECK_RET("top", top, XML_ReadNode_GetAttrVal_AsBool);
  172. MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool);
  173. MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat);
  174. MACRO_ATTRREAD_LOOPEND;
  175. // if "USE" defined then find already defined element.
  176. if(!use.empty())
  177. {
  178. MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cylinder, ne);
  179. }
  180. else
  181. {
  182. const unsigned int tess = 30;///TODO: IME tessellation factor through ai_property
  183. std::vector<aiVector3D> tside;// temp array for vertices of side.
  184. std::vector<aiVector3D> tcir;// temp array for vertices of circle.
  185. // create and if needed - define new geometry object.
  186. ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cylinder, NodeElement_Cur);
  187. if(!def.empty()) ne->ID = def;
  188. // make cilynder or parts according to flags.
  189. if(side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true);
  190. height /= 2;// height defined for whole cylinder, when creating top and bottom circle we are using just half of height.
  191. if(top || bottom) StandardShapes::MakeCircle(radius, tess, tcir);
  192. // copy data from temp arrays
  193. std::list<aiVector3D>& vlist = ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices;// just short alias.
  194. for(std::vector<aiVector3D>::iterator it = tside.begin(); it != tside.end(); ++it) vlist.push_back(*it);
  195. if(top)
  196. {
  197. for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); ++it)
  198. {
  199. (*it).y = height;// y - because circle made in oXZ.
  200. vlist.push_back(*it);
  201. }
  202. }// if(top)
  203. if(bottom)
  204. {
  205. for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); ++it)
  206. {
  207. (*it).y = -height;// y - because circle made in oXZ.
  208. vlist.push_back(*it);
  209. }
  210. }// if(top)
  211. ((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
  212. ((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 3;
  213. // check for X3DMetadataObject childs.
  214. if(!mReader->isEmptyElement())
  215. ParseNode_Metadata(ne, "Cylinder");
  216. else
  217. NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
  218. NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
  219. }// if(!use.empty()) else
  220. }
  221. // <ElevationGrid
  222. // DEF="" ID
  223. // USE="" IDREF
  224. // ccw="true" SFBool [initializeOnly]
  225. // colorPerVertex="true" SFBool [initializeOnly]
  226. // creaseAngle="0" SFloat [initializeOnly]
  227. // height="" MFloat [initializeOnly]
  228. // normalPerVertex="true" SFBool [initializeOnly]
  229. // solid="true" SFBool [initializeOnly]
  230. // xDimension="0" SFInt32 [initializeOnly]
  231. // xSpacing="1.0" SFloat [initializeOnly]
  232. // zDimension="0" SFInt32 [initializeOnly]
  233. // zSpacing="1.0" SFloat [initializeOnly]
  234. // >
  235. // <!-- ColorNormalTexCoordContentModel -->
  236. // ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single
  237. // node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
  238. // </ElevationGrid>
  239. // The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described
  240. // by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate
  241. // the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero.
  242. // If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals.
  243. void X3DImporter::ParseNode_Geometry3D_ElevationGrid()
  244. {
  245. std::string use, def;
  246. bool ccw = true;
  247. bool colorPerVertex = true;
  248. float creaseAngle = 0;
  249. std::vector<float> height;
  250. bool normalPerVertex = true;
  251. bool solid = true;
  252. int32_t xDimension = 0;
  253. float xSpacing = 1;
  254. int32_t zDimension = 0;
  255. float zSpacing = 1;
  256. CX3DImporter_NodeElement* ne( nullptr );
  257. MACRO_ATTRREAD_LOOPBEG;
  258. MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
  259. MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
  260. MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
  261. MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
  262. MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
  263. MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
  264. MACRO_ATTRREAD_CHECK_REF("height", height, XML_ReadNode_GetAttrVal_AsArrF);
  265. MACRO_ATTRREAD_CHECK_RET("xDimension", xDimension, XML_ReadNode_GetAttrVal_AsI32);
  266. MACRO_ATTRREAD_CHECK_RET("xSpacing", xSpacing, XML_ReadNode_GetAttrVal_AsFloat);
  267. MACRO_ATTRREAD_CHECK_RET("zDimension", zDimension, XML_ReadNode_GetAttrVal_AsI32);
  268. MACRO_ATTRREAD_CHECK_RET("zSpacing", zSpacing, XML_ReadNode_GetAttrVal_AsFloat);
  269. MACRO_ATTRREAD_LOOPEND;
  270. // if "USE" defined then find already defined element.
  271. if(!use.empty())
  272. {
  273. MACRO_USE_CHECKANDAPPLY(def, use, ENET_ElevationGrid, ne);
  274. }
  275. else
  276. {
  277. if((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in <ElevationGrid> must be grater than zero.");
  278. if((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in <ElevationGrid> must be grater than zero.");
  279. if((size_t)(xDimension * zDimension) != height.size()) Throw_IncorrectAttrValue("Heights count must be equal to \"xDimension * zDimension\"");
  280. // create and if needed - define new geometry object.
  281. ne = new CX3DImporter_NodeElement_ElevationGrid(CX3DImporter_NodeElement::ENET_ElevationGrid, NodeElement_Cur);
  282. if(!def.empty()) ne->ID = def;
  283. CX3DImporter_NodeElement_ElevationGrid& grid_alias = *((CX3DImporter_NodeElement_ElevationGrid*)ne);// create alias for conveience
  284. {// create grid vertices list
  285. std::vector<float>::const_iterator he_it = height.begin();
  286. for(int32_t zi = 0; zi < zDimension; zi++)// rows
  287. {
  288. for(int32_t xi = 0; xi < xDimension; xi++)// columns
  289. {
  290. aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi);
  291. grid_alias.Vertices.push_back(tvec);
  292. ++he_it;
  293. }
  294. }
  295. }// END: create grid vertices list
  296. //
  297. // create faces list. In "coordIdx" format
  298. //
  299. // check if we have quads
  300. if((xDimension < 2) || (zDimension < 2))// only one element in dimension is set, create line set.
  301. {
  302. ((CX3DImporter_NodeElement_ElevationGrid*)ne)->NumIndices = 2;// will be holded as line set.
  303. for(size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++)
  304. {
  305. grid_alias.CoordIdx.push_back(static_cast<int32_t>(i));
  306. grid_alias.CoordIdx.push_back(static_cast<int32_t>(i + 1));
  307. grid_alias.CoordIdx.push_back(-1);
  308. }
  309. }
  310. else// two or more elements in every dimension is set. create quad set.
  311. {
  312. ((CX3DImporter_NodeElement_ElevationGrid*)ne)->NumIndices = 4;
  313. for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)// rows
  314. {
  315. for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)// columns
  316. {
  317. // points direction in face.
  318. if(ccw)
  319. {
  320. // CCW:
  321. // 3 2
  322. // 0 1
  323. grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi);
  324. grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1));
  325. grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1));
  326. grid_alias.CoordIdx.push_back(fzi * xDimension + fxi);
  327. }
  328. else
  329. {
  330. // CW:
  331. // 0 1
  332. // 3 2
  333. grid_alias.CoordIdx.push_back(fzi * xDimension + fxi);
  334. grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1));
  335. grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1));
  336. grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi);
  337. }// if(ccw) else
  338. grid_alias.CoordIdx.push_back(-1);
  339. }// for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)
  340. }// for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)
  341. }// if((xDimension < 2) || (zDimension < 2)) else
  342. grid_alias.ColorPerVertex = colorPerVertex;
  343. grid_alias.NormalPerVertex = normalPerVertex;
  344. grid_alias.CreaseAngle = creaseAngle;
  345. grid_alias.Solid = solid;
  346. // check for child nodes
  347. if(!mReader->isEmptyElement())
  348. {
  349. ParseHelper_Node_Enter(ne);
  350. MACRO_NODECHECK_LOOPBEGIN("ElevationGrid");
  351. // check for X3DComposedGeometryNodes
  352. if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
  353. if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
  354. if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
  355. if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
  356. // check for X3DMetadataObject
  357. if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("ElevationGrid");
  358. MACRO_NODECHECK_LOOPEND("ElevationGrid");
  359. ParseHelper_Node_Exit();
  360. }// if(!mReader->isEmptyElement())
  361. else
  362. {
  363. NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
  364. }// if(!mReader->isEmptyElement()) else
  365. NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
  366. }// if(!use.empty()) else
  367. }
  368. template<typename TVector>
  369. static void GeometryHelper_Extrusion_CurveIsClosed(std::vector<TVector>& pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool& pCurveIsClosed)
  370. {
  371. size_t cur_sz = pCurve.size();
  372. pCurveIsClosed = false;
  373. // for curve with less than four points checking is have no sense,
  374. if(cur_sz < 4) return;
  375. for(size_t s = 3, s_e = cur_sz; s < s_e; s++)
  376. {
  377. // search for first point of duplicated part.
  378. if(pCurve[0] == pCurve[s])
  379. {
  380. bool found = true;
  381. // check if tail(indexed by b2) is duplicate of head(indexed by b1).
  382. for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++)
  383. {
  384. if(pCurve[b1] != pCurve[b2])
  385. {// points not match: clear flag and break loop.
  386. found = false;
  387. break;
  388. }
  389. }// for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++)
  390. // if duplicate tail is found then drop or not it depending on flags.
  391. if(found)
  392. {
  393. pCurveIsClosed = true;
  394. if(pDropTail)
  395. {
  396. if(!pRemoveLastPoint) s++;// prepare value for iterator's arithmetics.
  397. pCurve.erase(pCurve.begin() + s, pCurve.end());// remove tail
  398. }
  399. break;
  400. }// if(found)
  401. }// if(pCurve[0] == pCurve[s])
  402. }// for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++)
  403. }
  404. static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector<aiVector3D>& pSpine, const bool pSpine_Closed)
  405. {
  406. const size_t spine_idx_last = pSpine.size() - 1;
  407. aiVector3D tvec;
  408. if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))// at first special cases
  409. {
  410. if(pSpine_Closed)
  411. {// If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis.
  412. // As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0])
  413. // in tail are removed.
  414. // So, last point in pSpine is a spine[n - 2]
  415. tvec = pSpine[1] - pSpine[spine_idx_last];
  416. }
  417. else if(pSpine_PointIdx == 0)
  418. {// The Y-axis used for the first point is the vector from spine[0] to spine[1]
  419. tvec = pSpine[1] - pSpine[0];
  420. }
  421. else
  422. {// The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is
  423. // the spine[0].
  424. tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1];
  425. }
  426. }// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))
  427. else
  428. {// For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]).
  429. tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1];
  430. }// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else
  431. return tvec.Normalize();
  432. }
  433. static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector<aiVector3D>& pSpine, const bool pSpine_Closed,
  434. const aiVector3D pVecZ_Prev)
  435. {
  436. const aiVector3D zero_vec(0);
  437. const size_t spine_idx_last = pSpine.size() - 1;
  438. aiVector3D tvec;
  439. // at first special cases
  440. if(pSpine.size() < 3)// spine have not enough points for vector calculations.
  441. {
  442. tvec.Set(0, 0, 1);
  443. }
  444. else if(pSpine_PointIdx == 0)// special case: first point
  445. {
  446. if(pSpine_Closed)// for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate.
  447. {
  448. tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]);
  449. }
  450. else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z.
  451. {
  452. bool found = false;
  453. // As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear)
  454. // then the Z-axis for the first spine point with a defined Z-axis is used."
  455. // Walk through spine and find Z.
  456. for(size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++)
  457. {
  458. // (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1])
  459. tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]);
  460. found = !tvec.Equal(zero_vec);
  461. }
  462. // if entire spine are collinear then use OZ axis.
  463. if(!found) tvec.Set(0, 0, 1);
  464. }// if(pSpine_Closed) else
  465. }// else if(pSpine_PointIdx == 0)
  466. else if(pSpine_PointIdx == spine_idx_last)// special case: last point
  467. {
  468. if(pSpine_Closed)
  469. {// do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2].
  470. tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]);
  471. // if taken spine vectors are collinear then use previous vector Z.
  472. if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev;
  473. }
  474. else
  475. {// vector Z for last point of not closed curve is previous vector Z.
  476. tvec = pVecZ_Prev;
  477. }
  478. }
  479. else// regular point
  480. {
  481. tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]);
  482. // if taken spine vectors are collinear then use previous vector Z.
  483. if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev;
  484. }
  485. // After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis
  486. // is flipped (multiplied by -1).
  487. if((tvec * pVecZ_Prev) < 0) tvec = -tvec;
  488. return tvec.Normalize();
  489. }
  490. // <Extrusion
  491. // DEF="" ID
  492. // USE="" IDREF
  493. // beginCap="true" SFBool [initializeOnly]
  494. // ccw="true" SFBool [initializeOnly]
  495. // convex="true" SFBool [initializeOnly]
  496. // creaseAngle="0.0" SFloat [initializeOnly]
  497. // crossSection="1 1 1 -1 -1 -1 -1 1 1 1" MFVec2f [initializeOnly]
  498. // endCap="true" SFBool [initializeOnly]
  499. // orientation="0 0 1 0" MFRotation [initializeOnly]
  500. // scale="1 1" MFVec2f [initializeOnly]
  501. // solid="true" SFBool [initializeOnly]
  502. // spine="0 0 0 0 1 0" MFVec3f [initializeOnly]
  503. // />
  504. void X3DImporter::ParseNode_Geometry3D_Extrusion()
  505. {
  506. std::string use, def;
  507. bool beginCap = true;
  508. bool ccw = true;
  509. bool convex = true;
  510. float creaseAngle = 0;
  511. std::vector<aiVector2D> crossSection;
  512. bool endCap = true;
  513. std::vector<float> orientation;
  514. std::vector<aiVector2D> scale;
  515. bool solid = true;
  516. std::vector<aiVector3D> spine;
  517. CX3DImporter_NodeElement* ne( nullptr );
  518. MACRO_ATTRREAD_LOOPBEG;
  519. MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
  520. MACRO_ATTRREAD_CHECK_RET("beginCap", beginCap, XML_ReadNode_GetAttrVal_AsBool);
  521. MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
  522. MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool);
  523. MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
  524. MACRO_ATTRREAD_CHECK_REF("crossSection", crossSection, XML_ReadNode_GetAttrVal_AsArrVec2f);
  525. MACRO_ATTRREAD_CHECK_RET("endCap", endCap, XML_ReadNode_GetAttrVal_AsBool);
  526. MACRO_ATTRREAD_CHECK_REF("orientation", orientation, XML_ReadNode_GetAttrVal_AsArrF);
  527. MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsArrVec2f);
  528. MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
  529. MACRO_ATTRREAD_CHECK_REF("spine", spine, XML_ReadNode_GetAttrVal_AsArrVec3f);
  530. MACRO_ATTRREAD_LOOPEND;
  531. // if "USE" defined then find already defined element.
  532. if(!use.empty())
  533. {
  534. MACRO_USE_CHECKANDAPPLY(def, use, ENET_Extrusion, ne);
  535. }
  536. else
  537. {
  538. //
  539. // check if default values must be assigned
  540. //
  541. if(spine.size() == 0)
  542. {
  543. spine.resize(2);
  544. spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0);
  545. }
  546. else if(spine.size() == 1)
  547. {
  548. throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points.");
  549. }
  550. if(crossSection.size() == 0)
  551. {
  552. crossSection.resize(5);
  553. crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1);
  554. }
  555. {// orientation
  556. size_t ori_size = orientation.size() / 4;
  557. if(ori_size < spine.size())
  558. {
  559. float add_ori[4];// values that will be added
  560. if(ori_size == 1)// if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points.
  561. {
  562. add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3];
  563. }
  564. else// else - use default values
  565. {
  566. add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0;
  567. }
  568. orientation.reserve(spine.size() * 4);
  569. for(size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++)
  570. orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]);
  571. }
  572. if(orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in <Extrusion> must has multiple four quantity of numbers.");
  573. }// END: orientation
  574. {// scale
  575. if(scale.size() < spine.size())
  576. {
  577. aiVector2D add_sc;
  578. if(scale.size() == 1)// if "scale" has one element then use it value for all spine points.
  579. add_sc = scale[0];
  580. else// else - use default values
  581. add_sc.Set(1, 1);
  582. scale.reserve(spine.size());
  583. for(size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) scale.push_back(add_sc);
  584. }
  585. }// END: scale
  586. //
  587. // create and if needed - define new geometry object.
  588. //
  589. ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_Extrusion, NodeElement_Cur);
  590. if(!def.empty()) ne->ID = def;
  591. CX3DImporter_NodeElement_IndexedSet& ext_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);// create alias for conveience
  592. // assign part of input data
  593. ext_alias.CCW = ccw;
  594. ext_alias.Convex = convex;
  595. ext_alias.CreaseAngle = creaseAngle;
  596. ext_alias.Solid = solid;
  597. //
  598. // How we done it at all?
  599. // 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector
  600. // are applied vor every basis.
  601. // 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position
  602. // using relative spine point.
  603. // 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if
  604. // needed. While createing CootdIdx is taking in account CCW flag.
  605. // 4. The last step: create Vertices list.
  606. //
  607. bool spine_closed;// flag: true if spine curve is closed.
  608. bool cross_closed;// flag: true if cross curve is closed.
  609. std::vector<aiMatrix3x3> basis_arr;// array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z.
  610. std::vector<std::vector<aiVector3D> > pointset_arr;// array of point sets: cross curves.
  611. // detect closed curves
  612. GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed);// true - drop tail, true - remove duplicate end.
  613. GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed);// true - drop tail, true - remove duplicate end.
  614. // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface.
  615. if(spine_closed)
  616. {
  617. beginCap |= endCap;
  618. endCap = false;
  619. }
  620. {// 1. Calculate array of basises.
  621. aiMatrix4x4 rotmat;
  622. aiVector3D vecX(0), vecY(0), vecZ(0);
  623. basis_arr.resize(spine.size());
  624. for(size_t i = 0, i_e = spine.size(); i < i_e; i++)
  625. {
  626. aiVector3D tvec;
  627. // get axises of basis.
  628. vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed);
  629. vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ);
  630. vecX = (vecY ^ vecZ).Normalize();
  631. // get rotation matrix and apply "orientation" to basis
  632. aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat);
  633. tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z;
  634. tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z;
  635. tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z;
  636. }// for(size_t i = 0, i_e = spine.size(); i < i_e; i++)
  637. }// END: 1. Calculate array of basises
  638. {// 2. Create array of point sets.
  639. aiMatrix4x4 scmat;
  640. std::vector<aiVector3D> tcross(crossSection.size());
  641. pointset_arr.resize(spine.size());
  642. for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++)
  643. {
  644. aiVector3D tc23vec;
  645. tc23vec.Set(scale[spi].x, 0, scale[spi].y);
  646. aiMatrix4x4::Scaling(tc23vec, scmat);
  647. for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++)
  648. {
  649. aiVector3D tvecX, tvecY, tvecZ;
  650. tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y);
  651. // apply scaling to point
  652. tcross[cri] = scmat * tc23vec;
  653. //
  654. // transfer point to new basis
  655. // calculate coordinate in new basis
  656. tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x;
  657. tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y;
  658. tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z;
  659. // apply new coordinates and translate it to spine point.
  660. tcross[cri] = tvecX + tvecY + tvecZ + spine[spi];
  661. }// for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++)
  662. pointset_arr[spi] = tcross;// store transferred point set
  663. }// for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++)
  664. }// END: 2. Create array of point sets.
  665. {// 3. Create CoordIdx.
  666. // add caps if needed
  667. if(beginCap)
  668. {
  669. // add cap as polygon. vertices of cap are places at begin, so just add numbers from zero.
  670. for(size_t i = 0, i_e = crossSection.size(); i < i_e; i++) ext_alias.CoordIndex.push_back(static_cast<int32_t>(i));
  671. // add delimiter
  672. ext_alias.CoordIndex.push_back(-1);
  673. }// if(beginCap)
  674. if(endCap)
  675. {
  676. // add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset.
  677. size_t beg = (pointset_arr.size() - 1) * crossSection.size();
  678. for(size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) ext_alias.CoordIndex.push_back(static_cast<int32_t>(i));
  679. // add delimiter
  680. ext_alias.CoordIndex.push_back(-1);
  681. }// if(beginCap)
  682. // add quads
  683. for(size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++)
  684. {
  685. const size_t cr_sz = crossSection.size();
  686. const size_t cr_last = crossSection.size() - 1;
  687. size_t right_col;// hold index basis for points of quad placed in right column;
  688. if(spi != spi_e)
  689. right_col = spi + 1;
  690. else if(spine_closed)// if spine curve is closed then one more quad is needed: between first and last points of curve.
  691. right_col = 0;
  692. else
  693. break;// if spine curve is not closed then break the loop, because spi is out of range for that type of spine.
  694. for(size_t cri = 0; cri < cr_sz; cri++)
  695. {
  696. if(cri != cr_last)
  697. {
  698. MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex,
  699. static_cast<int32_t>(spi * cr_sz + cri),
  700. static_cast<int32_t>(right_col * cr_sz + cri),
  701. static_cast<int32_t>(right_col * cr_sz + cri + 1),
  702. static_cast<int32_t>(spi * cr_sz + cri + 1));
  703. // add delimiter
  704. ext_alias.CoordIndex.push_back(-1);
  705. }
  706. else if(cross_closed)// if cross curve is closed then one more quad is needed: between first and last points of curve.
  707. {
  708. MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex,
  709. static_cast<int32_t>(spi * cr_sz + cri),
  710. static_cast<int32_t>(right_col * cr_sz + cri),
  711. static_cast<int32_t>(right_col * cr_sz + 0),
  712. static_cast<int32_t>(spi * cr_sz + 0));
  713. // add delimiter
  714. ext_alias.CoordIndex.push_back(-1);
  715. }
  716. }// for(size_t cri = 0; cri < cr_sz; cri++)
  717. }// for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++)
  718. }// END: 3. Create CoordIdx.
  719. {// 4. Create vertices list.
  720. // just copy all vertices
  721. for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++)
  722. {
  723. for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++)
  724. {
  725. ext_alias.Vertices.push_back(pointset_arr[spi][cri]);
  726. }
  727. }
  728. }// END: 4. Create vertices list.
  729. //PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex);
  730. //PrintVectorSet("Ext. Vertices", ext_alias.Vertices);
  731. // check for child nodes
  732. if(!mReader->isEmptyElement())
  733. ParseNode_Metadata(ne, "Extrusion");
  734. else
  735. NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
  736. NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
  737. }// if(!use.empty()) else
  738. }
  739. // <IndexedFaceSet
  740. // DEF="" ID
  741. // USE="" IDREF
  742. // ccw="true" SFBool [initializeOnly]
  743. // colorIndex="" MFInt32 [initializeOnly]
  744. // colorPerVertex="true" SFBool [initializeOnly]
  745. // convex="true" SFBool [initializeOnly]
  746. // coordIndex="" MFInt32 [initializeOnly]
  747. // creaseAngle="0" SFFloat [initializeOnly]
  748. // normalIndex="" MFInt32 [initializeOnly]
  749. // normalPerVertex="true" SFBool [initializeOnly]
  750. // solid="true" SFBool [initializeOnly]
  751. // texCoordIndex="" MFInt32 [initializeOnly]
  752. // >
  753. // <!-- ComposedGeometryContentModel -->
  754. // ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
  755. // Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
  756. // Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
  757. // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
  758. // </IndexedFaceSet>
  759. void X3DImporter::ParseNode_Geometry3D_IndexedFaceSet()
  760. {
  761. std::string use, def;
  762. bool ccw = true;
  763. std::vector<int32_t> colorIndex;
  764. bool colorPerVertex = true;
  765. bool convex = true;
  766. std::vector<int32_t> coordIndex;
  767. float creaseAngle = 0;
  768. std::vector<int32_t> normalIndex;
  769. bool normalPerVertex = true;
  770. bool solid = true;
  771. std::vector<int32_t> texCoordIndex;
  772. CX3DImporter_NodeElement* ne( nullptr );
  773. MACRO_ATTRREAD_LOOPBEG;
  774. MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
  775. MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
  776. MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsArrI32);
  777. MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
  778. MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool);
  779. MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsArrI32);
  780. MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
  781. MACRO_ATTRREAD_CHECK_REF("normalIndex", normalIndex, XML_ReadNode_GetAttrVal_AsArrI32);
  782. MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
  783. MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
  784. MACRO_ATTRREAD_CHECK_REF("texCoordIndex", texCoordIndex, XML_ReadNode_GetAttrVal_AsArrI32);
  785. MACRO_ATTRREAD_LOOPEND;
  786. // if "USE" defined then find already defined element.
  787. if(!use.empty())
  788. {
  789. MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedFaceSet, ne);
  790. }
  791. else
  792. {
  793. // check data
  794. if(coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute.");
  795. // create and if needed - define new geometry object.
  796. ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedFaceSet, NodeElement_Cur);
  797. if(!def.empty()) ne->ID = def;
  798. CX3DImporter_NodeElement_IndexedSet& ne_alias = *((CX3DImporter_NodeElement_IndexedSet*)ne);
  799. ne_alias.CCW = ccw;
  800. ne_alias.ColorIndex = colorIndex;
  801. ne_alias.ColorPerVertex = colorPerVertex;
  802. ne_alias.Convex = convex;
  803. ne_alias.CoordIndex = coordIndex;
  804. ne_alias.CreaseAngle = creaseAngle;
  805. ne_alias.NormalIndex = normalIndex;
  806. ne_alias.NormalPerVertex = normalPerVertex;
  807. ne_alias.Solid = solid;
  808. ne_alias.TexCoordIndex = texCoordIndex;
  809. // check for child nodes
  810. if(!mReader->isEmptyElement())
  811. {
  812. ParseHelper_Node_Enter(ne);
  813. MACRO_NODECHECK_LOOPBEGIN("IndexedFaceSet");
  814. // check for X3DComposedGeometryNodes
  815. if(XML_CheckNode_NameEqual("Color")) { ParseNode_Rendering_Color(); continue; }
  816. if(XML_CheckNode_NameEqual("ColorRGBA")) { ParseNode_Rendering_ColorRGBA(); continue; }
  817. if(XML_CheckNode_NameEqual("Coordinate")) { ParseNode_Rendering_Coordinate(); continue; }
  818. if(XML_CheckNode_NameEqual("Normal")) { ParseNode_Rendering_Normal(); continue; }
  819. if(XML_CheckNode_NameEqual("TextureCoordinate")) { ParseNode_Texturing_TextureCoordinate(); continue; }
  820. // check for X3DMetadataObject
  821. if(!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedFaceSet");
  822. MACRO_NODECHECK_LOOPEND("IndexedFaceSet");
  823. ParseHelper_Node_Exit();
  824. }// if(!mReader->isEmptyElement())
  825. else
  826. {
  827. NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
  828. }
  829. NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
  830. }// if(!use.empty()) else
  831. }
  832. // <Sphere
  833. // DEF="" ID
  834. // USE="" IDREF
  835. // radius="1" SFloat [initializeOnly]
  836. // solid="true" SFBool [initializeOnly]
  837. // />
  838. void X3DImporter::ParseNode_Geometry3D_Sphere()
  839. {
  840. std::string use, def;
  841. ai_real radius = 1;
  842. bool solid = true;
  843. CX3DImporter_NodeElement* ne( nullptr );
  844. MACRO_ATTRREAD_LOOPBEG;
  845. MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
  846. MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
  847. MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
  848. MACRO_ATTRREAD_LOOPEND;
  849. // if "USE" defined then find already defined element.
  850. if(!use.empty())
  851. {
  852. MACRO_USE_CHECKANDAPPLY(def, use, ENET_Sphere, ne);
  853. }
  854. else
  855. {
  856. const unsigned int tess = 3;///TODO: IME tessellation factor through ai_property
  857. std::vector<aiVector3D> tlist;
  858. // create and if needed - define new geometry object.
  859. ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Sphere, NodeElement_Cur);
  860. if(!def.empty()) ne->ID = def;
  861. StandardShapes::MakeSphere(tess, tlist);
  862. // copy data from temp array and apply scale
  863. for(std::vector<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); ++it)
  864. {
  865. ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices.push_back(*it * radius);
  866. }
  867. ((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
  868. ((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 3;
  869. // check for X3DMetadataObject childs.
  870. if(!mReader->isEmptyElement())
  871. ParseNode_Metadata(ne, "Sphere");
  872. else
  873. NodeElement_Cur->Child.push_back(ne);// add made object as child to current element
  874. NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
  875. }// if(!use.empty()) else
  876. }
  877. }// namespace Assimp
  878. #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER