1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4
5 Copyright (c) 2006-2019, assimp team
6
7
8 All rights reserved.
9
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the
12 following conditions are met:
13
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
17
18 * Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the
20 following disclaimer in the documentation and/or other
21 materials provided with the distribution.
22
23 * Neither the name of the assimp team, nor the names of its
24 contributors may be used to endorse or promote products
25 derived from this software without specific prior
26 written permission of the assimp team.
27
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
40 ----------------------------------------------------------------------
41 */
42 /// \file X3DImporter.cpp
43 /// \brief X3D-format files importer for Assimp: main algorithm implementation.
44 /// \date 2015-2016
45 /// \author smal.root@gmail.com
46
47 #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
48
49 #include "X3DImporter.hpp"
50 #include "X3DImporter_Macro.hpp"
51 #include <assimp/StringUtils.h>
52
53 // Header files, Assimp.
54 #include <assimp/DefaultIOSystem.h>
55 #include <assimp/fast_atof.h>
56 #include "FIReader.hpp"
57
58 // Header files, stdlib.
59 #include <memory>
60 #include <string>
61 #include <iterator>
62
63 namespace Assimp {
64
65 /// \var aiImporterDesc X3DImporter::Description
66 /// Constant which holds the importer description
67 const aiImporterDesc X3DImporter::Description = {
68 "Extensible 3D(X3D) Importer",
69 "smalcom",
70 "",
71 "See documentation in source code. Chapter: Limitations.",
72 aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
73 0,
74 0,
75 0,
76 0,
77 "x3d x3db"
78 };
79
80 //const std::regex X3DImporter::pattern_nws(R"([^, \t\r\n]+)");
81 //const std::regex X3DImporter::pattern_true(R"(^\s*(?:true|1)\s*$)", std::regex::icase);
82
83 struct WordIterator {
84 using iterator_category = std::input_iterator_tag;
85 using value_type = const char*;
86 using difference_type = ptrdiff_t;
87 using pointer = value_type*;
88 using reference = value_type&;
89
90 static const char *whitespace;
91 const char *start_, *end_;
WordIteratorAssimp::WordIterator92 WordIterator(const char *start, const char *end): start_(start), end_(end) {
93 start_ = start + strspn(start, whitespace);
94 if (start_ >= end_) {
95 start_ = 0;
96 }
97 }
WordIteratorAssimp::WordIterator98 WordIterator(): start_(0), end_(0) {}
WordIteratorAssimp::WordIterator99 WordIterator(const WordIterator &other): start_(other.start_), end_(other.end_) {}
operator =Assimp::WordIterator100 WordIterator &operator=(const WordIterator &other) {
101 start_ = other.start_;
102 end_ = other.end_;
103 return *this;
104 }
operator ==Assimp::WordIterator105 bool operator==(const WordIterator &other) const { return start_ == other.start_; }
operator !=Assimp::WordIterator106 bool operator!=(const WordIterator &other) const { return start_ != other.start_; }
operator ++Assimp::WordIterator107 WordIterator &operator++() {
108 start_ += strcspn(start_, whitespace);
109 start_ += strspn(start_, whitespace);
110 if (start_ >= end_) {
111 start_ = 0;
112 }
113 return *this;
114 }
operator ++Assimp::WordIterator115 WordIterator operator++(int) {
116 WordIterator result(*this);
117 ++(*this);
118 return result;
119 }
operator *Assimp::WordIterator120 const char *operator*() const { return start_; }
121 };
122
123 const char *WordIterator::whitespace = ", \t\r\n";
124
X3DImporter()125 X3DImporter::X3DImporter()
126 : NodeElement_Cur( nullptr )
127 , mReader( nullptr ) {
128 // empty
129 }
130
~X3DImporter()131 X3DImporter::~X3DImporter() {
132 // Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
133 Clear();
134 }
135
Clear()136 void X3DImporter::Clear() {
137 NodeElement_Cur = nullptr;
138 // Delete all elements
139 if(!NodeElement_List.empty()) {
140 for ( std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it ) {
141 delete *it;
142 }
143 NodeElement_List.clear();
144 }
145 }
146
147
148 /*********************************************************************************************************************************************/
149 /************************************************************ Functions: find set ************************************************************/
150 /*********************************************************************************************************************************************/
151
FindNodeElement_FromRoot(const std::string & pID,const CX3DImporter_NodeElement::EType pType,CX3DImporter_NodeElement ** pElement)152 bool X3DImporter::FindNodeElement_FromRoot(const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
153 {
154 for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it)
155 {
156 if(((*it)->Type == pType) && ((*it)->ID == pID))
157 {
158 if(pElement != nullptr) *pElement = *it;
159
160 return true;
161 }
162 }// for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++)
163
164 return false;
165 }
166
FindNodeElement_FromNode(CX3DImporter_NodeElement * pStartNode,const std::string & pID,const CX3DImporter_NodeElement::EType pType,CX3DImporter_NodeElement ** pElement)167 bool X3DImporter::FindNodeElement_FromNode(CX3DImporter_NodeElement* pStartNode, const std::string& pID,
168 const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
169 {
170 bool found = false;// flag: true - if requested element is found.
171
172 // Check if pStartNode - this is the element, we are looking for.
173 if((pStartNode->Type == pType) && (pStartNode->ID == pID))
174 {
175 found = true;
176 if ( pElement != nullptr )
177 {
178 *pElement = pStartNode;
179 }
180
181 goto fne_fn_end;
182 }// if((pStartNode->Type() == pType) && (pStartNode->ID() == pID))
183
184 // Check childs of pStartNode.
185 for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = pStartNode->Child.begin(); ch_it != pStartNode->Child.end(); ++ch_it)
186 {
187 found = FindNodeElement_FromNode(*ch_it, pID, pType, pElement);
188 if ( found )
189 {
190 break;
191 }
192 }// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = it->Child.begin(); ch_it != it->Child.end(); ch_it++)
193
194 fne_fn_end:
195
196 return found;
197 }
198
FindNodeElement(const std::string & pID,const CX3DImporter_NodeElement::EType pType,CX3DImporter_NodeElement ** pElement)199 bool X3DImporter::FindNodeElement(const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
200 {
201 CX3DImporter_NodeElement* tnd = NodeElement_Cur;// temporary pointer to node.
202 bool static_search = false;// flag: true if searching in static node.
203
204 // At first check if we have deal with static node. Go up through parent nodes and check flag.
205 while(tnd != nullptr)
206 {
207 if(tnd->Type == CX3DImporter_NodeElement::ENET_Group)
208 {
209 if(((CX3DImporter_NodeElement_Group*)tnd)->Static)
210 {
211 static_search = true;// Flag found, stop walking up. Node with static flag will holded in tnd variable.
212 break;
213 }
214 }
215
216 tnd = tnd->Parent;// go up in graph.
217 }// while(tnd != nullptr)
218
219 // at now call appropriate search function.
220 if ( static_search )
221 {
222 return FindNodeElement_FromNode( tnd, pID, pType, pElement );
223 }
224 else
225 {
226 return FindNodeElement_FromRoot( pID, pType, pElement );
227 }
228 }
229
230 /*********************************************************************************************************************************************/
231 /************************************************************ Functions: throw set ***********************************************************/
232 /*********************************************************************************************************************************************/
233
Throw_ArgOutOfRange(const std::string & pArgument)234 void X3DImporter::Throw_ArgOutOfRange(const std::string& pArgument)
235 {
236 throw DeadlyImportError("Argument value is out of range for: \"" + pArgument + "\".");
237 }
238
Throw_CloseNotFound(const std::string & pNode)239 void X3DImporter::Throw_CloseNotFound(const std::string& pNode)
240 {
241 throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
242 }
243
Throw_ConvertFail_Str2ArrF(const std::string & pAttrValue)244 void X3DImporter::Throw_ConvertFail_Str2ArrF(const std::string& pAttrValue)
245 {
246 throw DeadlyImportError("In <" + std::string(mReader->getNodeName()) + "> failed to convert attribute value \"" + pAttrValue +
247 "\" from string to array of floats.");
248 }
249
Throw_DEF_And_USE()250 void X3DImporter::Throw_DEF_And_USE()
251 {
252 throw DeadlyImportError("\"DEF\" and \"USE\" can not be defined both in <" + std::string(mReader->getNodeName()) + ">.");
253 }
254
Throw_IncorrectAttr(const std::string & pAttrName)255 void X3DImporter::Throw_IncorrectAttr(const std::string& pAttrName)
256 {
257 throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
258 }
259
Throw_IncorrectAttrValue(const std::string & pAttrName)260 void X3DImporter::Throw_IncorrectAttrValue(const std::string& pAttrName)
261 {
262 throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
263 }
264
Throw_MoreThanOnceDefined(const std::string & pNodeType,const std::string & pDescription)265 void X3DImporter::Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription)
266 {
267 throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
268 }
269
Throw_TagCountIncorrect(const std::string & pNode)270 void X3DImporter::Throw_TagCountIncorrect(const std::string& pNode)
271 {
272 throw DeadlyImportError("Count of open and close tags for node <" + pNode + "> are not equivalent. Seems file is corrupt.");
273 }
274
Throw_USE_NotFound(const std::string & pAttrValue)275 void X3DImporter::Throw_USE_NotFound(const std::string& pAttrValue)
276 {
277 throw DeadlyImportError("Not found node with name \"" + pAttrValue + "\" in <" + std::string(mReader->getNodeName()) + ">.");
278 }
279
280 /*********************************************************************************************************************************************/
281 /************************************************************* Functions: XML set ************************************************************/
282 /*********************************************************************************************************************************************/
283
XML_CheckNode_MustBeEmpty()284 void X3DImporter::XML_CheckNode_MustBeEmpty()
285 {
286 if(!mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must be empty.");
287 }
288
XML_CheckNode_SkipUnsupported(const std::string & pParentNodeName)289 void X3DImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
290 {
291 static const size_t Uns_Skip_Len = 192;
292 const char* Uns_Skip[ Uns_Skip_Len ] = {
293 // CAD geometry component
294 "CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet",
295 // Core
296 "ROUTE", "ExternProtoDeclare", "ProtoDeclare", "ProtoInstance", "ProtoInterface", "WorldInfo",
297 // Distributed interactive simulation (DIS) component
298 "DISEntityManager", "DISEntityTypeMapping", "EspduTransform", "ReceiverPdu", "SignalPdu", "TransmitterPdu",
299 // Cube map environmental texturing component
300 "ComposedCubeMapTexture", "GeneratedCubeMapTexture", "ImageCubeMapTexture",
301 // Environmental effects component
302 "Background", "Fog", "FogCoordinate", "LocalFog", "TextureBackground",
303 // Environmental sensor component
304 "ProximitySensor", "TransformSensor", "VisibilitySensor",
305 // Followers component
306 "ColorChaser", "ColorDamper", "CoordinateChaser", "CoordinateDamper", "OrientationChaser", "OrientationDamper", "PositionChaser", "PositionChaser2D",
307 "PositionDamper", "PositionDamper2D", "ScalarChaser", "ScalarDamper", "TexCoordChaser2D", "TexCoordDamper2D",
308 // Geospatial component
309 "GeoCoordinate", "GeoElevationGrid", "GeoLocation", "GeoLOD", "GeoMetadata", "GeoOrigin", "GeoPositionInterpolator", "GeoProximitySensor",
310 "GeoTouchSensor", "GeoTransform", "GeoViewpoint",
311 // Humanoid Animation (H-Anim) component
312 "HAnimDisplacer", "HAnimHumanoid", "HAnimJoint", "HAnimSegment", "HAnimSite",
313 // Interpolation component
314 "ColorInterpolator", "CoordinateInterpolator", "CoordinateInterpolator2D", "EaseInEaseOut", "NormalInterpolator", "OrientationInterpolator",
315 "PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D",
316 "SplineScalarInterpolator", "SquadOrientationInterpolator",
317 // Key device sensor component
318 "KeySensor", "StringSensor",
319 // Layering component
320 "Layer", "LayerSet", "Viewport",
321 // Layout component
322 "Layout", "LayoutGroup", "LayoutLayer", "ScreenFontStyle", "ScreenGroup",
323 // Navigation component
324 "Billboard", "Collision", "LOD", "NavigationInfo", "OrthoViewpoint", "Viewpoint", "ViewpointGroup",
325 // Networking component
326 "EXPORT", "IMPORT", "Anchor", "LoadSensor",
327 // NURBS component
328 "Contour2D", "ContourPolyline2D", "CoordinateDouble", "NurbsCurve", "NurbsCurve2D", "NurbsOrientationInterpolator", "NurbsPatchSurface",
329 "NurbsPositionInterpolator", "NurbsSet", "NurbsSurfaceInterpolator", "NurbsSweptSurface", "NurbsSwungSurface", "NurbsTextureCoordinate",
330 "NurbsTrimmedSurface",
331 // Particle systems component
332 "BoundedPhysicsModel", "ConeEmitter", "ExplosionEmitter", "ForcePhysicsModel", "ParticleSystem", "PointEmitter", "PolylineEmitter", "SurfaceEmitter",
333 "VolumeEmitter", "WindPhysicsModel",
334 // Picking component
335 "LinePickSensor", "PickableGroup", "PointPickSensor", "PrimitivePickSensor", "VolumePickSensor",
336 // Pointing device sensor component
337 "CylinderSensor", "PlaneSensor", "SphereSensor", "TouchSensor",
338 // Rendering component
339 "ClipPlane",
340 // Rigid body physics
341 "BallJoint", "CollidableOffset", "CollidableShape", "CollisionCollection", "CollisionSensor", "CollisionSpace", "Contact", "DoubleAxisHingeJoint",
342 "MotorJoint", "RigidBody", "RigidBodyCollection", "SingleAxisHingeJoint", "SliderJoint", "UniversalJoint",
343 // Scripting component
344 "Script",
345 // Programmable shaders component
346 "ComposedShader", "FloatVertexAttribute", "Matrix3VertexAttribute", "Matrix4VertexAttribute", "PackagedShader", "ProgramShader", "ShaderPart",
347 "ShaderProgram",
348 // Shape component
349 "FillProperties", "LineProperties", "TwoSidedMaterial",
350 // Sound component
351 "AudioClip", "Sound",
352 // Text component
353 "FontStyle", "Text",
354 // Texturing3D Component
355 "ComposedTexture3D", "ImageTexture3D", "PixelTexture3D", "TextureCoordinate3D", "TextureCoordinate4D", "TextureTransformMatrix3D", "TextureTransform3D",
356 // Texturing component
357 "MovieTexture", "MultiTexture", "MultiTextureCoordinate", "MultiTextureTransform", "PixelTexture", "TextureCoordinateGenerator", "TextureProperties",
358 // Time component
359 "TimeSensor",
360 // Event Utilities component
361 "BooleanFilter", "BooleanSequencer", "BooleanToggle", "BooleanTrigger", "IntegerSequencer", "IntegerTrigger", "TimeTrigger",
362 // Volume rendering component
363 "BlendedVolumeStyle", "BoundaryEnhancementVolumeStyle", "CartoonVolumeStyle", "ComposedVolumeStyle", "EdgeEnhancementVolumeStyle", "IsoSurfaceVolumeData",
364 "OpacityMapVolumeStyle", "ProjectionVolumeStyle", "SegmentedVolumeData", "ShadedVolumeStyle", "SilhouetteEnhancementVolumeStyle", "ToneMappedVolumeStyle",
365 "VolumeData"
366 };
367
368 const std::string nn( mReader->getNodeName() );
369 bool found = false;
370 bool close_found = false;
371
372 for(size_t i = 0; i < Uns_Skip_Len; i++)
373 {
374 if(nn == Uns_Skip[i])
375 {
376 found = true;
377 if(mReader->isEmptyElement())
378 {
379 close_found = true;
380
381 goto casu_cres;
382 }
383
384 while(mReader->read())
385 {
386 if((mReader->getNodeType() == irr::io::EXN_ELEMENT_END) && (nn == mReader->getNodeName()))
387 {
388 close_found = true;
389
390 goto casu_cres;
391 }
392 }
393 }
394 }
395
396 casu_cres:
397
398 if(!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
399
400 if(close_found)
401 LogInfo("Skipping node \"" + nn + "\" in " + pParentNodeName + ".");
402 else
403 Throw_CloseNotFound(nn);
404 }
405
XML_SearchNode(const std::string & pNodeName)406 bool X3DImporter::XML_SearchNode(const std::string& pNodeName)
407 {
408 while(mReader->read())
409 {
410 if((mReader->getNodeType() == irr::io::EXN_ELEMENT) && XML_CheckNode_NameEqual(pNodeName)) return true;
411 }
412
413 return false;
414 }
415
XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)416 bool X3DImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
417 {
418 auto boolValue = std::dynamic_pointer_cast<const FIBoolValue>(mReader->getAttributeEncodedValue(pAttrIdx));
419 if (boolValue) {
420 if (boolValue->value.size() == 1) {
421 return boolValue->value.front();
422 }
423 throw DeadlyImportError("Invalid bool value");
424 }
425 else {
426 std::string val(mReader->getAttributeValue(pAttrIdx));
427
428 if(val == "false")
429 return false;
430 else if(val == "true")
431 return true;
432 else
433 throw DeadlyImportError("Bool attribute value can contain \"false\" or \"true\" not the \"" + val + "\"");
434 }
435 }
436
XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)437 float X3DImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
438 {
439 auto floatValue = std::dynamic_pointer_cast<const FIFloatValue>(mReader->getAttributeEncodedValue(pAttrIdx));
440 if (floatValue) {
441 if (floatValue->value.size() == 1) {
442 return floatValue->value.front();
443 }
444 throw DeadlyImportError("Invalid float value");
445 }
446 else {
447 std::string val;
448 float tvalf;
449
450 ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
451 fast_atoreal_move(val.c_str(), tvalf, false);
452
453 return tvalf;
454 }
455 }
456
XML_ReadNode_GetAttrVal_AsI32(const int pAttrIdx)457 int32_t X3DImporter::XML_ReadNode_GetAttrVal_AsI32(const int pAttrIdx)
458 {
459 auto intValue = std::dynamic_pointer_cast<const FIIntValue>(mReader->getAttributeEncodedValue(pAttrIdx));
460 if (intValue) {
461 if (intValue->value.size() == 1) {
462 return intValue->value.front();
463 }
464 throw DeadlyImportError("Invalid int value");
465 }
466 else {
467 return strtol10(mReader->getAttributeValue(pAttrIdx));
468 }
469 }
470
XML_ReadNode_GetAttrVal_AsCol3f(const int pAttrIdx,aiColor3D & pValue)471 void X3DImporter::XML_ReadNode_GetAttrVal_AsCol3f(const int pAttrIdx, aiColor3D& pValue)
472 {
473 std::vector<float> tlist;
474 std::vector<float>::iterator it;
475
476 XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);
477 if(tlist.size() != 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
478
479 it = tlist.begin();
480 pValue.r = *it++;
481 pValue.g = *it++;
482 pValue.b = *it;
483 }
484
XML_ReadNode_GetAttrVal_AsVec2f(const int pAttrIdx,aiVector2D & pValue)485 void X3DImporter::XML_ReadNode_GetAttrVal_AsVec2f(const int pAttrIdx, aiVector2D& pValue)
486 {
487 std::vector<float> tlist;
488 std::vector<float>::iterator it;
489
490 XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);
491 if(tlist.size() != 2) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
492
493 it = tlist.begin();
494 pValue.x = *it++;
495 pValue.y = *it;
496 }
497
XML_ReadNode_GetAttrVal_AsVec3f(const int pAttrIdx,aiVector3D & pValue)498 void X3DImporter::XML_ReadNode_GetAttrVal_AsVec3f(const int pAttrIdx, aiVector3D& pValue)
499 {
500 std::vector<float> tlist;
501 std::vector<float>::iterator it;
502
503 XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);
504 if(tlist.size() != 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
505
506 it = tlist.begin();
507 pValue.x = *it++;
508 pValue.y = *it++;
509 pValue.z = *it;
510 }
511
XML_ReadNode_GetAttrVal_AsArrB(const int pAttrIdx,std::vector<bool> & pValue)512 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrB(const int pAttrIdx, std::vector<bool>& pValue)
513 {
514 auto boolValue = std::dynamic_pointer_cast<const FIBoolValue>(mReader->getAttributeEncodedValue(pAttrIdx));
515 if (boolValue) {
516 pValue = boolValue->value;
517 }
518 else {
519 const char *val = mReader->getAttributeValue(pAttrIdx);
520 pValue.clear();
521
522 //std::cregex_iterator wordItBegin(val, val + strlen(val), pattern_nws);
523 //const std::cregex_iterator wordItEnd;
524 //std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const std::cmatch &match) { return std::regex_match(match.str(), pattern_true); });
525
526 WordIterator wordItBegin(val, val + strlen(val));
527 WordIterator wordItEnd;
528 std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const char *match) { return (::tolower(match[0]) == 't') || (match[0] == '1'); });
529 }
530 }
531
XML_ReadNode_GetAttrVal_AsArrI32(const int pAttrIdx,std::vector<int32_t> & pValue)532 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrI32(const int pAttrIdx, std::vector<int32_t>& pValue)
533 {
534 auto intValue = std::dynamic_pointer_cast<const FIIntValue>(mReader->getAttributeEncodedValue(pAttrIdx));
535 if (intValue) {
536 pValue = intValue->value;
537 }
538 else {
539 const char *val = mReader->getAttributeValue(pAttrIdx);
540 pValue.clear();
541
542 //std::cregex_iterator wordItBegin(val, val + strlen(val), pattern_nws);
543 //const std::cregex_iterator wordItEnd;
544 //std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const std::cmatch &match) { return std::stoi(match.str()); });
545
546 WordIterator wordItBegin(val, val + strlen(val));
547 WordIterator wordItEnd;
548 std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const char *match) { return atoi(match); });
549 }
550 }
551
XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx,std::vector<float> & pValue)552 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx, std::vector<float>& pValue)
553 {
554 auto floatValue = std::dynamic_pointer_cast<const FIFloatValue>(mReader->getAttributeEncodedValue(pAttrIdx));
555 if (floatValue) {
556 pValue = floatValue->value;
557 }
558 else {
559 const char *val = mReader->getAttributeValue(pAttrIdx);
560 pValue.clear();
561
562 //std::cregex_iterator wordItBegin(val, val + strlen(val), pattern_nws);
563 //const std::cregex_iterator wordItEnd;
564 //std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const std::cmatch &match) { return std::stof(match.str()); });
565
566 WordIterator wordItBegin(val, val + strlen(val));
567 WordIterator wordItEnd;
568 std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const char *match) { return static_cast<float>(atof(match)); });
569 }
570 }
571
XML_ReadNode_GetAttrVal_AsArrD(const int pAttrIdx,std::vector<double> & pValue)572 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrD(const int pAttrIdx, std::vector<double>& pValue)
573 {
574 auto doubleValue = std::dynamic_pointer_cast<const FIDoubleValue>(mReader->getAttributeEncodedValue(pAttrIdx));
575 if (doubleValue) {
576 pValue = doubleValue->value;
577 }
578 else {
579 const char *val = mReader->getAttributeValue(pAttrIdx);
580 pValue.clear();
581
582 //std::cregex_iterator wordItBegin(val, val + strlen(val), pattern_nws);
583 //const std::cregex_iterator wordItEnd;
584 //std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const std::cmatch &match) { return std::stod(match.str()); });
585
586 WordIterator wordItBegin(val, val + strlen(val));
587 WordIterator wordItEnd;
588 std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const char *match) { return atof(match); });
589 }
590 }
591
XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx,std::list<aiColor3D> & pValue)592 void X3DImporter::XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx, std::list<aiColor3D>& pValue)
593 {
594 std::vector<float> tlist;
595
596 XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);// read as list
597 if(tlist.size() % 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
598
599 // copy data to array
600 for(std::vector<float>::iterator it = tlist.begin(); it != tlist.end();)
601 {
602 aiColor3D tcol;
603
604 tcol.r = *it++;
605 tcol.g = *it++;
606 tcol.b = *it++;
607 pValue.push_back(tcol);
608 }
609 }
610
XML_ReadNode_GetAttrVal_AsArrCol3f(const int pAttrIdx,std::vector<aiColor3D> & pValue)611 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrCol3f(const int pAttrIdx, std::vector<aiColor3D>& pValue)
612 {
613 std::list<aiColor3D> tlist;
614
615 XML_ReadNode_GetAttrVal_AsListCol3f(pAttrIdx, tlist);// read as list
616 // and copy to array
617 if(!tlist.empty())
618 {
619 pValue.reserve(tlist.size());
620 for(std::list<aiColor3D>::iterator it = tlist.begin(); it != tlist.end(); ++it) pValue.push_back(*it);
621 }
622 }
623
XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx,std::list<aiColor4D> & pValue)624 void X3DImporter::XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx, std::list<aiColor4D>& pValue)
625 {
626 std::vector<float> tlist;
627
628 XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);// read as list
629 if(tlist.size() % 4) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
630
631 // copy data to array
632 for(std::vector<float>::iterator it = tlist.begin(); it != tlist.end();)
633 {
634 aiColor4D tcol;
635
636 tcol.r = *it++;
637 tcol.g = *it++;
638 tcol.b = *it++;
639 tcol.a = *it++;
640 pValue.push_back(tcol);
641 }
642 }
643
XML_ReadNode_GetAttrVal_AsArrCol4f(const int pAttrIdx,std::vector<aiColor4D> & pValue)644 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrCol4f(const int pAttrIdx, std::vector<aiColor4D>& pValue)
645 {
646 std::list<aiColor4D> tlist;
647
648 XML_ReadNode_GetAttrVal_AsListCol4f(pAttrIdx, tlist);// read as list
649 // and copy to array
650 if(!tlist.empty())
651 {
652 pValue.reserve(tlist.size());
653 for ( std::list<aiColor4D>::iterator it = tlist.begin(); it != tlist.end(); ++it )
654 {
655 pValue.push_back( *it );
656 }
657 }
658 }
659
XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx,std::list<aiVector2D> & pValue)660 void X3DImporter::XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx, std::list<aiVector2D>& pValue)
661 {
662 std::vector<float> tlist;
663
664 XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);// read as list
665 if ( tlist.size() % 2 )
666 {
667 Throw_ConvertFail_Str2ArrF( mReader->getAttributeValue( pAttrIdx ) );
668 }
669
670 // copy data to array
671 for(std::vector<float>::iterator it = tlist.begin(); it != tlist.end();)
672 {
673 aiVector2D tvec;
674
675 tvec.x = *it++;
676 tvec.y = *it++;
677 pValue.push_back(tvec);
678 }
679 }
680
XML_ReadNode_GetAttrVal_AsArrVec2f(const int pAttrIdx,std::vector<aiVector2D> & pValue)681 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrVec2f(const int pAttrIdx, std::vector<aiVector2D>& pValue)
682 {
683 std::list<aiVector2D> tlist;
684
685 XML_ReadNode_GetAttrVal_AsListVec2f(pAttrIdx, tlist);// read as list
686 // and copy to array
687 if(!tlist.empty())
688 {
689 pValue.reserve(tlist.size());
690 for ( std::list<aiVector2D>::iterator it = tlist.begin(); it != tlist.end(); ++it )
691 {
692 pValue.push_back( *it );
693 }
694 }
695 }
696
XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx,std::list<aiVector3D> & pValue)697 void X3DImporter::XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx, std::list<aiVector3D>& pValue)
698 {
699 std::vector<float> tlist;
700
701 XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);// read as list
702 if ( tlist.size() % 3 )
703 {
704 Throw_ConvertFail_Str2ArrF( mReader->getAttributeValue( pAttrIdx ) );
705 }
706
707 // copy data to array
708 for(std::vector<float>::iterator it = tlist.begin(); it != tlist.end();)
709 {
710 aiVector3D tvec;
711
712 tvec.x = *it++;
713 tvec.y = *it++;
714 tvec.z = *it++;
715 pValue.push_back(tvec);
716 }
717 }
718
XML_ReadNode_GetAttrVal_AsArrVec3f(const int pAttrIdx,std::vector<aiVector3D> & pValue)719 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrVec3f(const int pAttrIdx, std::vector<aiVector3D>& pValue)
720 {
721 std::list<aiVector3D> tlist;
722
723 XML_ReadNode_GetAttrVal_AsListVec3f(pAttrIdx, tlist);// read as list
724 // and copy to array
725 if(!tlist.empty())
726 {
727 pValue.reserve(tlist.size());
728 for ( std::list<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); ++it )
729 {
730 pValue.push_back( *it );
731 }
732 }
733 }
734
XML_ReadNode_GetAttrVal_AsListS(const int pAttrIdx,std::list<std::string> & pValue)735 void X3DImporter::XML_ReadNode_GetAttrVal_AsListS(const int pAttrIdx, std::list<std::string>& pValue)
736 {
737 // make copy of attribute value - strings list.
738 const size_t tok_str_len = strlen(mReader->getAttributeValue(pAttrIdx));
739 if ( 0 == tok_str_len )
740 {
741 Throw_IncorrectAttrValue( mReader->getAttributeName( pAttrIdx ) );
742 }
743
744 // get pointer to begin of value.
745 char *tok_str = const_cast<char*>(mReader->getAttributeValue(pAttrIdx));
746 char *tok_str_end = tok_str + tok_str_len;
747 // string list has following format: attr_name='"s1" "s2" "sn"'.
748 do
749 {
750 char* tbeg;
751 char* tend;
752 size_t tlen;
753 std::string tstr;
754
755 // find begin of string(element of string list): "sn".
756 tbeg = strstr(tok_str, "\"");
757 if(tbeg == nullptr) Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
758
759 tbeg++;// forward pointer from '\"' symbol to next after it.
760 tok_str = tbeg;
761 // find end of string(element of string list): "sn".
762 tend = strstr(tok_str, "\"");
763 if(tend == nullptr) Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
764
765 tok_str = tend + 1;
766 // create storage for new string
767 tlen = tend - tbeg;
768 tstr.resize(tlen);// reserve enough space and copy data
769 memcpy((void*)tstr.data(), tbeg, tlen);// not strcpy because end of copied string from tok_str has no terminator.
770 // and store string in output list.
771 pValue.push_back(tstr);
772 } while(tok_str < tok_str_end);
773 }
774
775 /*********************************************************************************************************************************************/
776 /****************************************************** Functions: geometry helper set ******************************************************/
777 /*********************************************************************************************************************************************/
778
GeometryHelper_Make_Point2D(const float pAngle,const float pRadius)779 aiVector3D X3DImporter::GeometryHelper_Make_Point2D(const float pAngle, const float pRadius)
780 {
781 return aiVector3D(pRadius * std::cos(pAngle), pRadius * std::sin(pAngle), 0);
782 }
783
GeometryHelper_Make_Arc2D(const float pStartAngle,const float pEndAngle,const float pRadius,size_t pNumSegments,std::list<aiVector3D> & pVertices)784 void X3DImporter::GeometryHelper_Make_Arc2D(const float pStartAngle, const float pEndAngle, const float pRadius, size_t pNumSegments,
785 std::list<aiVector3D>& pVertices)
786 {
787 // check argument values ranges.
788 if ( ( pStartAngle < -AI_MATH_TWO_PI_F ) || ( pStartAngle > AI_MATH_TWO_PI_F ) )
789 {
790 Throw_ArgOutOfRange( "GeometryHelper_Make_Arc2D.pStartAngle" );
791 }
792 if ( ( pEndAngle < -AI_MATH_TWO_PI_F ) || ( pEndAngle > AI_MATH_TWO_PI_F ) )
793 {
794 Throw_ArgOutOfRange( "GeometryHelper_Make_Arc2D.pEndAngle" );
795 }
796 if ( pRadius <= 0 )
797 {
798 Throw_ArgOutOfRange( "GeometryHelper_Make_Arc2D.pRadius" );
799 }
800
801 // calculate arc angle and check type of arc
802 float angle_full = std::fabs(pEndAngle - pStartAngle);
803 if ( ( angle_full > AI_MATH_TWO_PI_F ) || ( angle_full == 0.0f ) )
804 {
805 angle_full = AI_MATH_TWO_PI_F;
806 }
807
808 // calculate angle for one step - angle to next point of line.
809 float angle_step = angle_full / (float)pNumSegments;
810 // make points
811 for(size_t pi = 0; pi <= pNumSegments; pi++)
812 {
813 float tangle = pStartAngle + pi * angle_step;
814 pVertices.push_back(GeometryHelper_Make_Point2D(tangle, pRadius));
815 }// for(size_t pi = 0; pi <= pNumSegments; pi++)
816
817 // if we making full circle then add last vertex equal to first vertex
818 if(angle_full == AI_MATH_TWO_PI_F) pVertices.push_back(*pVertices.begin());
819 }
820
GeometryHelper_Extend_PointToLine(const std::list<aiVector3D> & pPoint,std::list<aiVector3D> & pLine)821 void X3DImporter::GeometryHelper_Extend_PointToLine(const std::list<aiVector3D>& pPoint, std::list<aiVector3D>& pLine)
822 {
823 std::list<aiVector3D>::const_iterator pit = pPoint.begin();
824 std::list<aiVector3D>::const_iterator pit_last = pPoint.end();
825
826 --pit_last;
827
828 if ( pPoint.size() < 2 )
829 {
830 Throw_ArgOutOfRange( "GeometryHelper_Extend_PointToLine.pPoint.size() can not be less than 2." );
831 }
832
833 // add first point of first line.
834 pLine.push_back(*pit++);
835 // add internal points
836 while(pit != pit_last)
837 {
838 pLine.push_back(*pit);// second point of previous line
839 pLine.push_back(*pit);// first point of next line
840 ++pit;
841 }
842 // add last point of last line
843 pLine.push_back(*pit);
844 }
845
GeometryHelper_Extend_PolylineIdxToLineIdx(const std::list<int32_t> & pPolylineCoordIdx,std::list<int32_t> & pLineCoordIdx)846 void X3DImporter::GeometryHelper_Extend_PolylineIdxToLineIdx(const std::list<int32_t>& pPolylineCoordIdx, std::list<int32_t>& pLineCoordIdx)
847 {
848 std::list<int32_t>::const_iterator plit = pPolylineCoordIdx.begin();
849
850 while(plit != pPolylineCoordIdx.end())
851 {
852 // add first point of polyline
853 pLineCoordIdx.push_back(*plit++);
854 while((*plit != (-1)) && (plit != pPolylineCoordIdx.end()))
855 {
856 std::list<int32_t>::const_iterator plit_next;
857
858 plit_next = plit, ++plit_next;
859 pLineCoordIdx.push_back(*plit);// second point of previous line.
860 pLineCoordIdx.push_back(-1);// delimiter
861 if((*plit_next == (-1)) || (plit_next == pPolylineCoordIdx.end())) break;// current polyline is finished
862
863 pLineCoordIdx.push_back(*plit);// first point of next line.
864 plit = plit_next;
865 }// while((*plit != (-1)) && (plit != pPolylineCoordIdx.end()))
866 }// while(plit != pPolylineCoordIdx.end())
867 }
868
869 #define MESH_RectParallelepiped_CREATE_VERT \
870 aiVector3D vert_set[8]; \
871 float x1, x2, y1, y2, z1, z2, hs; \
872 \
873 hs = pSize.x / 2, x1 = -hs, x2 = hs; \
874 hs = pSize.y / 2, y1 = -hs, y2 = hs; \
875 hs = pSize.z / 2, z1 = -hs, z2 = hs; \
876 vert_set[0].Set(x2, y1, z2); \
877 vert_set[1].Set(x2, y2, z2); \
878 vert_set[2].Set(x2, y2, z1); \
879 vert_set[3].Set(x2, y1, z1); \
880 vert_set[4].Set(x1, y1, z2); \
881 vert_set[5].Set(x1, y2, z2); \
882 vert_set[6].Set(x1, y2, z1); \
883 vert_set[7].Set(x1, y1, z1)
884
GeometryHelper_MakeQL_RectParallelepiped(const aiVector3D & pSize,std::list<aiVector3D> & pVertices)885 void X3DImporter::GeometryHelper_MakeQL_RectParallelepiped(const aiVector3D& pSize, std::list<aiVector3D>& pVertices)
886 {
887 MESH_RectParallelepiped_CREATE_VERT;
888 MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 3, 2, 1, 0);// front
889 MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 6, 7, 4, 5);// back
890 MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 3, 0, 4);// left
891 MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 2, 6, 5, 1);// right
892 MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 0, 1, 5, 4);// top
893 MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 6, 2, 3);// bottom
894 }
895
896 #undef MESH_RectParallelepiped_CREATE_VERT
897
GeometryHelper_CoordIdxStr2FacesArr(const std::vector<int32_t> & pCoordIdx,std::vector<aiFace> & pFaces,unsigned int & pPrimitiveTypes) const898 void X3DImporter::GeometryHelper_CoordIdxStr2FacesArr(const std::vector<int32_t>& pCoordIdx, std::vector<aiFace>& pFaces, unsigned int& pPrimitiveTypes) const
899 {
900 std::vector<int32_t> f_data(pCoordIdx);
901 std::vector<unsigned int> inds;
902 unsigned int prim_type = 0;
903
904 if ( f_data.back() != ( -1 ) )
905 {
906 f_data.push_back( -1 );
907 }
908
909 // reserve average size.
910 pFaces.reserve(f_data.size() / 3);
911 inds.reserve(4);
912 //PrintVectorSet("build. ci", pCoordIdx);
913 for(std::vector<int32_t>::iterator it = f_data.begin(); it != f_data.end(); ++it)
914 {
915 // when face is got count how many indices in it.
916 if(*it == (-1))
917 {
918 aiFace tface;
919 size_t ts;
920
921 ts = inds.size();
922 switch(ts)
923 {
924 case 0: goto mg_m_err;
925 case 1: prim_type |= aiPrimitiveType_POINT; break;
926 case 2: prim_type |= aiPrimitiveType_LINE; break;
927 case 3: prim_type |= aiPrimitiveType_TRIANGLE; break;
928 default: prim_type |= aiPrimitiveType_POLYGON; break;
929 }
930
931 tface.mNumIndices = static_cast<unsigned int>(ts);
932 tface.mIndices = new unsigned int[ts];
933 memcpy(tface.mIndices, inds.data(), ts * sizeof(unsigned int));
934 pFaces.push_back(tface);
935 inds.clear();
936 }// if(*it == (-1))
937 else
938 {
939 inds.push_back(*it);
940 }// if(*it == (-1)) else
941 }// for(std::list<int32_t>::iterator it = f_data.begin(); it != f_data.end(); it++)
942 //PrintVectorSet("build. faces", pCoordIdx);
943
944 pPrimitiveTypes = prim_type;
945
946 return;
947
948 mg_m_err:
949
950 for(size_t i = 0, i_e = pFaces.size(); i < i_e; i++) delete [] pFaces.at(i).mIndices;
951
952 pFaces.clear();
953 }
954
MeshGeometry_AddColor(aiMesh & pMesh,const std::list<aiColor3D> & pColors,const bool pColorPerVertex) const955 void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const
956 {
957 std::list<aiColor4D> tcol;
958
959 // create RGBA array from RGB.
960 for(std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); ++it) tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1));
961
962 // call existing function for adding RGBA colors
963 MeshGeometry_AddColor(pMesh, tcol, pColorPerVertex);
964 }
965
MeshGeometry_AddColor(aiMesh & pMesh,const std::list<aiColor4D> & pColors,const bool pColorPerVertex) const966 void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
967 {
968 std::list<aiColor4D>::const_iterator col_it = pColors.begin();
969
970 if(pColorPerVertex)
971 {
972 if(pColors.size() < pMesh.mNumVertices)
973 {
974 throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + to_string(pColors.size()) + ") can not be less than Vertices count(" +
975 to_string(pMesh.mNumVertices) + ").");
976 }
977
978 // copy colors to mesh
979 pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices];
980 for(size_t i = 0; i < pMesh.mNumVertices; i++) pMesh.mColors[0][i] = *col_it++;
981 }// if(pColorPerVertex)
982 else
983 {
984 if(pColors.size() < pMesh.mNumFaces)
985 {
986 throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + to_string(pColors.size()) + ") can not be less than Faces count(" +
987 to_string(pMesh.mNumFaces) + ").");
988 }
989
990 // copy colors to mesh
991 pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices];
992 for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
993 {
994 // apply color to all vertices of face
995 for ( size_t vi = 0, vi_e = pMesh.mFaces[ fi ].mNumIndices; vi < vi_e; vi++ )
996 {
997 pMesh.mColors[ 0 ][ pMesh.mFaces[ fi ].mIndices[ vi ] ] = *col_it;
998 }
999
1000 ++col_it;
1001 }
1002 }// if(pColorPerVertex) else
1003 }
1004
MeshGeometry_AddColor(aiMesh & pMesh,const std::vector<int32_t> & pCoordIdx,const std::vector<int32_t> & pColorIdx,const std::list<aiColor3D> & pColors,const bool pColorPerVertex) const1005 void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pColorIdx,
1006 const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const
1007 {
1008 std::list<aiColor4D> tcol;
1009
1010 // create RGBA array from RGB.
1011 for ( std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); ++it )
1012 {
1013 tcol.push_back( aiColor4D( ( *it ).r, ( *it ).g, ( *it ).b, 1 ) );
1014 }
1015
1016 // call existing function for adding RGBA colors
1017 MeshGeometry_AddColor(pMesh, pCoordIdx, pColorIdx, tcol, pColorPerVertex);
1018 }
1019
MeshGeometry_AddColor(aiMesh & pMesh,const std::vector<int32_t> & pCoordIdx,const std::vector<int32_t> & pColorIdx,const std::list<aiColor4D> & pColors,const bool pColorPerVertex) const1020 void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pColorIdx,
1021 const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
1022 {
1023 std::vector<aiColor4D> col_tgt_arr;
1024 std::list<aiColor4D> col_tgt_list;
1025 std::vector<aiColor4D> col_arr_copy;
1026
1027 if ( pCoordIdx.size() == 0 )
1028 {
1029 throw DeadlyImportError( "MeshGeometry_AddColor2. pCoordIdx can not be empty." );
1030 }
1031
1032 // copy list to array because we are need indexed access to colors.
1033 col_arr_copy.reserve(pColors.size());
1034 for ( std::list<aiColor4D>::const_iterator it = pColors.begin(); it != pColors.end(); ++it )
1035 {
1036 col_arr_copy.push_back( *it );
1037 }
1038
1039 if(pColorPerVertex)
1040 {
1041 if(pColorIdx.size() > 0)
1042 {
1043 // check indices array count.
1044 if(pColorIdx.size() < pCoordIdx.size())
1045 {
1046 throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + to_string(pColorIdx.size()) +
1047 ") can not be less than Coords inidces count(" + to_string(pCoordIdx.size()) + ").");
1048 }
1049 // create list with colors for every vertex.
1050 col_tgt_arr.resize(pMesh.mNumVertices);
1051 for(std::vector<int32_t>::const_iterator colidx_it = pColorIdx.begin(), coordidx_it = pCoordIdx.begin(); colidx_it != pColorIdx.end(); ++colidx_it, ++coordidx_it)
1052 {
1053 if ( *colidx_it == ( -1 ) )
1054 {
1055 continue;// skip faces delimiter
1056 }
1057 if ( ( unsigned int ) ( *coordidx_it ) > pMesh.mNumVertices )
1058 {
1059 throw DeadlyImportError( "MeshGeometry_AddColor2. Coordinate idx is out of range." );
1060 }
1061 if ( ( unsigned int ) *colidx_it > pMesh.mNumVertices )
1062 {
1063 throw DeadlyImportError( "MeshGeometry_AddColor2. Color idx is out of range." );
1064 }
1065
1066 col_tgt_arr[*coordidx_it] = col_arr_copy[*colidx_it];
1067 }
1068 }// if(pColorIdx.size() > 0)
1069 else
1070 {
1071 // when color indices list is absent use CoordIdx.
1072 // check indices array count.
1073 if(pColors.size() < pMesh.mNumVertices)
1074 {
1075 throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + to_string(pColors.size()) + ") can not be less than Vertices count(" +
1076 to_string(pMesh.mNumVertices) + ").");
1077 }
1078 // create list with colors for every vertex.
1079 col_tgt_arr.resize(pMesh.mNumVertices);
1080 for ( size_t i = 0; i < pMesh.mNumVertices; i++ )
1081 {
1082 col_tgt_arr[ i ] = col_arr_copy[ i ];
1083 }
1084 }// if(pColorIdx.size() > 0) else
1085 }// if(pColorPerVertex)
1086 else
1087 {
1088 if(pColorIdx.size() > 0)
1089 {
1090 // check indices array count.
1091 if(pColorIdx.size() < pMesh.mNumFaces)
1092 {
1093 throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + to_string(pColorIdx.size()) +
1094 ") can not be less than Faces count(" + to_string(pMesh.mNumFaces) + ").");
1095 }
1096 // create list with colors for every vertex using faces indices.
1097 col_tgt_arr.resize(pMesh.mNumFaces);
1098
1099 std::vector<int32_t>::const_iterator colidx_it = pColorIdx.begin();
1100 for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
1101 {
1102 if((unsigned int)*colidx_it > pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddColor2. Face idx is out of range.");
1103
1104 col_tgt_arr[fi] = col_arr_copy[*colidx_it++];
1105 }
1106 }// if(pColorIdx.size() > 0)
1107 else
1108 {
1109 // when color indices list is absent use CoordIdx.
1110 // check indices array count.
1111 if(pColors.size() < pMesh.mNumFaces)
1112 {
1113 throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + to_string(pColors.size()) + ") can not be less than Faces count(" +
1114 to_string(pMesh.mNumFaces) + ").");
1115 }
1116 // create list with colors for every vertex using faces indices.
1117 col_tgt_arr.resize(pMesh.mNumFaces);
1118 for(size_t fi = 0; fi < pMesh.mNumFaces; fi++) col_tgt_arr[fi] = col_arr_copy[fi];
1119
1120 }// if(pColorIdx.size() > 0) else
1121 }// if(pColorPerVertex) else
1122
1123 // copy array to list for calling function that add colors.
1124 for(std::vector<aiColor4D>::const_iterator it = col_tgt_arr.begin(); it != col_tgt_arr.end(); ++it) col_tgt_list.push_back(*it);
1125 // add prepared colors list to mesh.
1126 MeshGeometry_AddColor(pMesh, col_tgt_list, pColorPerVertex);
1127 }
1128
MeshGeometry_AddNormal(aiMesh & pMesh,const std::vector<int32_t> & pCoordIdx,const std::vector<int32_t> & pNormalIdx,const std::list<aiVector3D> & pNormals,const bool pNormalPerVertex) const1129 void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pNormalIdx,
1130 const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const
1131 {
1132 std::vector<size_t> tind;
1133 std::vector<aiVector3D> norm_arr_copy;
1134
1135 // copy list to array because we are need indexed access to normals.
1136 norm_arr_copy.reserve(pNormals.size());
1137 for ( std::list<aiVector3D>::const_iterator it = pNormals.begin(); it != pNormals.end(); ++it )
1138 {
1139 norm_arr_copy.push_back( *it );
1140 }
1141
1142 if(pNormalPerVertex)
1143 {
1144 if(pNormalIdx.size() > 0)
1145 {
1146 // check indices array count.
1147 if(pNormalIdx.size() != pCoordIdx.size()) throw DeadlyImportError("Normals and Coords inidces count must be equal.");
1148
1149 tind.reserve(pNormalIdx.size());
1150 for(std::vector<int32_t>::const_iterator it = pNormalIdx.begin(); it != pNormalIdx.end(); ++it)
1151 {
1152 if(*it != (-1)) tind.push_back(*it);
1153 }
1154
1155 // copy normals to mesh
1156 pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
1157 for(size_t i = 0; (i < pMesh.mNumVertices) && (i < tind.size()); i++)
1158 {
1159 if(tind[i] >= norm_arr_copy.size())
1160 throw DeadlyImportError("MeshGeometry_AddNormal. Normal index(" + to_string(tind[i]) +
1161 ") is out of range. Normals count: " + to_string(norm_arr_copy.size()) + ".");
1162
1163 pMesh.mNormals[i] = norm_arr_copy[tind[i]];
1164 }
1165 }
1166 else
1167 {
1168 if(pNormals.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and vertices count must be equal.");
1169
1170 // copy normals to mesh
1171 pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
1172 std::list<aiVector3D>::const_iterator norm_it = pNormals.begin();
1173 for(size_t i = 0; i < pMesh.mNumVertices; i++) pMesh.mNormals[i] = *norm_it++;
1174 }
1175 }// if(pNormalPerVertex)
1176 else
1177 {
1178 if(pNormalIdx.size() > 0)
1179 {
1180 if(pMesh.mNumFaces != pNormalIdx.size()) throw DeadlyImportError("Normals faces count must be equal to mesh faces count.");
1181
1182 std::vector<int32_t>::const_iterator normidx_it = pNormalIdx.begin();
1183
1184 tind.reserve(pNormalIdx.size());
1185 for(size_t i = 0, i_e = pNormalIdx.size(); i < i_e; i++) tind.push_back(*normidx_it++);
1186
1187 }
1188 else
1189 {
1190 tind.reserve(pMesh.mNumFaces);
1191 for(size_t i = 0; i < pMesh.mNumFaces; i++) tind.push_back(i);
1192
1193 }
1194
1195 // copy normals to mesh
1196 pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
1197 for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
1198 {
1199 aiVector3D tnorm;
1200
1201 tnorm = norm_arr_copy[tind[fi]];
1202 for(size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = tnorm;
1203 }
1204 }// if(pNormalPerVertex) else
1205 }
1206
MeshGeometry_AddNormal(aiMesh & pMesh,const std::list<aiVector3D> & pNormals,const bool pNormalPerVertex) const1207 void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const
1208 {
1209 std::list<aiVector3D>::const_iterator norm_it = pNormals.begin();
1210
1211 if(pNormalPerVertex)
1212 {
1213 if(pNormals.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and vertices count must be equal.");
1214
1215 // copy normals to mesh
1216 pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
1217 for(size_t i = 0; i < pMesh.mNumVertices; i++) pMesh.mNormals[i] = *norm_it++;
1218 }// if(pNormalPerVertex)
1219 else
1220 {
1221 if(pNormals.size() != pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and faces count must be equal.");
1222
1223 // copy normals to mesh
1224 pMesh.mNormals = new aiVector3D[pMesh.mNumVertices];
1225 for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
1226 {
1227 // apply color to all vertices of face
1228 for(size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = *norm_it;
1229
1230 ++norm_it;
1231 }
1232 }// if(pNormalPerVertex) else
1233 }
1234
MeshGeometry_AddTexCoord(aiMesh & pMesh,const std::vector<int32_t> & pCoordIdx,const std::vector<int32_t> & pTexCoordIdx,const std::list<aiVector2D> & pTexCoords) const1235 void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pTexCoordIdx,
1236 const std::list<aiVector2D>& pTexCoords) const
1237 {
1238 std::vector<aiVector3D> texcoord_arr_copy;
1239 std::vector<aiFace> faces;
1240 unsigned int prim_type;
1241
1242 // copy list to array because we are need indexed access to normals.
1243 texcoord_arr_copy.reserve(pTexCoords.size());
1244 for(std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); ++it)
1245 {
1246 texcoord_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0));
1247 }
1248
1249 if(pTexCoordIdx.size() > 0)
1250 {
1251 GeometryHelper_CoordIdxStr2FacesArr(pTexCoordIdx, faces, prim_type);
1252 if ( faces.empty() )
1253 {
1254 throw DeadlyImportError( "Failed to add texture coordinates to mesh, faces list is empty." );
1255 }
1256 if ( faces.size() != pMesh.mNumFaces )
1257 {
1258 throw DeadlyImportError( "Texture coordinates faces count must be equal to mesh faces count." );
1259 }
1260 }
1261 else
1262 {
1263 GeometryHelper_CoordIdxStr2FacesArr(pCoordIdx, faces, prim_type);
1264 }
1265
1266 pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices];
1267 pMesh.mNumUVComponents[0] = 2;
1268 for(size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++)
1269 {
1270 if(pMesh.mFaces[fi].mNumIndices != faces.at(fi).mNumIndices)
1271 throw DeadlyImportError("Number of indices in texture face and mesh face must be equal. Invalid face index: " + to_string(fi) + ".");
1272
1273 for(size_t ii = 0; ii < pMesh.mFaces[fi].mNumIndices; ii++)
1274 {
1275 size_t vert_idx = pMesh.mFaces[fi].mIndices[ii];
1276 size_t tc_idx = faces.at(fi).mIndices[ii];
1277
1278 pMesh.mTextureCoords[0][vert_idx] = texcoord_arr_copy.at(tc_idx);
1279 }
1280 }// for(size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++)
1281 }
1282
MeshGeometry_AddTexCoord(aiMesh & pMesh,const std::list<aiVector2D> & pTexCoords) const1283 void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<aiVector2D>& pTexCoords) const
1284 {
1285 std::vector<aiVector3D> tc_arr_copy;
1286
1287 if ( pTexCoords.size() != pMesh.mNumVertices )
1288 {
1289 throw DeadlyImportError( "MeshGeometry_AddTexCoord. Texture coordinates and vertices count must be equal." );
1290 }
1291
1292 // copy list to array because we are need convert aiVector2D to aiVector3D and also get indexed access as a bonus.
1293 tc_arr_copy.reserve(pTexCoords.size());
1294 for ( std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); ++it )
1295 {
1296 tc_arr_copy.push_back( aiVector3D( ( *it ).x, ( *it ).y, 0 ) );
1297 }
1298
1299 // copy texture coordinates to mesh
1300 pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices];
1301 pMesh.mNumUVComponents[0] = 2;
1302 for ( size_t i = 0; i < pMesh.mNumVertices; i++ )
1303 {
1304 pMesh.mTextureCoords[ 0 ][ i ] = tc_arr_copy[ i ];
1305 }
1306 }
1307
GeometryHelper_MakeMesh(const std::vector<int32_t> & pCoordIdx,const std::list<aiVector3D> & pVertices) const1308 aiMesh* X3DImporter::GeometryHelper_MakeMesh(const std::vector<int32_t>& pCoordIdx, const std::list<aiVector3D>& pVertices) const
1309 {
1310 std::vector<aiFace> faces;
1311 unsigned int prim_type = 0;
1312
1313 // create faces array from input string with vertices indices.
1314 GeometryHelper_CoordIdxStr2FacesArr(pCoordIdx, faces, prim_type);
1315 if ( !faces.size() )
1316 {
1317 throw DeadlyImportError( "Failed to create mesh, faces list is empty." );
1318 }
1319
1320 //
1321 // Create new mesh and copy geometry data.
1322 //
1323 aiMesh *tmesh = new aiMesh;
1324 size_t ts = faces.size();
1325 // faces
1326 tmesh->mFaces = new aiFace[ts];
1327 tmesh->mNumFaces = static_cast<unsigned int>(ts);
1328 for(size_t i = 0; i < ts; i++) tmesh->mFaces[i] = faces.at(i);
1329
1330 // vertices
1331 std::list<aiVector3D>::const_iterator vit = pVertices.begin();
1332
1333 ts = pVertices.size();
1334 tmesh->mVertices = new aiVector3D[ts];
1335 tmesh->mNumVertices = static_cast<unsigned int>(ts);
1336 for ( size_t i = 0; i < ts; i++ )
1337 {
1338 tmesh->mVertices[ i ] = *vit++;
1339 }
1340
1341 // set primitives type and return result.
1342 tmesh->mPrimitiveTypes = prim_type;
1343
1344 return tmesh;
1345 }
1346
1347 /*********************************************************************************************************************************************/
1348 /************************************************************ Functions: parse set ***********************************************************/
1349 /*********************************************************************************************************************************************/
1350
ParseHelper_Group_Begin(const bool pStatic)1351 void X3DImporter::ParseHelper_Group_Begin(const bool pStatic)
1352 {
1353 CX3DImporter_NodeElement_Group* new_group = new CX3DImporter_NodeElement_Group(NodeElement_Cur, pStatic);// create new node with current node as parent.
1354
1355 // if we are adding not the root element then add new element to current element child list.
1356 if ( NodeElement_Cur != nullptr )
1357 {
1358 NodeElement_Cur->Child.push_back( new_group );
1359 }
1360
1361 NodeElement_List.push_back(new_group);// it's a new element - add it to list.
1362 NodeElement_Cur = new_group;// switch current element to new one.
1363 }
1364
ParseHelper_Node_Enter(CX3DImporter_NodeElement * pNode)1365 void X3DImporter::ParseHelper_Node_Enter(CX3DImporter_NodeElement* pNode)
1366 {
1367 NodeElement_Cur->Child.push_back(pNode);// add new element to current element child list.
1368 NodeElement_Cur = pNode;// switch current element to new one.
1369 }
1370
ParseHelper_Node_Exit()1371 void X3DImporter::ParseHelper_Node_Exit()
1372 {
1373 // check if we can walk up.
1374 if ( NodeElement_Cur != nullptr )
1375 {
1376 NodeElement_Cur = NodeElement_Cur->Parent;
1377 }
1378 }
1379
ParseHelper_FixTruncatedFloatString(const char * pInStr,std::string & pOutString)1380 void X3DImporter::ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
1381 {
1382 pOutString.clear();
1383 const size_t instr_len = strlen(pInStr);
1384 if ( 0 == instr_len )
1385 {
1386 return;
1387 }
1388
1389 pOutString.reserve(instr_len * 3 / 2);
1390 // check and correct floats in format ".x". Must be "x.y".
1391 if ( pInStr[ 0 ] == '.' )
1392 {
1393 pOutString.push_back( '0' );
1394 }
1395
1396 pOutString.push_back(pInStr[0]);
1397 for(size_t ci = 1; ci < instr_len; ci++)
1398 {
1399 if((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t')))
1400 {
1401 pOutString.push_back('0');
1402 pOutString.push_back('.');
1403 }
1404 else
1405 {
1406 pOutString.push_back(pInStr[ci]);
1407 }
1408 }
1409 }
1410
1411 extern FIVocabulary X3D_vocabulary_3_2;
1412 extern FIVocabulary X3D_vocabulary_3_3;
1413
ParseFile(const std::string & pFile,IOSystem * pIOHandler)1414 void X3DImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
1415 {
1416 std::unique_ptr<FIReader> OldReader = std::move(mReader);// store current XMLreader.
1417 std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
1418
1419 // Check whether we can read from the file
1420 if ( file.get() == nullptr )
1421 {
1422 throw DeadlyImportError( "Failed to open X3D file " + pFile + "." );
1423 }
1424 mReader = FIReader::create(file.get());
1425 if ( !mReader )
1426 {
1427 throw DeadlyImportError( "Failed to create XML reader for file" + pFile + "." );
1428 }
1429 mReader->registerVocabulary("urn:web3d:x3d:fi-vocabulary-3.2", &X3D_vocabulary_3_2);
1430 mReader->registerVocabulary("urn:web3d:x3d:fi-vocabulary-3.3", &X3D_vocabulary_3_3);
1431 // start reading
1432 ParseNode_Root();
1433
1434 // restore old XMLreader
1435 mReader = std::move(OldReader);
1436 }
1437
ParseNode_Root()1438 void X3DImporter::ParseNode_Root()
1439 {
1440 // search for root tag <X3D>
1441 if ( !XML_SearchNode( "X3D" ) )
1442 {
1443 throw DeadlyImportError( "Root node \"X3D\" not found." );
1444 }
1445
1446 ParseHelper_Group_Begin();// create root node element.
1447 // parse other contents
1448 while(mReader->read())
1449 {
1450 if ( mReader->getNodeType() != irr::io::EXN_ELEMENT )
1451 {
1452 continue;
1453 }
1454
1455 if(XML_CheckNode_NameEqual("head"))
1456 ParseNode_Head();
1457 else if(XML_CheckNode_NameEqual("Scene"))
1458 ParseNode_Scene();
1459 else
1460 XML_CheckNode_SkipUnsupported("Root");
1461 }
1462
1463 // exit from root node element.
1464 ParseHelper_Node_Exit();
1465 }
1466
ParseNode_Head()1467 void X3DImporter::ParseNode_Head()
1468 {
1469 bool close_found = false;// flag: true if close tag of node are found.
1470
1471 while(mReader->read())
1472 {
1473 if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
1474 {
1475 if(XML_CheckNode_NameEqual("meta"))
1476 {
1477 XML_CheckNode_MustBeEmpty();
1478
1479 // adding metadata from <head> as MetaString from <Scene>
1480 bool added( false );
1481 CX3DImporter_NodeElement_MetaString* ms = new CX3DImporter_NodeElement_MetaString(NodeElement_Cur);
1482
1483 ms->Name = mReader->getAttributeValueSafe("name");
1484 // name must not be empty
1485 if(!ms->Name.empty())
1486 {
1487 ms->Value.push_back(mReader->getAttributeValueSafe("content"));
1488 NodeElement_List.push_back(ms);
1489 if ( NodeElement_Cur != nullptr )
1490 {
1491 NodeElement_Cur->Child.push_back( ms );
1492 added = true;
1493 }
1494 }
1495 // if an error has occurred, release instance
1496 if ( !added ) {
1497 delete ms;
1498 }
1499 }// if(XML_CheckNode_NameEqual("meta"))
1500 }// if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
1501 else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1502 {
1503 if(XML_CheckNode_NameEqual("head"))
1504 {
1505 close_found = true;
1506 break;
1507 }
1508 }// if(mReader->getNodeType() == irr::io::EXN_ELEMENT) else
1509 }// while(mReader->read())
1510
1511 if ( !close_found )
1512 {
1513 Throw_CloseNotFound( "head" );
1514 }
1515 }
1516
ParseNode_Scene()1517 void X3DImporter::ParseNode_Scene()
1518 {
1519 auto GroupCounter_Increase = [](size_t& pCounter, const char* pGroupName) -> void
1520 {
1521 pCounter++;
1522 if(pCounter == 0) throw DeadlyImportError("Group counter overflow. Too much groups with type: " + std::string(pGroupName) + ".");
1523 };
1524
1525 auto GroupCounter_Decrease = [&](size_t& pCounter, const char* pGroupName) -> void
1526 {
1527 if(pCounter == 0) Throw_TagCountIncorrect(pGroupName);
1528
1529 pCounter--;
1530 };
1531
1532 static const char* GroupName_Group = "Group";
1533 static const char* GroupName_StaticGroup = "StaticGroup";
1534 static const char* GroupName_Transform = "Transform";
1535 static const char* GroupName_Switch = "Switch";
1536
1537 bool close_found = false;
1538 size_t counter_group = 0;
1539 size_t counter_transform = 0;
1540 size_t counter_switch = 0;
1541
1542 // while create static node? Because objects name used deeper in "USE" attribute can be equal to some meta in <head> node.
1543 ParseHelper_Group_Begin(true);
1544 while(mReader->read())
1545 {
1546 if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
1547 {
1548 if(XML_CheckNode_NameEqual("Shape"))
1549 {
1550 ParseNode_Shape_Shape();
1551 }
1552 else if(XML_CheckNode_NameEqual(GroupName_Group))
1553 {
1554 GroupCounter_Increase(counter_group, GroupName_Group);
1555 ParseNode_Grouping_Group();
1556 // if node is empty then decrease group counter at this place.
1557 if(mReader->isEmptyElement()) GroupCounter_Decrease(counter_group, GroupName_Group);
1558 }
1559 else if(XML_CheckNode_NameEqual(GroupName_StaticGroup))
1560 {
1561 GroupCounter_Increase(counter_group, GroupName_StaticGroup);
1562 ParseNode_Grouping_StaticGroup();
1563 // if node is empty then decrease group counter at this place.
1564 if(mReader->isEmptyElement()) GroupCounter_Decrease(counter_group, GroupName_StaticGroup);
1565 }
1566 else if(XML_CheckNode_NameEqual(GroupName_Transform))
1567 {
1568 GroupCounter_Increase(counter_transform, GroupName_Transform);
1569 ParseNode_Grouping_Transform();
1570 // if node is empty then decrease group counter at this place.
1571 if(mReader->isEmptyElement()) GroupCounter_Decrease(counter_transform, GroupName_Transform);
1572 }
1573 else if(XML_CheckNode_NameEqual(GroupName_Switch))
1574 {
1575 GroupCounter_Increase(counter_switch, GroupName_Switch);
1576 ParseNode_Grouping_Switch();
1577 // if node is empty then decrease group counter at this place.
1578 if(mReader->isEmptyElement()) GroupCounter_Decrease(counter_switch, GroupName_Switch);
1579 }
1580 else if(XML_CheckNode_NameEqual("DirectionalLight"))
1581 {
1582 ParseNode_Lighting_DirectionalLight();
1583 }
1584 else if(XML_CheckNode_NameEqual("PointLight"))
1585 {
1586 ParseNode_Lighting_PointLight();
1587 }
1588 else if(XML_CheckNode_NameEqual("SpotLight"))
1589 {
1590 ParseNode_Lighting_SpotLight();
1591 }
1592 else if(XML_CheckNode_NameEqual("Inline"))
1593 {
1594 ParseNode_Networking_Inline();
1595 }
1596 else if(!ParseHelper_CheckRead_X3DMetadataObject())
1597 {
1598 XML_CheckNode_SkipUnsupported("Scene");
1599 }
1600 }// if(mReader->getNodeType() == irr::io::EXN_ELEMENT)
1601 else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
1602 {
1603 if(XML_CheckNode_NameEqual("Scene"))
1604 {
1605 close_found = true;
1606
1607 break;
1608 }
1609 else if(XML_CheckNode_NameEqual(GroupName_Group))
1610 {
1611 GroupCounter_Decrease(counter_group, GroupName_Group);
1612 ParseNode_Grouping_GroupEnd();
1613 }
1614 else if(XML_CheckNode_NameEqual(GroupName_StaticGroup))
1615 {
1616 GroupCounter_Decrease(counter_group, GroupName_StaticGroup);
1617 ParseNode_Grouping_StaticGroupEnd();
1618 }
1619 else if(XML_CheckNode_NameEqual(GroupName_Transform))
1620 {
1621 GroupCounter_Decrease(counter_transform, GroupName_Transform);
1622 ParseNode_Grouping_TransformEnd();
1623 }
1624 else if(XML_CheckNode_NameEqual(GroupName_Switch))
1625 {
1626 GroupCounter_Decrease(counter_switch, GroupName_Switch);
1627 ParseNode_Grouping_SwitchEnd();
1628 }
1629 }// if(mReader->getNodeType() == irr::io::EXN_ELEMENT) else
1630 }// while(mReader->read())
1631
1632 ParseHelper_Node_Exit();
1633
1634 if(counter_group) Throw_TagCountIncorrect("Group");
1635 if(counter_transform) Throw_TagCountIncorrect("Transform");
1636 if(counter_switch) Throw_TagCountIncorrect("Switch");
1637 if(!close_found) Throw_CloseNotFound("Scene");
1638
1639 }
1640
1641 /*********************************************************************************************************************************************/
1642 /******************************************************** Functions: BaseImporter set ********************************************************/
1643 /*********************************************************************************************************************************************/
1644
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool pCheckSig) const1645 bool X3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool pCheckSig) const
1646 {
1647 const std::string extension = GetExtension(pFile);
1648
1649 if((extension == "x3d") || (extension == "x3db")) return true;
1650
1651 if(!extension.length() || pCheckSig)
1652 {
1653 const char* tokens[] = { "DOCTYPE X3D PUBLIC", "http://www.web3d.org/specifications/x3d" };
1654
1655 return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2);
1656 }
1657
1658 return false;
1659 }
1660
GetExtensionList(std::set<std::string> & pExtensionList)1661 void X3DImporter::GetExtensionList(std::set<std::string>& pExtensionList)
1662 {
1663 pExtensionList.insert("x3d");
1664 pExtensionList.insert("x3db");
1665 }
1666
GetInfo() const1667 const aiImporterDesc* X3DImporter::GetInfo () const
1668 {
1669 return &Description;
1670 }
1671
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)1672 void X3DImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
1673 {
1674 mpIOHandler = pIOHandler;
1675
1676 Clear();// delete old graph.
1677 std::string::size_type slashPos = pFile.find_last_of("\\/");
1678 pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1));
1679 ParseFile(pFile, pIOHandler);
1680 pIOHandler->PopDirectory();
1681 //
1682 // Assimp use static arrays of objects for fast speed of rendering. That's good, but need some additional operations/
1683 // We know that geometry objects(meshes) are stored in <Shape>, also in <Shape>-><Appearance> materials(in Assimp logical view)
1684 // are stored. So at first we need to count how meshes and materials are stored in scene graph.
1685 //
1686 // at first creating root node for aiScene.
1687 pScene->mRootNode = new aiNode;
1688 pScene->mRootNode->mParent = nullptr;
1689 pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
1690 //search for root node element
1691 NodeElement_Cur = NodeElement_List.front();
1692 while(NodeElement_Cur->Parent != nullptr) NodeElement_Cur = NodeElement_Cur->Parent;
1693
1694 {// fill aiScene with objects.
1695 std::list<aiMesh*> mesh_list;
1696 std::list<aiMaterial*> mat_list;
1697 std::list<aiLight*> light_list;
1698
1699 // create nodes tree
1700 Postprocess_BuildNode(*NodeElement_Cur, *pScene->mRootNode, mesh_list, mat_list, light_list);
1701 // copy needed data to scene
1702 if(!mesh_list.empty())
1703 {
1704 std::list<aiMesh*>::const_iterator it = mesh_list.begin();
1705
1706 pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size());
1707 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
1708 for(size_t i = 0; i < pScene->mNumMeshes; i++) pScene->mMeshes[i] = *it++;
1709 }
1710
1711 if(!mat_list.empty())
1712 {
1713 std::list<aiMaterial*>::const_iterator it = mat_list.begin();
1714
1715 pScene->mNumMaterials = static_cast<unsigned int>(mat_list.size());
1716 pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
1717 for(size_t i = 0; i < pScene->mNumMaterials; i++) pScene->mMaterials[i] = *it++;
1718 }
1719
1720 if(!light_list.empty())
1721 {
1722 std::list<aiLight*>::const_iterator it = light_list.begin();
1723
1724 pScene->mNumLights = static_cast<unsigned int>(light_list.size());
1725 pScene->mLights = new aiLight*[pScene->mNumLights];
1726 for(size_t i = 0; i < pScene->mNumLights; i++) pScene->mLights[i] = *it++;
1727 }
1728 }// END: fill aiScene with objects.
1729
1730 ///TODO: IME optimize tree
1731 }
1732
1733 }// namespace Assimp
1734
1735 #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER
1736