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