Coverage Report

Created: 2025-11-11 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/X3D/X3DImporter.cpp
Line
Count
Source
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2025, assimp team
6
7
All rights reserved.
8
9
Redistribution and use of this software in source and binary forms,
10
with or without modification, are permitted provided that the
11
following conditions are met:
12
13
* Redistributions of source code must retain the above
14
copyright notice, this list of conditions and the
15
following disclaimer.
16
17
* Redistributions in binary form must reproduce the above
18
copyright notice, this list of conditions and the
19
following disclaimer in the documentation and/or other
20
materials provided with the distribution.
21
22
* Neither the name of the assimp team, nor the names of its
23
contributors may be used to endorse or promote products
24
derived from this software without specific prior
25
written permission of the assimp team.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39
----------------------------------------------------------------------
40
*/
41
/// \file   X3DImporter.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 "AssetLib/VRML/VrmlConverter.hpp"
49
#include "X3DImporter.hpp"
50
#include "X3DImporter_Macro.hpp"
51
52
#include <assimp/DefaultIOSystem.h>
53
54
// Header files, stdlib.
55
#include <iterator>
56
#include <memory>
57
58
#if defined(ASSIMP_BUILD_NO_VRML_IMPORTER)
59
#define X3D_FORMATS_DESCR_STR "Extensible 3D(X3D, X3DB) Importer"
60
#define X3D_FORMATS_EXTENSIONS_STR "x3d x3db"
61
#else
62
#define X3D_FORMATS_DESCR_STR "VRML(WRL, X3DV) and Extensible 3D(X3D, X3DB) Importer"
63
#define X3D_FORMATS_EXTENSIONS_STR "wrl x3d x3db x3dv"
64
#endif // #if defined(ASSIMP_BUILD_NO_VRML_IMPORTER)
65
66
namespace Assimp {
67
68
/// Constant which holds the importer description
69
const aiImporterDesc X3DImporter::Description = {
70
    X3D_FORMATS_DESCR_STR,
71
    "smalcom",
72
    "",
73
    "See documentation in source code. Chapter: Limitations.",
74
    aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
75
    0,
76
    0,
77
    0,
78
    0,
79
    X3D_FORMATS_EXTENSIONS_STR
80
};
81
82
0
bool X3DImporter::isNodeEmpty(XmlNode &node) {
83
0
    return node.first_child().empty();
84
0
}
85
86
0
void X3DImporter::checkNodeMustBeEmpty(XmlNode &node) {
87
0
    if (!isNodeEmpty(node)) throw DeadlyImportError(std::string("Node <") + node.name() + "> must be empty.");
88
0
}
89
90
0
void X3DImporter::skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) {
91
0
    static const size_t Uns_Skip_Len = 192;
92
0
    static constexpr char const * Uns_Skip[Uns_Skip_Len] = {
93
        // CAD geometry component
94
0
        "CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet",
95
        // Core
96
0
        "ROUTE", "ExternProtoDeclare", "ProtoDeclare", "ProtoInstance", "ProtoInterface", "WorldInfo",
97
        // Distributed interactive simulation (DIS) component
98
0
        "DISEntityManager", "DISEntityTypeMapping", "EspduTransform", "ReceiverPdu", "SignalPdu", "TransmitterPdu",
99
        // Cube map environmental texturing component
100
0
        "ComposedCubeMapTexture", "GeneratedCubeMapTexture", "ImageCubeMapTexture",
101
        // Environmental effects component
102
0
        "Background", "Fog", "FogCoordinate", "LocalFog", "TextureBackground",
103
        // Environmental sensor component
104
0
        "ProximitySensor", "TransformSensor", "VisibilitySensor",
105
        // Followers component
106
0
        "ColorChaser", "ColorDamper", "CoordinateChaser", "CoordinateDamper", "OrientationChaser", "OrientationDamper", "PositionChaser", "PositionChaser2D",
107
0
        "PositionDamper", "PositionDamper2D", "ScalarChaser", "ScalarDamper", "TexCoordChaser2D", "TexCoordDamper2D",
108
        // Geospatial component
109
0
        "GeoCoordinate", "GeoElevationGrid", "GeoLocation", "GeoLOD", "GeoMetadata", "GeoOrigin", "GeoPositionInterpolator", "GeoProximitySensor",
110
0
        "GeoTouchSensor", "GeoTransform", "GeoViewpoint",
111
        // Humanoid Animation (H-Anim) component
112
0
        "HAnimDisplacer", "HAnimHumanoid", "HAnimJoint", "HAnimSegment", "HAnimSite",
113
        // Interpolation component
114
0
        "ColorInterpolator", "CoordinateInterpolator", "CoordinateInterpolator2D", "EaseInEaseOut", "NormalInterpolator", "OrientationInterpolator",
115
0
        "PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D",
116
0
        "SplineScalarInterpolator", "SquadOrientationInterpolator",
117
        // Key device sensor component
118
0
        "KeySensor", "StringSensor",
119
        // Layering component
120
0
        "Layer", "LayerSet", "Viewport",
121
        // Layout component
122
0
        "Layout", "LayoutGroup", "LayoutLayer", "ScreenFontStyle", "ScreenGroup",
123
        // Navigation component
124
0
        "Billboard", "Collision", "LOD", "NavigationInfo", "OrthoViewpoint", "Viewpoint", "ViewpointGroup",
125
        // Networking component
126
0
        "EXPORT", "IMPORT", "Anchor", "LoadSensor",
127
        // NURBS component
128
0
        "Contour2D", "ContourPolyline2D", "CoordinateDouble", "NurbsCurve", "NurbsCurve2D", "NurbsOrientationInterpolator", "NurbsPatchSurface",
129
0
        "NurbsPositionInterpolator", "NurbsSet", "NurbsSurfaceInterpolator", "NurbsSweptSurface", "NurbsSwungSurface", "NurbsTextureCoordinate",
130
0
        "NurbsTrimmedSurface",
131
        // Particle systems component
132
0
        "BoundedPhysicsModel", "ConeEmitter", "ExplosionEmitter", "ForcePhysicsModel", "ParticleSystem", "PointEmitter", "PolylineEmitter", "SurfaceEmitter",
133
0
        "VolumeEmitter", "WindPhysicsModel",
134
        // Picking component
135
0
        "LinePickSensor", "PickableGroup", "PointPickSensor", "PrimitivePickSensor", "VolumePickSensor",
136
        // Pointing device sensor component
137
0
        "CylinderSensor", "PlaneSensor", "SphereSensor", "TouchSensor",
138
        // Rendering component
139
0
        "ClipPlane",
140
        // Rigid body physics
141
0
        "BallJoint", "CollidableOffset", "CollidableShape", "CollisionCollection", "CollisionSensor", "CollisionSpace", "Contact", "DoubleAxisHingeJoint",
142
0
        "MotorJoint", "RigidBody", "RigidBodyCollection", "SingleAxisHingeJoint", "SliderJoint", "UniversalJoint",
143
        // Scripting component
144
0
        "Script",
145
        // Programmable shaders component
146
0
        "ComposedShader", "FloatVertexAttribute", "Matrix3VertexAttribute", "Matrix4VertexAttribute", "PackagedShader", "ProgramShader", "ShaderPart",
147
0
        "ShaderProgram",
148
        // Shape component
149
0
        "FillProperties", "LineProperties", "TwoSidedMaterial",
150
        // Sound component
151
0
        "AudioClip", "Sound",
152
        // Text component
153
0
        "FontStyle", "Text",
154
        // Texturing3D Component
155
0
        "ComposedTexture3D", "ImageTexture3D", "PixelTexture3D", "TextureCoordinate3D", "TextureCoordinate4D", "TextureTransformMatrix3D", "TextureTransform3D",
156
        // Texturing component
157
0
        "MovieTexture", "MultiTexture", "MultiTextureCoordinate", "MultiTextureTransform", "PixelTexture", "TextureCoordinateGenerator", "TextureProperties",
158
        // Time component
159
0
        "TimeSensor",
160
        // Event Utilities component
161
0
        "BooleanFilter", "BooleanSequencer", "BooleanToggle", "BooleanTrigger", "IntegerSequencer", "IntegerTrigger", "TimeTrigger",
162
        // Volume rendering component
163
0
        "BlendedVolumeStyle", "BoundaryEnhancementVolumeStyle", "CartoonVolumeStyle", "ComposedVolumeStyle", "EdgeEnhancementVolumeStyle", "IsoSurfaceVolumeData",
164
0
        "OpacityMapVolumeStyle", "ProjectionVolumeStyle", "SegmentedVolumeData", "ShadedVolumeStyle", "SilhouetteEnhancementVolumeStyle", "ToneMappedVolumeStyle",
165
0
        "VolumeData"
166
0
    };
167
168
0
    const std::string nn = node.name();
169
170
0
    if (nn.empty()) {
171
0
        const std::string nv = node.value();
172
0
        if (!nv.empty()) {
173
0
            LogInfo("Ignoring comment \"" + nv + "\" in " + pParentNodeName + ".");
174
0
            return;
175
0
        }
176
0
    }
177
178
0
    bool found = false;
179
180
0
    for (size_t i = 0; i < Uns_Skip_Len; i++) {
181
0
        if (nn == Uns_Skip[i]) {
182
0
            found = true;
183
0
        }
184
0
    }
185
186
0
    if (!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
187
188
0
    LogInfo("Skipping node \"" + nn + "\" in " + pParentNodeName + ".");
189
0
}
190
191
X3DImporter::X3DImporter() :
192
891
        mNodeElementCur(nullptr),
193
891
        mScene(nullptr),
194
891
        mpIOHandler(nullptr) {
195
    // empty
196
891
}
197
198
891
X3DImporter::~X3DImporter() {
199
    // Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
200
891
    Clear();
201
891
}
202
203
897
void X3DImporter::Clear() {
204
897
    mNodeElementCur = nullptr;
205
    // Delete all elements
206
897
    if (!NodeElement_List.empty()) {
207
0
        for (std::list<X3DNodeElementBase *>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it) {
208
0
            delete *it;
209
0
        }
210
0
        NodeElement_List.clear();
211
0
    }
212
897
}
213
214
6
void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) {
215
6
    ai_assert(nullptr != pIOHandler);
216
217
6
    static const std::string mode = "rb";
218
6
    std::unique_ptr<IOStream> fileStream(pIOHandler->Open(file, mode));
219
6
    if (!fileStream) {
220
0
        throw DeadlyImportError("Failed to open file " + file + ".");
221
0
    }
222
223
6
    XmlParser theParser;
224
6
    if (!theParser.parse(fileStream.get())) {
225
6
        return;
226
6
    }
227
0
    ParseFile(theParser);
228
0
}
229
230
0
void X3DImporter::ParseFile(std::istream &myIstream) {
231
0
    XmlParser theParser;
232
0
    if (!theParser.parse(myIstream)) {
233
0
        LogInfo("ParseFile(): ERROR: failed to convert VRML istream to xml");
234
0
        return;
235
0
    }
236
0
    ParseFile(theParser);
237
0
}
238
239
0
void X3DImporter::ParseFile(XmlParser &theParser) {
240
0
    XmlNode *node = theParser.findNode("X3D");
241
0
    if (nullptr == node) {
242
0
        return;
243
0
    }
244
245
0
    for (auto &currentNode : node->children()) {
246
0
        const std::string &currentName = currentNode.name();
247
0
        if (currentName == "head") {
248
0
            readHead(currentNode);
249
0
        } else if (currentName == "Scene") {
250
0
            readScene(currentNode);
251
0
        } else {
252
0
            skipUnsupportedNode("X3D", currentNode);
253
0
        }
254
0
    }
255
0
}
256
257
82
bool X3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool checkSig) const {
258
82
    if (checkSig) {
259
82
        if (GetExtension(pFile) == "x3d")
260
0
            return true;
261
82
    }
262
263
82
    return false;
264
82
}
265
266
6
void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
267
6
    mpIOHandler = pIOHandler;
268
269
6
    Clear();
270
6
    std::stringstream ss = ConvertVrmlFileToX3dXmlFile(pFile);
271
6
    const bool isReadFromMem{ ss.str().length() > 0 };
272
6
    if (!isReadFromMem) {
273
6
        std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
274
6
        if (!stream) {
275
0
            throw DeadlyImportError("Could not open file for reading");
276
0
        }
277
6
    }
278
6
    std::string::size_type slashPos = pFile.find_last_of("\\/");
279
280
6
    mScene = pScene;
281
6
    pScene->mRootNode = new aiNode(pFile);
282
6
    pScene->mRootNode->mParent = nullptr;
283
6
    pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
284
285
6
    if (isReadFromMem) {
286
0
        ParseFile(ss);
287
6
    } else {
288
6
        pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1));
289
6
        ParseFile(pFile, pIOHandler);
290
6
        pIOHandler->PopDirectory();
291
6
    }
292
293
    //search for root node element
294
295
6
    mNodeElementCur = NodeElement_List.front();
296
6
    if (mNodeElementCur == nullptr) {
297
6
        return;
298
6
    }
299
0
    while (mNodeElementCur->Parent != nullptr) {
300
0
        mNodeElementCur = mNodeElementCur->Parent;
301
0
    }
302
303
0
    { // fill aiScene with objects.
304
0
        std::list<aiMesh *> mesh_list;
305
0
        std::list<aiMaterial *> mat_list;
306
0
        std::list<aiLight *> light_list;
307
308
        // create nodes tree
309
0
        Postprocess_BuildNode(*mNodeElementCur, *pScene->mRootNode, mesh_list, mat_list, light_list);
310
        // copy needed data to scene
311
0
        if (!mesh_list.empty()) {
312
0
            std::list<aiMesh *>::const_iterator it = mesh_list.begin();
313
314
0
            pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size());
315
0
            pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
316
0
            for (size_t i = 0; i < pScene->mNumMeshes; i++)
317
0
                pScene->mMeshes[i] = *it++;
318
0
        }
319
320
0
        if (!mat_list.empty()) {
321
0
            std::list<aiMaterial *>::const_iterator it = mat_list.begin();
322
323
0
            pScene->mNumMaterials = static_cast<unsigned int>(mat_list.size());
324
0
            pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
325
0
            for (size_t i = 0; i < pScene->mNumMaterials; i++)
326
0
                pScene->mMaterials[i] = *it++;
327
0
        }
328
329
0
        if (!light_list.empty()) {
330
0
            std::list<aiLight *>::const_iterator it = light_list.begin();
331
332
0
            pScene->mNumLights = static_cast<unsigned int>(light_list.size());
333
0
            pScene->mLights = new aiLight *[pScene->mNumLights];
334
0
            for (size_t i = 0; i < pScene->mNumLights; i++)
335
0
                pScene->mLights[i] = *it++;
336
0
        }
337
0
    }
338
0
}
339
340
883
const aiImporterDesc *X3DImporter::GetInfo() const {
341
883
    return &Description;
342
883
}
343
344
struct meta_entry {
345
    std::string name;
346
    std::string value;
347
};
348
349
0
void X3DImporter::readHead(XmlNode &node) {
350
0
    std::vector<meta_entry> metaArray;
351
0
    for (auto currentNode : node.children()) {
352
0
        const std::string &currentName = currentNode.name();
353
0
        if (currentName == "meta") {
354
            //checkNodeMustBeEmpty(node);
355
0
            meta_entry entry;
356
0
            if (XmlParser::getStdStrAttribute(currentNode, "name", entry.name)) {
357
0
                XmlParser::getStdStrAttribute(currentNode, "content", entry.value);
358
0
                metaArray.emplace_back(entry);
359
0
            }
360
0
        }
361
        // TODO: check if other node types in head should be supported
362
0
    }
363
0
    mScene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metaArray.size()));
364
0
    unsigned int i = 0;
365
0
    for (const auto& currentMeta : metaArray) {
366
0
        mScene->mMetaData->Set(i, currentMeta.name, aiString(currentMeta.value));
367
0
        ++i;
368
0
    }
369
0
}
370
371
0
void X3DImporter::readChildNodes(XmlNode &node, const std::string &pParentNodeName) {
372
0
    if (node.empty()) {
373
0
        return;
374
0
    }
375
0
    for (auto currentNode : node.children()) {
376
0
        const std::string &currentName = currentNode.name();
377
0
        if (currentName == "Shape")
378
0
            readShape(currentNode);
379
0
        else if (currentName == "Group") {
380
0
            startReadGroup(currentNode);
381
0
            readChildNodes(currentNode, "Group");
382
0
            endReadGroup();
383
0
        } else if (currentName == "StaticGroup") {
384
0
            startReadStaticGroup(currentNode);
385
0
            readChildNodes(currentNode, "StaticGroup");
386
0
            endReadStaticGroup();
387
0
        } else if (currentName == "Transform") {
388
0
            startReadTransform(currentNode);
389
0
            readChildNodes(currentNode, "Transform");
390
0
            endReadTransform();
391
0
        } else if (currentName == "Switch") {
392
0
            startReadSwitch(currentNode);
393
0
            readChildNodes(currentNode, "Switch");
394
0
            endReadSwitch();
395
0
        } else if (currentName == "DirectionalLight") {
396
0
            readDirectionalLight(currentNode);
397
0
        } else if (currentName == "PointLight") {
398
0
            readPointLight(currentNode);
399
0
        } else if (currentName == "SpotLight") {
400
0
            readSpotLight(currentNode);
401
0
        } else if (currentName == "Inline") {
402
0
            readInline(currentNode);
403
0
        } else if (!checkForMetadataNode(currentNode)) {
404
0
            skipUnsupportedNode(pParentNodeName, currentNode);
405
0
        }
406
0
    }
407
0
}
408
409
0
void X3DImporter::readScene(XmlNode &node) {
410
0
    ParseHelper_Group_Begin(true);
411
0
    readChildNodes(node, "Scene");
412
0
    ParseHelper_Node_Exit();
413
0
}
414
415
/*********************************************************************************************************************************************/
416
/************************************************************ Functions: find set ************************************************************/
417
/*********************************************************************************************************************************************/
418
419
0
bool X3DImporter::FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) {
420
0
    for (std::list<X3DNodeElementBase *>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it) {
421
0
        if (((*it)->Type == pType) && ((*it)->ID == pID)) {
422
0
            if (pElement != nullptr) *pElement = *it;
423
424
0
            return true;
425
0
        }
426
0
    } // for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++)
427
428
0
    return false;
429
0
}
430
431
bool X3DImporter::FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID,
432
0
        const X3DElemType pType, X3DNodeElementBase **pElement) {
433
0
    bool found = false; // flag: true - if requested element is found.
434
435
    // Check if pStartNode - this is the element, we are looking for.
436
0
    if ((pStartNode->Type == pType) && (pStartNode->ID == pID)) {
437
0
        found = true;
438
0
        if (pElement != nullptr) {
439
0
            *pElement = pStartNode;
440
0
        }
441
442
0
        goto fne_fn_end;
443
0
    } // if((pStartNode->Type() == pType) && (pStartNode->ID() == pID))
444
445
    // Check childs of pStartNode.
446
0
    for (std::list<X3DNodeElementBase *>::iterator ch_it = pStartNode->Children.begin(); ch_it != pStartNode->Children.end(); ++ch_it) {
447
0
        found = FindNodeElement_FromNode(*ch_it, pID, pType, pElement);
448
0
        if (found) {
449
0
            break;
450
0
        }
451
0
    } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = it->Children.begin(); ch_it != it->Children.end(); ch_it++)
452
453
0
fne_fn_end:
454
455
0
    return found;
456
0
}
457
458
0
bool X3DImporter::FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) {
459
0
    X3DNodeElementBase *tnd = mNodeElementCur; // temporary pointer to node.
460
0
    bool static_search = false; // flag: true if searching in static node.
461
462
    // At first check if we have deal with static node. Go up through parent nodes and check flag.
463
0
    while (tnd != nullptr) {
464
0
        if (tnd->Type == X3DElemType::ENET_Group) {
465
0
            if (((X3DNodeElementGroup *)tnd)->Static) {
466
0
                static_search = true; // Flag found, stop walking up. Node with static flag will holded in tnd variable.
467
0
                break;
468
0
            }
469
0
        }
470
471
0
        tnd = tnd->Parent; // go up in graph.
472
0
    } // while (tnd != nullptr)
473
474
    // at now call appropriate search function.
475
0
    if (static_search) {
476
0
        return FindNodeElement_FromNode(tnd, pID, pType, pElement);
477
0
    } else {
478
0
        return FindNodeElement_FromRoot(pID, pType, pElement);
479
0
    }
480
0
}
481
482
/*********************************************************************************************************************************************/
483
/************************************************************ Functions: parse set ***********************************************************/
484
/*********************************************************************************************************************************************/
485
486
0
void X3DImporter::ParseHelper_Group_Begin(const bool pStatic) {
487
0
    X3DNodeElementGroup *new_group = new X3DNodeElementGroup(mNodeElementCur, pStatic); // create new node with current node as parent.
488
489
    // if we are adding not the root element then add new element to current element child list.
490
0
    if (mNodeElementCur != nullptr) {
491
0
        mNodeElementCur->Children.push_back(new_group);
492
0
    }
493
494
0
    NodeElement_List.push_back(new_group); // it's a new element - add it to list.
495
0
    mNodeElementCur = new_group; // switch current element to new one.
496
0
}
497
498
0
void X3DImporter::ParseHelper_Node_Enter(X3DNodeElementBase *pNode) {
499
0
    ai_assert(nullptr != pNode);
500
501
0
    mNodeElementCur->Children.push_back(pNode); // add new element to current element child list.
502
0
    mNodeElementCur = pNode; // switch current element to new one.
503
0
}
504
505
0
void X3DImporter::ParseHelper_Node_Exit() {
506
    // check if we can walk up.
507
0
    if (mNodeElementCur != nullptr) {
508
0
        mNodeElementCur = mNodeElementCur->Parent;
509
0
    }
510
0
}
511
512
} // namespace Assimp
513
514
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER