X3DImporter_Geometry3D.cpp 37 KB

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