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