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