瀏覽代碼

migrated x3d importer to pugixml reader

René Martin 4 年之前
父節點
當前提交
3f6a371b64

+ 1 - 1
code/AssetLib/X3D/X3DGeoHelper.cpp

@@ -116,7 +116,7 @@ void X3DGeoHelper::polylineIdx_to_lineIdx(const std::list<int32_t> &pPolylineCoo
     vert_set[6].Set(x1, y2, z1);            \
     vert_set[7].Set(x1, y1, z1)
 
-void X3DGeoHelper::rect_parallele_piped(const aiVector3D &pSize, std::list<aiVector3D> &pVertices) {
+void X3DGeoHelper::rect_parallel_epiped(const aiVector3D &pSize, std::list<aiVector3D> &pVertices) {
     MESH_RectParallelepiped_CREATE_VERT;
     MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 3, 2, 1, 0); // front
     MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 6, 7, 4, 5); // back

+ 1 - 1
code/AssetLib/X3D/X3DGeoHelper.h

@@ -19,7 +19,7 @@ public:
     static void make_arc2D(float pStartAngle, float pEndAngle, float pRadius, size_t numSegments, std::list<aiVector3D> &pVertices);
     static void extend_point_to_line(const std::list<aiVector3D> &pPoint, std::list<aiVector3D> &pLine);
     static void polylineIdx_to_lineIdx(const std::list<int32_t> &pPolylineCoordIdx, std::list<int32_t> &pLineCoordIdx);
-    static void rect_parallele_piped(const aiVector3D &pSize, std::list<aiVector3D> &pVertices);
+    static void rect_parallel_epiped(const aiVector3D &pSize, std::list<aiVector3D> &pVertices);
     static void coordIdx_str2faces_arr(const std::vector<int32_t> &pCoordIdx, std::vector<aiFace> &pFaces, unsigned int &pPrimitiveTypes);
     static void add_color(aiMesh &pMesh, const std::list<aiColor3D> &pColors, const bool pColorPerVertex);
     static void add_color(aiMesh &pMesh, const std::list<aiColor4D> &pColors, const bool pColorPerVertex);

+ 127 - 2543
code/AssetLib/X3D/X3DImporter.cpp

@@ -46,11 +46,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
 
 #include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
 
-#include <assimp/StringUtils.h>
-#include <assimp/ParsingUtils.h>
 #include <assimp/DefaultIOSystem.h>
-#include <assimp/fast_atof.h>
 
 // Header files, stdlib.
 #include <iterator>
@@ -72,59 +70,15 @@ const aiImporterDesc X3DImporter::Description = {
     "x3d x3db"
 };
 
-struct WordIterator {
-    using iterator_category = std::input_iterator_tag;
-    using value_type = const char *;
-    using difference_type = ptrdiff_t;
-    using pointer = value_type *;
-    using reference = value_type &;
-
-    static const char *whitespace;
-    const char *mStart, *mEnd;
-
-    WordIterator(const char *start, const char *end) :
-            mStart(start),
-            mEnd(end) {
-        mStart = start + ::strspn(start, whitespace);
-        if (mStart >= mEnd) {
-            mStart = 0;
-        }
-    }
-    WordIterator() :
-            mStart(0),
-            mEnd(0) {}
-    WordIterator(const WordIterator &other) :
-            mStart(other.mStart),
-            mEnd(other.mEnd) {}
-    WordIterator &operator=(const WordIterator &other) {
-        mStart = other.mStart;
-        mEnd = other.mEnd;
-        return *this;
-    }
-
-    bool operator==(const WordIterator &other) const { return mStart == other.mStart; }
-
-    bool operator!=(const WordIterator &other) const { return mStart != other.mStart; }
-
-    WordIterator &operator++() {
-        mStart += strcspn(mStart, whitespace);
-        mStart += strspn(mStart, whitespace);
-        if (mStart >= mEnd) {
-            mStart = 0;
-        }
-        return *this;
-    }
-    WordIterator operator++(int) {
-        WordIterator result(*this);
-        ++(*this);
-        return result;
-    }
-    const char *operator*() const { return mStart; }
-};
+bool X3DImporter::isNodeEmpty(XmlNode &node) {
+    return node.first_child().empty();
+}
 
-const char *WordIterator::whitespace = ", \t\r\n";
+void X3DImporter::checkNodeMustBeEmpty(XmlNode &node) {
+    if (isNodeEmpty(node)) throw DeadlyImportError(std::string("Node <") + node.name() + "> must be empty.");
+}
 
-void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) {
+void X3DImporter::skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) {
     static const size_t Uns_Skip_Len = 192;
     static const char *Uns_Skip[Uns_Skip_Len] = {
         // CAD geometry component
@@ -203,16 +157,20 @@ void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) {
     };
 
     const std::string nn = node.name();
+
+    if (nn.empty()) {
+        const std::string nv = node.value();
+        if (!nv.empty()) {
+            LogInfo("Ignoring comment \"" + nv + "\" in " + pParentNodeName + ".");
+            return;
+        }
+    }
+
     bool found = false;
-    bool close_found = false;
 
     for (size_t i = 0; i < Uns_Skip_Len; i++) {
         if (nn == Uns_Skip[i]) {
             found = true;
-            if (node.empty()) {
-                close_found = true;
-                break;
-            }
         }
     }
 
@@ -223,7 +181,8 @@ void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) {
 
 X3DImporter::X3DImporter() :
         mNodeElementCur(nullptr),
-        mScene(nullptr) {
+        mScene(nullptr),
+        mpIOHandler(nullptr) {
     // empty
 }
 
@@ -265,9 +224,11 @@ void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) {
     for (auto &currentNode : node->children()) {
         const std::string &currentName = currentNode.name();
         if (currentName == "head") {
-            readMetadata(currentNode);
+            readHead(currentNode);
         } else if (currentName == "Scene") {
             readScene(currentNode);
+        } else {
+            skipUnsupportedNode("X3D", currentNode);
         }
     }
 }
@@ -283,23 +244,25 @@ bool X3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, b
     return false;
 }
 
-void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler ) {
+void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
+    mpIOHandler = pIOHandler;
+
+    Clear();
     std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
     if (!stream) {
         throw DeadlyImportError("Could not open file for reading");
     }
     std::string::size_type slashPos = pFile.find_last_of("\\/");
 
-    pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1));
-    ParseFile(pFile, pIOHandler);
-    pIOHandler->PopDirectory();
-
-    //
     mScene = pScene;
     pScene->mRootNode = new aiNode(pFile);
     pScene->mRootNode->mParent = nullptr;
     pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
 
+    pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1));
+    ParseFile(pFile, pIOHandler);
+    pIOHandler->PopDirectory();
+
     //search for root node element
 
     mNodeElementCur = NodeElement_List.front();
@@ -342,7 +305,6 @@ void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOS
                 pScene->mLights[i] = *it++;
         }
     }
-
 }
 
 const aiImporterDesc *X3DImporter::GetInfo() const {
@@ -354,2540 +316,162 @@ struct meta_entry {
     std::string value;
 };
 
-void X3DImporter::readMetadata(XmlNode &node) {
+void X3DImporter::readHead(XmlNode &node) {
     std::vector<meta_entry> metaArray;
     for (auto currentNode : node.children()) {
         const std::string &currentName = currentNode.name();
         if (currentName == "meta") {
+            checkNodeMustBeEmpty(node);
             meta_entry entry;
             if (XmlParser::getStdStrAttribute(currentNode, "name", entry.name)) {
                 XmlParser::getStdStrAttribute(currentNode, "content", entry.value);
                 metaArray.emplace_back(entry);
             }
         }
+        // TODO: check if other node types in head should be supported
     }
     mScene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(metaArray.size()));
     unsigned int i = 0;
     for (auto currentMeta : metaArray) {
-        mScene->mMetaData->Set(i, currentMeta.name, currentMeta.value);
+        mScene->mMetaData->Set(i, currentMeta.name, aiString(currentMeta.value));
         ++i;
     }
 }
 
-void X3DImporter::readScene(XmlNode &node) {
+void X3DImporter::readChildNodes(XmlNode &node, const std::string &pParentNodeName) {
     for (auto currentNode : node.children()) {
         const std::string &currentName = currentNode.name();
-        if (currentName == "Viewpoint") {
-            readViewpoint(currentNode);
-        }
-    }
-}
-
-void X3DImporter::readViewpoint(XmlNode &node) {
-    for (auto currentNode : node.children()) {
-        //const std::string &currentName = currentNode.name();
-    }
-}
-
-void readMetadataBoolean(XmlNode &node, X3DNodeElementBase *parent) {
-    std::string val;
-    X3DNodeElementMetaBoolean *boolean = nullptr;
-    if (XmlParser::getStdStrAttribute(node, "value", val)) {
-        std::vector<std::string> values;
-        tokenize<std::string>(val, values, " ");
-        boolean = new X3DNodeElementMetaBoolean(parent);
-        for (size_t i = 0; i < values.size(); ++i) {
-            bool current_boolean = false;
-            if (values[i] == "true") {
-                current_boolean = true;
-            }
-            boolean->Value.emplace_back(current_boolean);
-        }
-    }
-}
-
-void readMetadataDouble(XmlNode &node, X3DNodeElementBase *parent) {
-    std::string val;
-    X3DNodeElementMetaDouble *doubleNode = nullptr;
-    if (XmlParser::getStdStrAttribute(node, "value", val)) {
-        std::vector<std::string> values;
-        tokenize<std::string>(val, values, " ");
-        doubleNode = new X3DNodeElementMetaDouble(parent);
-        for (size_t i = 0; i < values.size(); ++i) {
-            double current_double = static_cast<double>(fast_atof(values[i].c_str()));
-            doubleNode->Value.emplace_back(current_double);
-        }
-    }
-}
-
-void readMetadataFloat(XmlNode &node, X3DNodeElementBase *parent) {
-    std::string val;
-    X3DNodeElementMetaFloat *floatNode = nullptr;
-    if (XmlParser::getStdStrAttribute(node, "value", val)) {
-        std::vector<std::string> values;
-        tokenize<std::string>(val, values, " ");
-        floatNode = new X3DNodeElementMetaFloat(parent);
-        for (size_t i = 0; i < values.size(); ++i) {
-            float current_float = static_cast<float>(fast_atof(values[i].c_str()));
-            floatNode->Value.emplace_back(current_float);
-        }
-    }
-}
-
-void readMetadataInteger(XmlNode &node, X3DNodeElementBase *parent) {
-    std::string val;
-    X3DNodeElementMetaInt *intNode = nullptr;
-    if (XmlParser::getStdStrAttribute(node, "value", val)) {
-        std::vector<std::string> values;
-        tokenize<std::string>(val, values, " ");
-        intNode = new X3DNodeElementMetaInt(parent);
-        for (size_t i = 0; i < values.size(); ++i) {
-            int current_int = static_cast<int>(std::atoi(values[i].c_str()));
-            intNode->Value.emplace_back(current_int);
-        }
-    }
-}
-
-void readMetadataSet(XmlNode &node, X3DNodeElementBase *parent) {
-    std::string val;
-    X3DNodeElementMetaSet *setNode = new X3DNodeElementMetaSet(parent);
-    if (XmlParser::getStdStrAttribute(node, "name", val)) {
-        setNode->Name = val;
-    }
-
-    if (XmlParser::getStdStrAttribute(node, "reference", val)) {
-        setNode->Reference = val;
-    }
-}
-
-void readMetadataString(XmlNode &node, X3DNodeElementBase *parent) {
-    std::string val;
-    X3DNodeElementMetaString *strNode = nullptr;
-    if (XmlParser::getStdStrAttribute(node, "value", val)) {
-        std::vector<std::string> values;
-        tokenize<std::string>(val, values, " ");
-        strNode = new X3DNodeElementMetaString(parent);
-        for (size_t i = 0; i < values.size(); ++i) {
-            strNode->Value.emplace_back(values[i]);
-        }
-    }
-}
-
-void X3DImporter::ParseDirectionalLight(XmlNode &node) {
-    std::string def, use;
-    float ambientIntensity = 0;
-    aiColor3D color(1, 1, 1);
-    aiVector3D direction(0, 0, -1);
-    bool global = false;
-    float intensity = 1;
-    bool on = true;
-    X3DNodeElementBase *ne = nullptr;
-
-    //MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity);
-    //MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f);
-    MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f);
-    MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_DirectionalLight, ne);
-    } else {
-        if (on) {
-            // create and if needed - define new geometry object.
-            ne = new X3DNodeNodeElementLight(CX3DImporter_NodeElement::ENET_DirectionalLight, NodeElement_Cur);
-            if (!def.empty())
-                ne->ID = def;
-            else
-                ne->ID = "DirectionalLight_" + to_string((size_t)ne); // make random name
-
-            ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity;
-            ((X3DNodeNodeElementLight *)ne)->Color = color;
-            ((X3DNodeNodeElementLight *)ne)->Direction = direction;
-            ((X3DNodeNodeElementLight *)ne)->Global = global;
-            ((X3DNodeNodeElementLight *)ne)->Intensity = intensity;
-            // Assimp want a node with name similar to a light. "Why? I don't no." )
-            ParseHelper_Group_Begin(false);
-
-            mNodeElementCur->ID = ne->ID; // assign name to node and return to light element.
-            ParseHelper_Node_Exit();
-            // check for child nodes
-            if (!mReader->isEmptyElement())
-                ParseNode_Metadata(ne, "DirectionalLight");
-            else
-                mNodeElementCur->Child.push_back(ne); // add made object as child to current element
-
-            NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-        } // if(on)
-    } // if(!use.empty()) else
-}
-
-// <PointLight
-// DEF=""               ID
-// USE=""               IDREF
-// ambientIntensity="0" SFFloat [inputOutput]
-// attenuation="1 0 0"  SFVec3f [inputOutput]
-// color="1 1 1"        SFColor [inputOutput]
-// global="true"        SFBool  [inputOutput]
-// intensity="1"        SFFloat [inputOutput]
-// location="0 0 0"     SFVec3f [inputOutput]
-// on="true"            SFBool  [inputOutput]
-// radius="100"         SFFloat [inputOutput]
-// />
-void X3DImporter::ParseNode_Lighting_PointLight() {
-    std::string def, use;
-    float ambientIntensity = 0;
-    aiVector3D attenuation(1, 0, 0);
-    aiColor3D color(1, 1, 1);
-    bool global = true;
-    float intensity = 1;
-    aiVector3D location(0, 0, 0);
-    bool on = true;
-    float radius = 100;
-    X3DNodeElementBase *ne = nullptr;
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f);
-    MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f);
-    MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f);
-    MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_PointLight, ne);
-    } else {
-        if (on) {
-            // create and if needed - define new geometry object.
-            ne = new X3DNodeNodeElementLight(X3DElemType::ENET_PointLight, mNodeElementCur);
-            if (!def.empty()) ne->ID = def;
-
-            ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity;
-            ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation;
-            ((X3DNodeNodeElementLight *)ne)->Color = color;
-            ((X3DNodeNodeElementLight *)ne)->Global = global;
-            ((X3DNodeNodeElementLight *)ne)->Intensity = intensity;
-            ((X3DNodeNodeElementLight *)ne)->Location = location;
-            ((X3DNodeNodeElementLight *)ne)->Radius = radius;
-            // Assimp want a node with name similar to a light. "Why? I don't no." )
-            ParseHelper_Group_Begin(false);
-            // make random name
-            if (ne->ID.empty()) ne->ID = "PointLight_" + to_string((size_t)ne);
-
-            mNodeElementCur->ID = ne->ID; // assign name to node and return to light element.
-            ParseHelper_Node_Exit();
-            // check for child nodes
-            if (!mReader->isEmptyElement())
-                ParseNode_Metadata(ne, "PointLight");
-            else
-                mNodeElementCur->Child.push_back(ne); // add made object as child to current element
-
-            NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-        } // if(on)
-    } // if(!use.empty()) else
-}
-
-// <SpotLight
-// DEF=""                 ID
-// USE=""                 IDREF
-// ambientIntensity="0"   SFFloat [inputOutput]
-// attenuation="1 0 0"    SFVec3f [inputOutput]
-// beamWidth="0.7854"     SFFloat [inputOutput]
-// color="1 1 1"          SFColor [inputOutput]
-// cutOffAngle="1.570796" SFFloat [inputOutput]
-// direction="0 0 -1"     SFVec3f [inputOutput]
-// global="true"          SFBool  [inputOutput]
-// intensity="1"          SFFloat [inputOutput]
-// location="0 0 0"       SFVec3f [inputOutput]
-// on="true"              SFBool  [inputOutput]
-// radius="100"           SFFloat [inputOutput]
-// />
-void X3DImporter::ParseNode_Lighting_SpotLight() {
-    std::string def, use;
-    float ambientIntensity = 0;
-    aiVector3D attenuation(1, 0, 0);
-    float beamWidth = 0.7854f;
-    aiColor3D color(1, 1, 1);
-    float cutOffAngle = 1.570796f;
-    aiVector3D direction(0, 0, -1);
-    bool global = true;
-    float intensity = 1;
-    aiVector3D location(0, 0, 0);
-    bool on = true;
-    float radius = 100;
-    X3DNodeElementBase *ne = nullptr;
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f);
-    MACRO_ATTRREAD_CHECK_RET("beamWidth", beamWidth, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f);
-    MACRO_ATTRREAD_CHECK_RET("cutOffAngle", cutOffAngle, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f);
-    MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f);
-    MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_SpotLight, ne);
-    } else {
-        if (on) {
-            // create and if needed - define new geometry object.
-            ne = new X3DNodeNodeElementLight(X3DElemType::ENET_SpotLight, mNodeElementCur);
-            if (!def.empty())
-                ne->ID = def;
-
-            if (beamWidth > cutOffAngle)
-                beamWidth = cutOffAngle;
-
-            ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity;
-            ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation;
-            ((X3DNodeNodeElementLight *)ne)->BeamWidth = beamWidth;
-            ((X3DNodeNodeElementLight *)ne)->Color = color;
-            ((X3DNodeNodeElementLight *)ne)->CutOffAngle = cutOffAngle;
-            ((X3DNodeNodeElementLight *)ne)->Direction = direction;
-            ((X3DNodeNodeElementLight *)ne)->Global = global;
-            ((X3DNodeNodeElementLight *)ne)->Intensity = intensity;
-            ((X3DNodeNodeElementLight *)ne)->Location = location;
-            ((X3DNodeNodeElementLight *)ne)->Radius = radius;
-
-            // Assimp want a node with name similar to a light. "Why? I don't no." )
-            ParseHelper_Group_Begin(false);
-            // make random name
-            if (ne->ID.empty()) ne->ID = "SpotLight_" + to_string((size_t)ne);
-
-            mNodeElementCur->ID = ne->ID; // assign name to node and return to light element.
-            ParseHelper_Node_Exit();
-            // check for child nodes
-            if (!mReader->isEmptyElement())
-                ParseNode_Metadata(ne, "SpotLight");
-            else
-                mNodeElementCur->Child.push_back(ne); // add made object as child to current element
-
-            NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-        } // if(on)
-    } // if(!use.empty()) else
-}
-
-void X3DImporter::ParseNode_Grouping_Group() {
-    std::string def, use;
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        X3DNodeElementBase *ne = nullptr;
-
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
-    } else {
-        ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children.
-        // at this place new group mode created and made current, so we can name it.
-        if (!def.empty()) mNodeElementCur->ID = def;
-        // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
-
-        // for empty element exit from node in that place
-        if (mReader->isEmptyElement()) ParseHelper_Node_Exit();
-    } // if(!use.empty()) else
-}
-
-void X3DImporter::ParseNode_Grouping_GroupEnd() {
-    ParseHelper_Node_Exit(); // go up in scene graph
-}
-
-// <StaticGroup
-// DEF=""              ID
-// USE=""              IDREF
-// bboxCenter="0 0 0"  SFVec3f [initializeOnly]
-// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
-// >
-//    <!-- ChildContentModel -->
-// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
-// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
-// precise palette of legal nodes that are available depends on assigned profile and components.
-// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
-// </StaticGroup>
-// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or
-// contain any USE references outside the StaticGroup.
-void X3DImporter::ParseNode_Grouping_StaticGroup() {
-    std::string def, use;
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        X3DNodeElementBase *ne = nullptr;
-
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
-    } else {
-        ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children.
-        // at this place new group mode created and made current, so we can name it.
-        if (!def.empty()) mNodeElementCur->ID = def;
-        // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
-
-        // for empty element exit from node in that place
-        if (mReader->isEmptyElement()) ParseHelper_Node_Exit();
-    } // if(!use.empty()) else
-}
-
-void X3DImporter::ParseNode_Grouping_StaticGroupEnd() {
-    ParseHelper_Node_Exit(); // go up in scene graph
-}
-
-// <Switch
-// DEF=""              ID
-// USE=""              IDREF
-// bboxCenter="0 0 0"  SFVec3f [initializeOnly]
-// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
-// whichChoice="-1"    SFInt32 [inputOutput]
-// >
-//    <!-- ChildContentModel -->
-// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
-// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
-// precise palette of legal nodes that are available depends on assigned profile and components.
-// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
-// </Switch>
-// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child
-// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing
-// is chosen.
-void X3DImporter::ParseNode_Grouping_Switch() {
-    std::string def, use;
-    int32_t whichChoice = -1;
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("whichChoice", whichChoice, XML_ReadNode_GetAttrVal_AsI32);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        CX3DImporter_NodeElement *ne;
-
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
-    } else {
-        ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children.
-        // at this place new group mode created and made current, so we can name it.
-        if (!def.empty()) NodeElement_Cur->ID = def;
-
-        // also set values specific to this type of group
-        ((CX3DNodeElementGroup *)NodeElement_Cur)->UseChoice = true;
-        ((CX3DNodeElementGroup *)NodeElement_Cur)->Choice = whichChoice;
-        // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
-
-        // for empty element exit from node in that place
-        if (mReader->isEmptyElement()) ParseHelper_Node_Exit();
-    } // if(!use.empty()) else
-}
-
-void X3DImporter::ParseNode_Grouping_SwitchEnd() {
-    // just exit from node. Defined choice will be accepted at postprocessing stage.
-    ParseHelper_Node_Exit(); // go up in scene graph
-}
-
-// <Transform
-// DEF=""                     ID
-// USE=""                     IDREF
-// bboxCenter="0 0 0"         SFVec3f    [initializeOnly]
-// bboxSize="-1 -1 -1"        SFVec3f    [initializeOnly]
-// center="0 0 0"             SFVec3f    [inputOutput]
-// rotation="0 0 1 0"         SFRotation [inputOutput]
-// scale="1 1 1"              SFVec3f    [inputOutput]
-// scaleOrientation="0 0 1 0" SFRotation [inputOutput]
-// translation="0 0 0"        SFVec3f    [inputOutput]
-// >
-//    <!-- ChildContentModel -->
-// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
-// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
-// precise palette of legal nodes that are available depends on assigned profile and components.
-// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
-// </Transform>
-// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors.
-// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate
-// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the
-// equivalent transformation matrices,
-//   P' = T * C * R * SR * S * -SR * -C * P
-void X3DImporter::ParseNode_Grouping_Transform() {
-    aiVector3D center(0, 0, 0);
-    float rotation[4] = { 0, 0, 1, 0 };
-    aiVector3D scale(1, 1, 1); // A value of zero indicates that any child geometry shall not be displayed
-    float scale_orientation[4] = { 0, 0, 1, 0 };
-    aiVector3D translation(0, 0, 0);
-    aiMatrix4x4 matr, tmatr;
-    std::string use, def;
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_REF("center", center, XML_ReadNode_GetAttrVal_AsVec3f);
-    MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsVec3f);
-    MACRO_ATTRREAD_CHECK_REF("translation", translation, XML_ReadNode_GetAttrVal_AsVec3f);
-    if (an == "rotation") {
-        std::vector<float> tvec;
-
-        XML_ReadNode_GetAttrVal_AsArrF(idx, tvec);
-        if (tvec.size() != 4) throw DeadlyImportError("<Transform>: rotation vector must have 4 elements.");
-
-        memcpy(rotation, tvec.data(), sizeof(rotation));
-
-        continue;
-    }
-
-    if (an == "scaleOrientation") {
-        std::vector<float> tvec;
-        XML_ReadNode_GetAttrVal_AsArrF(idx, tvec);
-        if (tvec.size() != 4) {
-            throw DeadlyImportError("<Transform>: scaleOrientation vector must have 4 elements.");
+        if (currentName == "Shape")
+            readShape(currentNode);
+        else if (currentName == "Group") {
+            startReadGroup(currentNode);
+            readChildNodes(currentNode, "Group");
+            endReadGroup();
+        } else if (currentName == "StaticGroup") {
+            startReadStaticGroup(currentNode);
+            readChildNodes(currentNode, "StaticGroup");
+            endReadStaticGroup();
+        } else if (currentName == "Transform") {
+            startReadTransform(currentNode);
+            readChildNodes(currentNode, "Transform");
+            endReadTransform();
+        } else if (currentName == "Switch") {
+            startReadSwitch(currentNode);
+            readChildNodes(currentNode, "Switch");
+            endReadSwitch();
+        } else if (currentName == "DirectionalLight") {
+            readDirectionalLight(currentNode);
+        } else if (currentName == "PointLight") {
+            readPointLight(currentNode);
+        } else if (currentName == "SpotLight") {
+            readSpotLight(currentNode);
+        } else if (currentName == "Inline") {
+            readInline(currentNode);
+        } else if (!checkForMetadataNode(currentNode)) {
+            skipUnsupportedNode(pParentNodeName, currentNode);
         }
-
-        ::memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation));
-
-        continue;
     }
-
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        CX3DImporter_NodeElement *ne(nullptr);
-
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
-    } else {
-        ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children.
-        // at this place new group mode created and made current, so we can name it.
-        if (!def.empty()) {
-            NodeElement_Cur->ID = def;
-        }
-
-        //
-        // also set values specific to this type of group
-        //
-        // calculate transformation matrix
-        aiMatrix4x4::Translation(translation, matr); // T
-        aiMatrix4x4::Translation(center, tmatr); // C
-        matr *= tmatr;
-        aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr); // R
-        matr *= tmatr;
-        aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // SR
-        matr *= tmatr;
-        aiMatrix4x4::Scaling(scale, tmatr); // S
-        matr *= tmatr;
-        aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // -SR
-        matr *= tmatr;
-        aiMatrix4x4::Translation(-center, tmatr); // -C
-        matr *= tmatr;
-        // and assign it
-        ((CX3DNodeElementGroup *)mNodeElementCur)->Transformation = matr;
-        // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
-
-        // for empty element exit from node in that place
-        if (mReader->isEmptyElement()) {
-            ParseHelper_Node_Exit();
-        }
-    } // if(!use.empty()) else
-}
-
-void X3DImporter::ParseNode_Grouping_TransformEnd() {
-    ParseHelper_Node_Exit(); // go up in scene graph
-}
-
-void X3DImporter::ParseNode_Geometry2D_Arc2D() {
-    std::string def, use;
-    float endAngle = AI_MATH_HALF_PI_F;
-    float radius = 1;
-    float startAngle = 0;
-    X3DNodeElementBase *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Arc2D, ne);
-    } else {
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Arc2D, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        // create point list of geometry object and convert it to line set.
-        std::list<aiVector3D> tlist;
-
-        GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg
-        GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices);
-        ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2;
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "Arc2D");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
-}
-
-// <ArcClose2D
-// DEF=""              ID
-// USE=""              IDREF
-// closureType="PIE"   SFString [initializeOnly], {"PIE", "CHORD"}
-// endAngle="1.570796" SFFloat  [initializeOnly]
-// radius="1"          SFFloat  [initializeOnly]
-// solid="false"       SFBool   [initializeOnly]
-// startAngle="0"      SFFloat  [initializeOnly]
-// />
-// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping
-// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius
-// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater
-// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has
-// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between
-// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center.
-// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then
-// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point
-// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when
-// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D.
-void X3DImporter::ParseNode_Geometry2D_ArcClose2D() {
-    std::string def, use;
-    std::string closureType("PIE");
-    float endAngle = AI_MATH_HALF_PI_F;
-    float radius = 1;
-    bool solid = false;
-    float startAngle = 0;
-    X3DNodeElementBase *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("closureType", closureType, mReader->getAttributeValue);
-    MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_ArcClose2D, ne);
-    } else {
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_ArcClose2D, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid;
-        // create point list of geometry object.
-        GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); ///TODO: IME - AI_CONFIG for NumSeg
-        // add chord or two radiuses only if not a circle was defined
-        if (!((std::fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle))) {
-            std::list<aiVector3D> &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias.
-
-            if ((closureType == "PIE") || (closureType == "\"PIE\""))
-                vlist.push_back(aiVector3D(0, 0, 0)); // center point - first radial line
-            else if ((closureType != "CHORD") && (closureType != "\"CHORD\""))
-                Throw_IncorrectAttrValue("closureType");
-
-            vlist.push_back(*vlist.begin()); // arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE).
-        }
-
-        ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.size();
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "ArcClose2D");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
-}
-
-// <Circle2D
-// DEF=""     ID
-// USE=""     IDREF
-// radius="1" SFFloat  [initializeOnly]
-// />
-void X3DImporter::ParseNode_Geometry2D_Circle2D() {
-    std::string def, use;
-    float radius = 1;
-    X3DNodeElementBase *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Circle2D, ne);
-    } else {
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Circle2D, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        // create point list of geometry object and convert it to line set.
-        std::list<aiVector3D> tlist;
-
-        GeometryHelper_Make_Arc2D(0, 0, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg
-        GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices);
-        ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2;
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "Circle2D");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
 }
 
-// <Disk2D
-// DEF=""          ID
-// USE=""          IDREF
-// innerRadius="0" SFFloat  [initializeOnly]
-// outerRadius="1" SFFloat  [initializeOnly]
-// solid="false"   SFBool   [initializeOnly]
-// />
-// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the
-// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero.
-// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely
-// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall
-// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of
-// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D.
-void X3DImporter::ParseNode_Geometry2D_Disk2D() {
-    std::string def, use;
-    float innerRadius = 0;
-    float outerRadius = 1;
-    bool solid = false;
-    X3DNodeElementBase *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("innerRadius", innerRadius, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_RET("outerRadius", outerRadius, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Disk2D, ne);
-    } else {
-        std::list<aiVector3D> tlist_o, tlist_i;
-
-        if (innerRadius > outerRadius) Throw_IncorrectAttrValue("innerRadius");
-
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Disk2D, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        // create point list of geometry object.
-        ///TODO: IME - AI_CONFIG for NumSeg
-        GeometryHelper_Make_Arc2D(0, 0, outerRadius, 10, tlist_o); // outer circle
-        if (innerRadius == 0.0f) { // make filled disk
-            // in tlist_o we already have points of circle. just copy it and sign as polygon.
-            ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices = tlist_o;
-            ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = tlist_o.size();
-        } else if (innerRadius == outerRadius) { // make circle
-            // in tlist_o we already have points of circle. convert it to line set.
-            GeometryHelper_Extend_PointToLine(tlist_o, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices);
-            ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2;
-        } else { // make disk
-            std::list<aiVector3D> &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias.
-
-            GeometryHelper_Make_Arc2D(0, 0, innerRadius, 10, tlist_i); // inner circle
-            //
-            // create quad list from two point lists
-            //
-            if (tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list."); // tlist_i and tlist_o has equal size.
-
-            // add all quads except last
-            for (std::list<aiVector3D>::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();) {
-                // do not forget - CCW direction
-                vlist.push_back(*it_i++); // 1st point
-                vlist.push_back(*it_o++); // 2nd point
-                vlist.push_back(*it_o); // 3rd point
-                vlist.push_back(*it_i); // 4th point
-            }
-
-            // add last quad
-            vlist.push_back(*tlist_i.end()); // 1st point
-            vlist.push_back(*tlist_o.end()); // 2nd point
-            vlist.push_back(*tlist_o.begin()); // 3rd point
-            vlist.push_back(*tlist_o.begin()); // 4th point
-
-            ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4;
-        }
-
-        ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid;
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "Disk2D");
-        else
-            mNodeElementCur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
+void X3DImporter::readScene(XmlNode &node) {
+    ParseHelper_Group_Begin(true);
+    readChildNodes(node, "Scene");
+    ParseHelper_Node_Exit();
 }
 
-// <Polyline2D
-// DEF=""          ID
-// USE=""          IDREF
-// lineSegments="" MFVec2F [intializeOnly]
-// />
-void X3DImporter::ParseNode_Geometry2D_Polyline2D() {
-    std::string def, use;
-    std::list<aiVector2D> lineSegments;
-    X3DNodeElementBase *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_REF("lineSegments", lineSegments, XML_ReadNode_GetAttrVal_AsListVec2f);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polyline2D, ne);
-    } else {
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polyline2D, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        //
-        // convert read point list of geometry object to line set.
-        //
-        std::list<aiVector3D> tlist;
-
-        // convert vec2 to vec3
-        for (std::list<aiVector2D>::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2)
-            tlist.push_back(aiVector3D(it2->x, it2->y, 0));
-
-        // convert point set to line set
-        GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices);
-        ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2;
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "Polyline2D");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
-}
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: find set ************************************************************/
+/*********************************************************************************************************************************************/
 
-// <Polypoint2D
-// DEF=""   ID
-// USE=""   IDREF
-// point="" MFVec2F [inputOutput]
-// />
-void X3DImporter::ParseNode_Geometry2D_Polypoint2D() {
-    std::string def, use;
-    std::list<aiVector2D> point;
-    CX3DImporter_NodeElement *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec2f);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polypoint2D, ne);
-    } else {
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polypoint2D, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
+bool X3DImporter::FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) {
+    for (std::list<X3DNodeElementBase *>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it) {
+        if (((*it)->Type == pType) && ((*it)->ID == pID)) {
+            if (pElement != nullptr) *pElement = *it;
 
-        // convert vec2 to vec3
-        for (std::list<aiVector2D>::iterator it2 = point.begin(); it2 != point.end(); ++it2) {
-            ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0));
+            return true;
         }
+    } // for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++)
 
-        ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 1;
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "Polypoint2D");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
-}
-
-// <Rectangle2D
-// DEF=""        ID
-// USE=""        IDREF
-// size="2 2"    SFVec2f [initializeOnly]
-// solid="false" SFBool  [initializeOnly]
-// />
-void X3DImporter::ParseNode_Geometry2D_Rectangle2D() {
-    std::string def, use;
-    aiVector2D size(2, 2);
-    bool solid = false;
-    CX3DImporter_NodeElement *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec2f);
-    MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Rectangle2D, ne);
-    } else {
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Rectangle2D, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        float x1 = -size.x / 2.0f;
-        float x2 = size.x / 2.0f;
-        float y1 = -size.y / 2.0f;
-        float y2 = size.y / 2.0f;
-        std::list<aiVector3D> &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias.
-
-        vlist.push_back(aiVector3D(x2, y1, 0)); // 1st point
-        vlist.push_back(aiVector3D(x2, y2, 0)); // 2nd point
-        vlist.push_back(aiVector3D(x1, y2, 0)); // 3rd point
-        vlist.push_back(aiVector3D(x1, y1, 0)); // 4th point
-        ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid;
-        ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4;
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "Rectangle2D");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
+    return false;
 }
 
-// <TriangleSet2D
-// DEF=""        ID
-// USE=""        IDREF
-// solid="false" SFBool  [initializeOnly]
-// vertices=""   MFVec2F [inputOutput]
-// />
-void X3DImporter::ParseNode_Geometry2D_TriangleSet2D() {
-    std::string def, use;
-    bool solid = false;
-    std::list<aiVector2D> vertices;
-    CX3DImporter_NodeElement *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_REF("vertices", vertices, XML_ReadNode_GetAttrVal_AsListVec2f);
-    MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleSet2D, ne);
-    } else {
-        if (vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle.");
-
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_TriangleSet2D, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
+bool X3DImporter::FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID,
+        const X3DElemType pType, X3DNodeElementBase **pElement) {
+    bool found = false; // flag: true - if requested element is found.
 
-        // convert vec2 to vec3
-        for (std::list<aiVector2D>::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2) {
-            ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0));
+    // Check if pStartNode - this is the element, we are looking for.
+    if ((pStartNode->Type == pType) && (pStartNode->ID == pID)) {
+        found = true;
+        if (pElement != nullptr) {
+            *pElement = pStartNode;
         }
 
-        ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid;
-        ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 3;
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "TriangleSet2D");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
-}
-
-// <Box
-// DEF=""       ID
-// USE=""       IDREF
-// size="2 2 2" SFVec3f [initializeOnly]
-// solid="true" SFBool  [initializeOnly]
-// />
-// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes.
-// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes
-// respectively and each component value shall be greater than zero.
-void X3DImporter::ParseNode_Geometry3D_Box() {
-    std::string def, use;
-    bool solid = true;
-    aiVector3D size(2, 2, 2);
-    CX3DImporter_NodeElement *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec3f);
-    MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Box, ne);
-    } else {
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Box, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        GeometryHelper_MakeQL_RectParallelepiped(size, ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices); // get quad list
-        ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid;
-        ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 4;
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "Box");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
-}
+        goto fne_fn_end;
+    } // if((pStartNode->Type() == pType) && (pStartNode->ID() == pID))
 
-// <Cone
-// DEF=""           ID
-// USE=""           IDREF
-// bottom="true"    SFBool [initializeOnly]
-// bottomRadius="1" SFloat [initializeOnly]
-// height="2"       SFloat [initializeOnly]
-// side="true"      SFBool [initializeOnly]
-// solid="true"     SFBool [initializeOnly]
-// />
-void X3DImporter::ParseNode_Geometry3D_Cone() {
-    std::string use, def;
-    bool bottom = true;
-    float bottomRadius = 1;
-    float height = 2;
-    bool side = true;
-    bool solid = true;
-    CX3DImporter_NodeElement *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_RET("bottomRadius", bottomRadius, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cone, ne);
-    } else {
-        const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property
-
-        std::vector<aiVector3D> tvec; // temp array for vertices.
-
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cone, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        // make cone or parts according to flags.
-        if (side) {
-            StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom);
-        } else if (bottom) {
-            StandardShapes::MakeCircle(bottomRadius, tess, tvec);
-            height = -(height / 2);
-            for (std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); ++it)
-                it->y = height; // y - because circle made in oXZ.
+    // Check childs of pStartNode.
+    for (std::list<X3DNodeElementBase *>::iterator ch_it = pStartNode->Children.begin(); ch_it != pStartNode->Children.end(); ++ch_it) {
+        found = FindNodeElement_FromNode(*ch_it, pID, pType, pElement);
+        if (found) {
+            break;
         }
+    } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = it->Children.begin(); ch_it != it->Children.end(); ch_it++)
 
-        // copy data from temp array
-        for (std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); ++it)
-            ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it);
-
-        ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid;
-        ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3;
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "Cone");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
+fne_fn_end:
 
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
+    return found;
 }
 
-// <Cylinder
-// DEF=""        ID
-// USE=""        IDREF
-// bottom="true" SFBool [initializeOnly]
-// height="2"    SFloat [initializeOnly]
-// radius="1"    SFloat [initializeOnly]
-// side="true"   SFBool [initializeOnly]
-// solid="true"  SFBool [initializeOnly]
-// top="true"    SFBool [initializeOnly]
-// />
-void X3DImporter::ParseNode_Geometry3D_Cylinder() {
-    std::string use, def;
-    bool bottom = true;
-    float height = 2;
-    float radius = 1;
-    bool side = true;
-    bool solid = true;
-    bool top = true;
-    CX3DImporter_NodeElement *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("top", top, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cylinder, ne);
-    } else {
-        const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property
-
-        std::vector<aiVector3D> tside; // temp array for vertices of side.
-        std::vector<aiVector3D> tcir; // temp array for vertices of circle.
-
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cylinder, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        // make cilynder or parts according to flags.
-        if (side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true);
-
-        height /= 2; // height defined for whole cylinder, when creating top and bottom circle we are using just half of height.
-        if (top || bottom) StandardShapes::MakeCircle(radius, tess, tcir);
-        // copy data from temp arrays
-        std::list<aiVector3D> &vlist = ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices; // just short alias.
-
-        for (std::vector<aiVector3D>::iterator it = tside.begin(); it != tside.end(); ++it)
-            vlist.push_back(*it);
-
-        if (top) {
-            for (std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); ++it) {
-                (*it).y = height; // y - because circle made in oXZ.
-                vlist.push_back(*it);
-            }
-        } // if(top)
-
-        if (bottom) {
-            for (std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); ++it) {
-                (*it).y = -height; // y - because circle made in oXZ.
-                vlist.push_back(*it);
-            }
-        } // if(top)
-
-        ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid;
-        ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3;
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "Cylinder");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
-}
-
-// <ElevationGrid
-// DEF=""                 ID
-// USE=""                 IDREF
-// ccw="true"             SFBool  [initializeOnly]
-// colorPerVertex="true"  SFBool  [initializeOnly]
-// creaseAngle="0"        SFloat  [initializeOnly]
-// height=""              MFloat  [initializeOnly]
-// normalPerVertex="true" SFBool  [initializeOnly]
-// solid="true"           SFBool  [initializeOnly]
-// xDimension="0"         SFInt32 [initializeOnly]
-// xSpacing="1.0"         SFloat  [initializeOnly]
-// zDimension="0"         SFInt32 [initializeOnly]
-// zSpacing="1.0"         SFloat  [initializeOnly]
-// >
-//   <!-- ColorNormalTexCoordContentModel -->
-// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single
-// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
-// </ElevationGrid>
-// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described
-// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate
-// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero.
-// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals.
-void X3DImporter::ParseNode_Geometry3D_ElevationGrid() {
-    std::string use, def;
-    bool ccw = true;
-    bool colorPerVertex = true;
-    float creaseAngle = 0;
-    std::vector<float> height;
-    bool normalPerVertex = true;
-    bool solid = true;
-    int32_t xDimension = 0;
-    float xSpacing = 1;
-    int32_t zDimension = 0;
-    float zSpacing = 1;
-    CX3DImporter_NodeElement *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_REF("height", height, XML_ReadNode_GetAttrVal_AsArrF);
-    MACRO_ATTRREAD_CHECK_RET("xDimension", xDimension, XML_ReadNode_GetAttrVal_AsI32);
-    MACRO_ATTRREAD_CHECK_RET("xSpacing", xSpacing, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_RET("zDimension", zDimension, XML_ReadNode_GetAttrVal_AsI32);
-    MACRO_ATTRREAD_CHECK_RET("zSpacing", zSpacing, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_ElevationGrid, ne);
-    } else {
-        if ((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in <ElevationGrid> must be grater than zero.");
-        if ((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in <ElevationGrid> must be grater than zero.");
-        if ((size_t)(xDimension * zDimension) != height.size()) Throw_IncorrectAttrValue("Heights count must be equal to \"xDimension * zDimension\"");
-
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_ElevationGrid(CX3DImporter_NodeElement::ENET_ElevationGrid, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        CX3DImporter_NodeElement_ElevationGrid &grid_alias = *((CX3DImporter_NodeElement_ElevationGrid *)ne); // create alias for conveience
-
-        { // create grid vertices list
-            std::vector<float>::const_iterator he_it = height.begin();
-
-            for (int32_t zi = 0; zi < zDimension; zi++) // rows
-            {
-                for (int32_t xi = 0; xi < xDimension; xi++) // columns
-                {
-                    aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi);
-
-                    grid_alias.Vertices.push_back(tvec);
-                    ++he_it;
-                }
-            }
-        } // END: create grid vertices list
-        //
-        // create faces list. In "coordIdx" format
-        //
-        // check if we have quads
-        if ((xDimension < 2) || (zDimension < 2)) // only one element in dimension is set, create line set.
-        {
-            ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 2; // will be holded as line set.
-            for (size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++) {
-                grid_alias.CoordIdx.push_back(static_cast<int32_t>(i));
-                grid_alias.CoordIdx.push_back(static_cast<int32_t>(i + 1));
-                grid_alias.CoordIdx.push_back(-1);
-            }
-        } else // two or more elements in every dimension is set. create quad set.
-        {
-            ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 4;
-            for (int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) // rows
-            {
-                for (int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) // columns
-                {
-                    // points direction in face.
-                    if (ccw) {
-                        // CCW:
-                        //	3 2
-                        //	0 1
-                        grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi);
-                        grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1));
-                        grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1));
-                        grid_alias.CoordIdx.push_back(fzi * xDimension + fxi);
-                    } else {
-                        // CW:
-                        //	0 1
-                        //	3 2
-                        grid_alias.CoordIdx.push_back(fzi * xDimension + fxi);
-                        grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1));
-                        grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1));
-                        grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi);
-                    } // if(ccw) else
-
-                    grid_alias.CoordIdx.push_back(-1);
-                } // for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)
-            } // for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)
-        } // if((xDimension < 2) || (zDimension < 2)) else
-
-        grid_alias.ColorPerVertex = colorPerVertex;
-        grid_alias.NormalPerVertex = normalPerVertex;
-        grid_alias.CreaseAngle = creaseAngle;
-        grid_alias.Solid = solid;
-        // check for child nodes
-        if (!mReader->isEmptyElement()) {
-            ParseHelper_Node_Enter(ne);
-            MACRO_NODECHECK_LOOPBEGIN("ElevationGrid");
-            // check for X3DComposedGeometryNodes
-            if (XML_CheckNode_NameEqual("Color")) {
-                ParseNode_Rendering_Color();
-                continue;
-            }
-            if (XML_CheckNode_NameEqual("ColorRGBA")) {
-                ParseNode_Rendering_ColorRGBA();
-                continue;
-            }
-            if (XML_CheckNode_NameEqual("Normal")) {
-                ParseNode_Rendering_Normal();
-                continue;
-            }
-            if (XML_CheckNode_NameEqual("TextureCoordinate")) {
-                ParseNode_Texturing_TextureCoordinate();
-                continue;
-            }
-            // check for X3DMetadataObject
-            if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("ElevationGrid");
-
-            MACRO_NODECHECK_LOOPEND("ElevationGrid");
-            ParseHelper_Node_Exit();
-        } // if(!mReader->isEmptyElement())
-        else {
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-        } // if(!mReader->isEmptyElement()) else
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
-}
-
-template <typename TVector>
-static void GeometryHelper_Extrusion_CurveIsClosed(std::vector<TVector> &pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool &pCurveIsClosed) {
-    size_t cur_sz = pCurve.size();
-
-    pCurveIsClosed = false;
-    // for curve with less than four points checking is have no sense,
-    if (cur_sz < 4) return;
-
-    for (size_t s = 3, s_e = cur_sz; s < s_e; s++) {
-        // search for first point of duplicated part.
-        if (pCurve[0] == pCurve[s]) {
-            bool found = true;
-
-            // check if tail(indexed by b2) is duplicate of head(indexed by b1).
-            for (size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) {
-                if (pCurve[b1] != pCurve[b2]) { // points not match: clear flag and break loop.
-                    found = false;
-
-                    break;
-                }
-            } // for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++)
-
-            // if duplicate tail is found then drop or not it depending on flags.
-            if (found) {
-                pCurveIsClosed = true;
-                if (pDropTail) {
-                    if (!pRemoveLastPoint) s++; // prepare value for iterator's arithmetics.
-
-                    pCurve.erase(pCurve.begin() + s, pCurve.end()); // remove tail
-                }
+bool X3DImporter::FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) {
+    X3DNodeElementBase *tnd = mNodeElementCur; // temporary pointer to node.
+    bool static_search = false; // flag: true if searching in static node.
 
+    // At first check if we have deal with static node. Go up through parent nodes and check flag.
+    while (tnd != nullptr) {
+        if (tnd->Type == X3DElemType::ENET_Group) {
+            if (((X3DNodeElementGroup *)tnd)->Static) {
+                static_search = true; // Flag found, stop walking up. Node with static flag will holded in tnd variable.
                 break;
-            } // if(found)
-        } // if(pCurve[0] == pCurve[s])
-    } // for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++)
-}
-
-static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector<aiVector3D> &pSpine, const bool pSpine_Closed) {
-    const size_t spine_idx_last = pSpine.size() - 1;
-    aiVector3D tvec;
-
-    if ((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) // at first special cases
-    {
-        if (pSpine_Closed) { // If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis.
-            // As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0])
-            // in tail are removed.
-            // So, last point in pSpine is a spine[n - 2]
-            tvec = pSpine[1] - pSpine[spine_idx_last];
-        } else if (pSpine_PointIdx == 0) { // The Y-axis used for the first point is the vector from spine[0] to spine[1]
-            tvec = pSpine[1] - pSpine[0];
-        } else { // The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is
-            // the spine[0].
-            tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1];
-        }
-    } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))
-    else { // For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]).
-        tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1];
-    } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else
-
-    return tvec.Normalize();
-}
-
-static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector<aiVector3D> &pSpine, const bool pSpine_Closed,
-        const aiVector3D pVecZ_Prev) {
-    const aiVector3D zero_vec(0);
-    const size_t spine_idx_last = pSpine.size() - 1;
-
-    aiVector3D tvec;
-
-    // at first special cases
-    if (pSpine.size() < 3) // spine have not enough points for vector calculations.
-    {
-        tvec.Set(0, 0, 1);
-    } else if (pSpine_PointIdx == 0) // special case: first point
-    {
-        if (pSpine_Closed) // for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate.
-        {
-            tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]);
-        } else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z.
-        {
-            bool found = false;
-
-            // As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear)
-            // then the Z-axis for the first spine point with a defined Z-axis is used."
-            // Walk through spine and find Z.
-            for (size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++) {
-                // (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1])
-                tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]);
-                found = !tvec.Equal(zero_vec);
-            }
-
-            // if entire spine are collinear then use OZ axis.
-            if (!found) tvec.Set(0, 0, 1);
-        } // if(pSpine_Closed) else
-    } // else if(pSpine_PointIdx == 0)
-    else if (pSpine_PointIdx == spine_idx_last) // special case: last point
-    {
-        if (pSpine_Closed) { // do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2].
-            tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]);
-            // if taken spine vectors are collinear then use previous vector Z.
-            if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev;
-        } else { // vector Z for last point of not closed curve is previous vector Z.
-            tvec = pVecZ_Prev;
-        }
-    } else // regular point
-    {
-        tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]);
-        // if taken spine vectors are collinear then use previous vector Z.
-        if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev;
-    }
-
-    // After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis
-    // is flipped (multiplied by -1).
-    if ((tvec * pVecZ_Prev) < 0) tvec = -tvec;
-
-    return tvec.Normalize();
-}
-
-// <Extrusion
-// DEF=""                                 ID
-// USE=""                                 IDREF
-// beginCap="true"                        SFBool     [initializeOnly]
-// ccw="true"                             SFBool     [initializeOnly]
-// convex="true"                          SFBool     [initializeOnly]
-// creaseAngle="0.0"                      SFloat     [initializeOnly]
-// crossSection="1 1 1 -1 -1 -1 -1 1 1 1" MFVec2f    [initializeOnly]
-// endCap="true"                          SFBool     [initializeOnly]
-// orientation="0 0 1 0"                  MFRotation [initializeOnly]
-// scale="1 1"                            MFVec2f    [initializeOnly]
-// solid="true"                           SFBool     [initializeOnly]
-// spine="0 0 0 0 1 0"                    MFVec3f    [initializeOnly]
-// />
-void X3DImporter::ParseNode_Geometry3D_Extrusion() {
-    std::string use, def;
-    bool beginCap = true;
-    bool ccw = true;
-    bool convex = true;
-    float creaseAngle = 0;
-    std::vector<aiVector2D> crossSection;
-    bool endCap = true;
-    std::vector<float> orientation;
-    std::vector<aiVector2D> scale;
-    bool solid = true;
-    std::vector<aiVector3D> spine;
-    CX3DImporter_NodeElement *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("beginCap", beginCap, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_REF("crossSection", crossSection, XML_ReadNode_GetAttrVal_AsArrVec2f);
-    MACRO_ATTRREAD_CHECK_RET("endCap", endCap, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_REF("orientation", orientation, XML_ReadNode_GetAttrVal_AsArrF);
-    MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsArrVec2f);
-    MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_REF("spine", spine, XML_ReadNode_GetAttrVal_AsArrVec3f);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Extrusion, ne);
-    } else {
-        //
-        // check if default values must be assigned
-        //
-        if (spine.size() == 0) {
-            spine.resize(2);
-            spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0);
-        } else if (spine.size() == 1) {
-            throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points.");
-        }
-
-        if (crossSection.size() == 0) {
-            crossSection.resize(5);
-            crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1);
-        }
-
-        { // orientation
-            size_t ori_size = orientation.size() / 4;
-
-            if (ori_size < spine.size()) {
-                float add_ori[4]; // values that will be added
-
-                if (ori_size == 1) // if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points.
-                {
-                    add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3];
-                } else // else - use default values
-                {
-                    add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0;
-                }
-
-                orientation.reserve(spine.size() * 4);
-                for (size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++)
-                    orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]);
             }
-
-            if (orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in <Extrusion> must has multiple four quantity of numbers.");
-        } // END: orientation
-
-        { // scale
-            if (scale.size() < spine.size()) {
-                aiVector2D add_sc;
-
-                if (scale.size() == 1) // if "scale" has one element then use it value for all spine points.
-                    add_sc = scale[0];
-                else // else - use default values
-                    add_sc.Set(1, 1);
-
-                scale.reserve(spine.size());
-                for (size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++)
-                    scale.push_back(add_sc);
-            }
-        } // END: scale
-        //
-        // create and if needed - define new geometry object.
-        //
-        ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_Extrusion, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        CX3DImporter_NodeElement_IndexedSet &ext_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne); // create alias for conveience
-        // assign part of input data
-        ext_alias.CCW = ccw;
-        ext_alias.Convex = convex;
-        ext_alias.CreaseAngle = creaseAngle;
-        ext_alias.Solid = solid;
-
-        //
-        // How we done it at all?
-        // 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector
-        // are applied vor every basis.
-        // 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position
-        // using relative spine point.
-        // 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if
-        // needed. While createing CootdIdx is taking in account CCW flag.
-        // 4. The last step: create Vertices list.
-        //
-        bool spine_closed; // flag: true if spine curve is closed.
-        bool cross_closed; // flag: true if cross curve is closed.
-        std::vector<aiMatrix3x3> basis_arr; // array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z.
-        std::vector<std::vector<aiVector3D>> pointset_arr; // array of point sets: cross curves.
-
-        // detect closed curves
-        GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed); // true - drop tail, true - remove duplicate end.
-        GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed); // true - drop tail, true - remove duplicate end.
-        // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface.
-        if (spine_closed) {
-            beginCap |= endCap;
-            endCap = false;
         }
 
-        { // 1. Calculate array of basises.
-            aiMatrix4x4 rotmat;
-            aiVector3D vecX(0), vecY(0), vecZ(0);
-
-            basis_arr.resize(spine.size());
-            for (size_t i = 0, i_e = spine.size(); i < i_e; i++) {
-                aiVector3D tvec;
-
-                // get axises of basis.
-                vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed);
-                vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ);
-                vecX = (vecY ^ vecZ).Normalize();
-                // get rotation matrix and apply "orientation" to basis
-                aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat);
-                tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z;
-                tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z;
-                tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z;
-            } // for(size_t i = 0, i_e = spine.size(); i < i_e; i++)
-        } // END: 1. Calculate array of basises
-
-        { // 2. Create array of point sets.
-            aiMatrix4x4 scmat;
-            std::vector<aiVector3D> tcross(crossSection.size());
-
-            pointset_arr.resize(spine.size());
-            for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) {
-                aiVector3D tc23vec;
-
-                tc23vec.Set(scale[spi].x, 0, scale[spi].y);
-                aiMatrix4x4::Scaling(tc23vec, scmat);
-                for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) {
-                    aiVector3D tvecX, tvecY, tvecZ;
-
-                    tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y);
-                    // apply scaling to point
-                    tcross[cri] = scmat * tc23vec;
-                    //
-                    // transfer point to new basis
-                    // calculate coordinate in new basis
-                    tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x;
-                    tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y;
-                    tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z;
-                    // apply new coordinates and translate it to spine point.
-                    tcross[cri] = tvecX + tvecY + tvecZ + spine[spi];
-                } // for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++)
-
-                pointset_arr[spi] = tcross; // store transferred point set
-            } // for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++)
-        } // END: 2. Create array of point sets.
-
-        { // 3. Create CoordIdx.
-            // add caps if needed
-            if (beginCap) {
-                // add cap as polygon. vertices of cap are places at begin, so just add numbers from zero.
-                for (size_t i = 0, i_e = crossSection.size(); i < i_e; i++)
-                    ext_alias.CoordIndex.push_back(static_cast<int32_t>(i));
-
-                // add delimiter
-                ext_alias.CoordIndex.push_back(-1);
-            } // if(beginCap)
-
-            if (endCap) {
-                // add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset.
-                size_t beg = (pointset_arr.size() - 1) * crossSection.size();
-
-                for (size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++)
-                    ext_alias.CoordIndex.push_back(static_cast<int32_t>(i));
-
-                // add delimiter
-                ext_alias.CoordIndex.push_back(-1);
-            } // if(beginCap)
-
-            // add quads
-            for (size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++) {
-                const size_t cr_sz = crossSection.size();
-                const size_t cr_last = crossSection.size() - 1;
-
-                size_t right_col; // hold index basis for points of quad placed in right column;
-
-                if (spi != spi_e)
-                    right_col = spi + 1;
-                else if (spine_closed) // if spine curve is closed then one more quad is needed: between first and last points of curve.
-                    right_col = 0;
-                else
-                    break; // if spine curve is not closed then break the loop, because spi is out of range for that type of spine.
-
-                for (size_t cri = 0; cri < cr_sz; cri++) {
-                    if (cri != cr_last) {
-                        MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex,
-                                static_cast<int32_t>(spi * cr_sz + cri),
-                                static_cast<int32_t>(right_col * cr_sz + cri),
-                                static_cast<int32_t>(right_col * cr_sz + cri + 1),
-                                static_cast<int32_t>(spi * cr_sz + cri + 1));
-                        // add delimiter
-                        ext_alias.CoordIndex.push_back(-1);
-                    } else if (cross_closed) // if cross curve is closed then one more quad is needed: between first and last points of curve.
-                    {
-                        MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex,
-                                static_cast<int32_t>(spi * cr_sz + cri),
-                                static_cast<int32_t>(right_col * cr_sz + cri),
-                                static_cast<int32_t>(right_col * cr_sz + 0),
-                                static_cast<int32_t>(spi * cr_sz + 0));
-                        // add delimiter
-                        ext_alias.CoordIndex.push_back(-1);
-                    }
-                } // for(size_t cri = 0; cri < cr_sz; cri++)
-            } // for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++)
-        } // END: 3. Create CoordIdx.
-
-        { // 4. Create vertices list.
-            // just copy all vertices
-            for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) {
-                for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) {
-                    ext_alias.Vertices.push_back(pointset_arr[spi][cri]);
-                }
-            }
-        } // END: 4. Create vertices list.
-        //PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex);
-        //PrintVectorSet("Ext. Vertices", ext_alias.Vertices);
-        // check for child nodes
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "Extrusion");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
-}
+        tnd = tnd->Parent; // go up in graph.
+    } // while (tnd != nullptr)
 
-// <IndexedFaceSet
-// DEF=""                         ID
-// USE=""                         IDREF
-// ccw="true"             SFBool  [initializeOnly]
-// colorIndex=""          MFInt32 [initializeOnly]
-// colorPerVertex="true"  SFBool  [initializeOnly]
-// convex="true"          SFBool  [initializeOnly]
-// coordIndex=""          MFInt32 [initializeOnly]
-// creaseAngle="0"        SFFloat [initializeOnly]
-// normalIndex=""         MFInt32 [initializeOnly]
-// normalPerVertex="true" SFBool  [initializeOnly]
-// solid="true"           SFBool  [initializeOnly]
-// texCoordIndex=""       MFInt32 [initializeOnly]
-// >
-//    <!-- ComposedGeometryContentModel -->
-// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
-// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
-// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
-// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
-// </IndexedFaceSet>
-void X3DImporter::ParseNode_Geometry3D_IndexedFaceSet() {
-    std::string use, def;
-    bool ccw = true;
-    std::vector<int32_t> colorIndex;
-    bool colorPerVertex = true;
-    bool convex = true;
-    std::vector<int32_t> coordIndex;
-    float creaseAngle = 0;
-    std::vector<int32_t> normalIndex;
-    bool normalPerVertex = true;
-    bool solid = true;
-    std::vector<int32_t> texCoordIndex;
-    CX3DImporter_NodeElement *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsArrI32);
-    MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsArrI32);
-    MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_REF("normalIndex", normalIndex, XML_ReadNode_GetAttrVal_AsArrI32);
-    MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_CHECK_REF("texCoordIndex", texCoordIndex, XML_ReadNode_GetAttrVal_AsArrI32);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedFaceSet, ne);
+    // at now call appropriate search function.
+    if (static_search) {
+        return FindNodeElement_FromNode(tnd, pID, pType, pElement);
     } else {
-        // check data
-        if (coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute.");
-
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedFaceSet, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        CX3DImporter_NodeElement_IndexedSet &ne_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne);
-
-        ne_alias.CCW = ccw;
-        ne_alias.ColorIndex = colorIndex;
-        ne_alias.ColorPerVertex = colorPerVertex;
-        ne_alias.Convex = convex;
-        ne_alias.CoordIndex = coordIndex;
-        ne_alias.CreaseAngle = creaseAngle;
-        ne_alias.NormalIndex = normalIndex;
-        ne_alias.NormalPerVertex = normalPerVertex;
-        ne_alias.Solid = solid;
-        ne_alias.TexCoordIndex = texCoordIndex;
-        // check for child nodes
-        if (!mReader->isEmptyElement()) {
-            ParseHelper_Node_Enter(ne);
-            MACRO_NODECHECK_LOOPBEGIN("IndexedFaceSet");
-            // check for X3DComposedGeometryNodes
-            if (XML_CheckNode_NameEqual("Color")) {
-                ParseNode_Rendering_Color();
-                continue;
-            }
-            if (XML_CheckNode_NameEqual("ColorRGBA")) {
-                ParseNode_Rendering_ColorRGBA();
-                continue;
-            }
-            if (XML_CheckNode_NameEqual("Coordinate")) {
-                ParseNode_Rendering_Coordinate();
-                continue;
-            }
-            if (XML_CheckNode_NameEqual("Normal")) {
-                ParseNode_Rendering_Normal();
-                continue;
-            }
-            if (XML_CheckNode_NameEqual("TextureCoordinate")) {
-                ParseNode_Texturing_TextureCoordinate();
-                continue;
-            }
-            // check for X3DMetadataObject
-            if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedFaceSet");
-
-            MACRO_NODECHECK_LOOPEND("IndexedFaceSet");
-            ParseHelper_Node_Exit();
-        } // if(!mReader->isEmptyElement())
-        else {
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-        }
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
-}
-
-// <Sphere
-// DEF=""       ID
-// USE=""       IDREF
-// radius="1"   SFloat [initializeOnly]
-// solid="true" SFBool [initializeOnly]
-// />
-void X3DImporter::ParseNode_Geometry3D_Sphere() {
-    std::string use, def;
-    ai_real radius = 1;
-    bool solid = true;
-    CX3DImporter_NodeElement *ne(nullptr);
-
-    MACRO_ATTRREAD_LOOPBEG;
-    MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-    MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat);
-    MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-    MACRO_ATTRREAD_LOOPEND;
-
-    // if "USE" defined then find already defined element.
-    if (!use.empty()) {
-        MACRO_USE_CHECKANDAPPLY(def, use, ENET_Sphere, ne);
-    } else {
-        const unsigned int tess = 3; ///TODO: IME tessellation factor through ai_property
-
-        std::vector<aiVector3D> tlist;
-
-        // create and if needed - define new geometry object.
-        ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Sphere, NodeElement_Cur);
-        if (!def.empty()) ne->ID = def;
-
-        StandardShapes::MakeSphere(tess, tlist);
-        // copy data from temp array and apply scale
-        for (std::vector<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); ++it) {
-            ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it * radius);
-        }
-
-        ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid;
-        ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3;
-        // check for X3DMetadataObject childs.
-        if (!mReader->isEmptyElement())
-            ParseNode_Metadata(ne, "Sphere");
-        else
-            NodeElement_Cur->Child.push_back(ne); // add made object as child to current element
-
-        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
-    } // if(!use.empty()) else
-}
-
-
-
-void X3DImporter::readMetadataObject(XmlNode &node) {
-    const std::string &name = node.name();
-    if (name == "MetadataBoolean") {
-        readMetadataBoolean(node, mNodeElementCur);
-    } else if (name == "MetadataDouble") {
-        readMetadataDouble(node, mNodeElementCur);
-    } else if (name == "MetadataFloat") {
-        readMetadataFloat(node, mNodeElementCur);
-    } else if (name == "MetadataInteger") {
-        readMetadataInteger(node, mNodeElementCur);
-    } else if (name == "MetadataSet") {
-        readMetadataSet(node, mNodeElementCur);
-    } else if (name == "MetadataString") {
-        readMetadataString(node, mNodeElementCur);
+        return FindNodeElement_FromRoot(pID, pType, pElement);
     }
 }
 
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: parse set ***********************************************************/
+/*********************************************************************************************************************************************/
 
-aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() {
-    X3DNodeElementBase *cur_node = nullptr;
-    std::list<aiMatrix4x4> matr;
-    aiMatrix4x4 out_matr;
-
-    // starting walk from current element to root
-    cur_node = cur_node;
-    if (cur_node != nullptr) {
-        do {
-            // if cur_node is group then store group transformation matrix in list.
-            if (cur_node->Type == X3DNodeElementBase::ENET_Group) matr.push_back(((X3DNodeElementBase *)cur_node)->Transformation);
+void X3DImporter::ParseHelper_Group_Begin(const bool pStatic) {
+    X3DNodeElementGroup *new_group = new X3DNodeElementGroup(mNodeElementCur, pStatic); // create new node with current node as parent.
 
-            cur_node = cur_node->Parent;
-        } while (cur_node != nullptr);
+    // if we are adding not the root element then add new element to current element child list.
+    if (mNodeElementCur != nullptr) {
+        mNodeElementCur->Children.push_back(new_group);
     }
 
-    // multiplicate all matrices in reverse order
-    for (std::list<aiMatrix4x4>::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit)
-        out_matr = out_matr * (*rit);
-
-    return out_matr;
-}
-
-void X3DImporter::PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, std::list<X3DNodeElementBase *> &pList) const {
-    // walk through childs and find for metadata.
-    for (std::list<X3DNodeElementBase *>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) {
-        if (((*el_it)->Type == X3DElemType::ENET_MetaBoolean) || ((*el_it)->Type == X3DElemType::ENET_MetaDouble) ||
-                ((*el_it)->Type == X3DElemType::ENET_MetaFloat) || ((*el_it)->Type == X3DElemType::ENET_MetaInteger) ||
-                ((*el_it)->Type == X3DElemType::ENET_MetaString)) {
-            pList.push_back(*el_it);
-        } else if ((*el_it)->Type == X3DElemType::ENET_MetaSet) {
-            PostprocessHelper_CollectMetadata(**el_it, pList);
-        }
-    } // for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
-}
-
-bool X3DImporter::PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const {
-    if ((pType == X3DNodeElementBase::ENET_MetaBoolean) || (pType == X3DElemType::ENET_MetaDouble) ||
-            (pType == X3DElemType::ENET_MetaFloat) || (pType == X3DElemType::ENET_MetaInteger) ||
-            (pType == X3DElemType::ENET_MetaString) || (pType == X3DElemType::ENET_MetaSet)) {
-        return true;
-    }
-    return false;
+    NodeElement_List.push_back(new_group); // it's a new element - add it to list.
+    mNodeElementCur = new_group; // switch current element to new one.
 }
 
-bool X3DImporter::PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const {
-    if ((pType == X3DElemType::ENET_Arc2D) || (pType == X3DElemType::ENET_ArcClose2D) ||
-            (pType == X3DElemType::ENET_Box) || (pType == X3DElemType::ENET_Circle2D) ||
-            (pType == X3DElemType::ENET_Cone) || (pType == X3DElemType::ENET_Cylinder) ||
-            (pType == X3DElemType::ENET_Disk2D) || (pType == X3DElemType::ENET_ElevationGrid) ||
-            (pType == X3DElemType::ENET_Extrusion) || (pType == X3DElemType::ENET_IndexedFaceSet) ||
-            (pType == X3DElemType::ENET_IndexedLineSet) || (pType == X3DElemType::ENET_IndexedTriangleFanSet) ||
-            (pType == X3DElemType::ENET_IndexedTriangleSet) || (pType == X3DElemType::ENET_IndexedTriangleStripSet) ||
-            (pType == X3DElemType::ENET_PointSet) || (pType == X3DElemType::ENET_LineSet) ||
-            (pType == X3DElemType::ENET_Polyline2D) || (pType == X3DElemType::ENET_Polypoint2D) ||
-            (pType == X3DElemType::ENET_Rectangle2D) || (pType == X3DElemType::ENET_Sphere) ||
-            (pType == X3DElemType::ENET_TriangleFanSet) || (pType == X3DElemType::ENET_TriangleSet) ||
-            (pType == X3DElemType::ENET_TriangleSet2D) || (pType == X3DElemType::ENET_TriangleStripSet)) {
-        return true;
-    } else {
-        return false;
-    }
-}
-
-void X3DImporter::Postprocess_BuildLight(const CX3DImporter_NodeElement &pNodeElement, std::list<aiLight *> &pSceneLightList) const {
-    const CX3DImporter_NodeElement_Light &ne = *((CX3DImporter_NodeElement_Light *)&pNodeElement);
-    aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent();
-    aiLight *new_light = new aiLight;
-
-    new_light->mName = ne.ID;
-    new_light->mColorAmbient = ne.Color * ne.AmbientIntensity;
-    new_light->mColorDiffuse = ne.Color * ne.Intensity;
-    new_light->mColorSpecular = ne.Color * ne.Intensity;
-    switch (pNodeElement.Type) {
-    case CX3DImporter_NodeElement::ENET_DirectionalLight:
-        new_light->mType = aiLightSource_DIRECTIONAL;
-        new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr;
-
-        break;
-    case CX3DImporter_NodeElement::ENET_PointLight:
-        new_light->mType = aiLightSource_POINT;
-        new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr;
-        new_light->mAttenuationConstant = ne.Attenuation.x;
-        new_light->mAttenuationLinear = ne.Attenuation.y;
-        new_light->mAttenuationQuadratic = ne.Attenuation.z;
-
-        break;
-    case CX3DImporter_NodeElement::ENET_SpotLight:
-        new_light->mType = aiLightSource_SPOT;
-        new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr;
-        new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr;
-        new_light->mAttenuationConstant = ne.Attenuation.x;
-        new_light->mAttenuationLinear = ne.Attenuation.y;
-        new_light->mAttenuationQuadratic = ne.Attenuation.z;
-        new_light->mAngleInnerCone = ne.BeamWidth;
-        new_light->mAngleOuterCone = ne.CutOffAngle;
-
-        break;
-    default:
-        throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + to_string(pNodeElement.Type) + ".");
-    }
-
-    pSceneLightList.push_back(new_light);
-}
-
-void X3DImporter::Postprocess_BuildMaterial(const CX3DImporter_NodeElement &pNodeElement, aiMaterial **pMaterial) const {
-    // check argument
-    if (pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr.");
-    if (*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr.");
-
-    *pMaterial = new aiMaterial;
-    aiMaterial &taimat = **pMaterial; // creating alias for convenience.
-
-    // at this point pNodeElement point to <Appearance> node. Walk through childs and add all stored data.
-    for (std::list<CX3DImporter_NodeElement *>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) {
-        if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material) {
-            aiColor3D tcol3;
-            float tvalf;
-            CX3DImporter_NodeElement_Material &tnemat = *((CX3DImporter_NodeElement_Material *)*el_it);
-
-            tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity;
-            taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT);
-            taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE);
-            taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE);
-            taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR);
-            tvalf = 1;
-            taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH);
-            taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS);
-            tvalf = 1.0f - tnemat.Transparency;
-            taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY);
-        } // if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material)
-        else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture) {
-            CX3DImporter_NodeElement_ImageTexture &tnetex = *((CX3DImporter_NodeElement_ImageTexture *)*el_it);
-            aiString url_str(tnetex.URL.c_str());
-            int mode = aiTextureOp_Multiply;
-
-            taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0));
-            taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
-            taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
-            taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0));
-        } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture)
-        else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform) {
-            aiUVTransform trans;
-            CX3DImporter_NodeElement_TextureTransform &tnetextr = *((CX3DImporter_NodeElement_TextureTransform *)*el_it);
-
-            trans.mTranslation = tnetextr.Translation - tnetextr.Center;
-            trans.mScaling = tnetextr.Scale;
-            trans.mRotation = tnetextr.Rotation;
-            taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
-        } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform)
-    } // for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
-}
+void X3DImporter::ParseHelper_Node_Enter(X3DNodeElementBase *pNode) {
+    mNodeElementCur->Children.push_back(pNode); // add new element to current element child list.
+    mNodeElementCur = pNode; // switch current element to new one.
+}    
 
-void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement &pNodeElement, aiMesh **pMesh) const {
-    // check argument
-    if (pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr.");
-    if (*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr.");
-
-    /************************************************************************************************************************************/
-    /************************************************************ Geometry2D ************************************************************/
-    /************************************************************************************************************************************/
-    if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Arc2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ArcClose2D) ||
-            (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Circle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Disk2D) ||
-            (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polyline2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polypoint2D) ||
-            (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet2D)) {
-        CX3DImporter_NodeElement_Geometry2D &tnemesh = *((CX3DImporter_NodeElement_Geometry2D *)&pNodeElement); // create alias for convenience
-        std::vector<aiVector3D> tarr;
-
-        tarr.reserve(tnemesh.Vertices.size());
-        for (std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it)
-            tarr.push_back(*it);
-        *pMesh = StandardShapes::MakeMesh(tarr, static_cast<unsigned int>(tnemesh.NumIndices)); // create mesh from vertices using Assimp help.
-
-        return; // mesh is build, nothing to do anymore.
-    }
-    /************************************************************************************************************************************/
-    /************************************************************ Geometry3D ************************************************************/
-    /************************************************************************************************************************************/
-    //
-    // Predefined figures
-    //
-    if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Box) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cone) ||
-            (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cylinder) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Sphere)) {
-        CX3DImporter_NodeElement_Geometry3D &tnemesh = *((CX3DImporter_NodeElement_Geometry3D *)&pNodeElement); // create alias for convenience
-        std::vector<aiVector3D> tarr;
-
-        tarr.reserve(tnemesh.Vertices.size());
-        for (std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it)
-            tarr.push_back(*it);
-
-        *pMesh = StandardShapes::MakeMesh(tarr, static_cast<unsigned int>(tnemesh.NumIndices)); // create mesh from vertices using Assimp help.
-
-        return; // mesh is build, nothing to do anymore.
-    }
-    //
-    // Parametric figures
-    //
-    if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid) {
-        CX3DImporter_NodeElement_ElevationGrid &tnemesh = *((CX3DImporter_NodeElement_ElevationGrid *)&pNodeElement); // create alias for convenience
-
-        // at first create mesh from existing vertices.
-        *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIdx, tnemesh.Vertices);
-        // copy additional information from children
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
-                MeshGeometry_AddNormal(**pMesh, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, tnemesh.NormalPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
-                MeshGeometry_AddTexCoord(**pMesh, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value);
-            else
-                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + to_string((*ch_it)->Type) + ".");
-        } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
-
-        return; // mesh is build, nothing to do anymore.
-    } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid)
-    //
-    // Indexed primitives sets
-    //
-    if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet) {
-        CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience
-
-        // at first search for <Coordinate> node and create mesh.
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-                *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value);
-            }
-        }
-
-        // copy additional information from children
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
-                MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
-                MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value,
-                        tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-            } // skip because already read when mesh created.
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
-                MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value,
-                        tnemesh.NormalPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
-                MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value);
-            else
-                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + to_string((*ch_it)->Type) + ".");
-        } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
-
-        return; // mesh is build, nothing to do anymore.
-    } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet)
-
-    if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet) {
-        CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience
-
-        // at first search for <Coordinate> node and create mesh.
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-                *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value);
-            }
-        }
-
-        // copy additional information from children
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            ai_assert(*pMesh);
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
-                MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
-                MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value,
-                        tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-            } // skip because already read when mesh created.
-            else
-                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + to_string((*ch_it)->Type) + ".");
-        } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
-
-        return; // mesh is build, nothing to do anymore.
-    } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet)
-
-    if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) ||
-            (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) ||
-            (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet)) {
-        CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience
-
-        // at first search for <Coordinate> node and create mesh.
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-                *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value);
-            }
-        }
-
-        // copy additional information from children
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            ai_assert(*pMesh);
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
-                MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
-                MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value,
-                        tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-            } // skip because already read when mesh created.
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
-                MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value,
-                        tnemesh.NormalPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
-                MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value);
-            else
-                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \
-																	IndexedTriangleStripSet: " +
-                                        to_string((*ch_it)->Type) + ".");
-        } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
-
-        return; // mesh is build, nothing to do anymore.
-    } // if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet))
-
-    if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion) {
-        CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience
-
-        *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, tnemesh.Vertices);
-
-        return; // mesh is build, nothing to do anymore.
-    } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion)
-
-    //
-    // Primitives sets
-    //
-    if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet) {
-        CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience
-
-        // at first search for <Coordinate> node and create mesh.
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-                std::vector<aiVector3D> vec_copy;
-
-                vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size());
-                for (std::list<aiVector3D>::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin();
-                        it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) {
-                    vec_copy.push_back(*it);
-                }
-
-                *pMesh = StandardShapes::MakeMesh(vec_copy, 1);
-            }
-        }
-
-        // copy additional information from children
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            ai_assert(*pMesh);
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-            } // skip because already read when mesh created.
-            else
-                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + to_string((*ch_it)->Type) + ".");
-        } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
-
-        return; // mesh is build, nothing to do anymore.
-    } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet)
-
-    if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet) {
-        CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience
-
-        // at first search for <Coordinate> node and create mesh.
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-                *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value);
-            }
-        }
-
-        // copy additional information from children
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            ai_assert(*pMesh);
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-            } // skip because already read when mesh created.
-            else
-                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + to_string((*ch_it)->Type) + ".");
-        } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
-
-        return; // mesh is build, nothing to do anymore.
-    } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet)
-
-    if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet) {
-        CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience
-
-        // at first search for <Coordinate> node and create mesh.
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-                *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value);
-            }
-        }
-
-        // copy additional information from children
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            if (nullptr == *pMesh) {
-                break;
-            }
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-            } // skip because already read when mesh created.
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
-                MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value,
-                        tnemesh.NormalPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
-                MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value);
-            else
-                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + to_string((*ch_it)->Type) + ".");
-        } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
-
-        return; // mesh is build, nothing to do anymore.
-    } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet)
-
-    if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet) {
-        CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience
-
-        // at first search for <Coordinate> node and create mesh.
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-                std::vector<aiVector3D> vec_copy;
-
-                vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size());
-                for (std::list<aiVector3D>::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin();
-                        it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) {
-                    vec_copy.push_back(*it);
-                }
-
-                *pMesh = StandardShapes::MakeMesh(vec_copy, 3);
-            }
-        }
-
-        // copy additional information from children
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            ai_assert(*pMesh);
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-            } // skip because already read when mesh created.
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
-                MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value,
-                        tnemesh.NormalPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
-                MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value);
-            else
-                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + to_string((*ch_it)->Type) + ".");
-        } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
-
-        return; // mesh is build, nothing to do anymore.
-    } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet)
-
-    if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet) {
-        CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience
-
-        // at first search for <Coordinate> node and create mesh.
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-                *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value);
-            }
-        }
-
-        // copy additional information from children
-        for (std::list<CX3DImporter_NodeElement *>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) {
-            ai_assert(*pMesh);
-            if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA)
-                MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) {
-            } // skip because already read when mesh created.
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal)
-                MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value,
-                        tnemesh.NormalPerVertex);
-            else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate)
-                MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value);
-            else
-                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + to_string((*ch_it)->Type) + ".");
-        } // for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
-
-        return; // mesh is build, nothing to do anymore.
-    } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet)
-
-    throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + to_string(pNodeElement.Type) + ".");
-}
-
-void X3DImporter::Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list<aiMesh *> &pSceneMeshList,
-        std::list<aiMaterial *> &pSceneMaterialList, std::list<aiLight *> &pSceneLightList) const {
-    X3DElementList::const_iterator chit_begin = pNodeElement.Children.begin();
-    X3DElementList::const_iterator chit_end = pNodeElement.Children.end();
-    std::list<aiNode *> SceneNode_Child;
-    std::list<unsigned int> SceneNode_Mesh;
-
-    // At first read all metadata
-    Postprocess_CollectMetadata(pNodeElement, pSceneNode);
-    // check if we have deal with grouping node. Which can contain transformation or switch
-    if (pNodeElement.Type == X3DElemType::ENET_Group) {
-        const CX3DNodeElementGroup &tne_group = *((CX3DNodeElementGroup*)&pNodeElement); // create alias for convenience
-
-        pSceneNode.mTransformation = tne_group.Transformation;
-        if (tne_group.UseChoice) {
-            // If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen.
-            if ((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Children.size())) {
-                chit_begin = pNodeElement.Children.end();
-                chit_end = pNodeElement.Children.end();
-            } else {
-                for (size_t i = 0; i < (size_t)tne_group.Choice; i++)
-                    ++chit_begin; // forward iterator to chosen node.
-
-                chit_end = chit_begin;
-                ++chit_end; // point end iterator to next element after chosen node.
-            }
-        } // if(tne_group.UseChoice)
-    } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group)
-
-    // Reserve memory for fast access and check children.
-    for (std::list<X3DNodeElementBase *>::const_iterator it = chit_begin; it != chit_end; ++it) { // in this loop we do not read metadata because it's already read at begin.
-        if ((*it)->Type == X3DElemType::ENET_Group) {
-            // if child is group then create new node and do recursive call.
-            aiNode *new_node = new aiNode;
-
-            new_node->mName = (*it)->ID;
-            new_node->mParent = &pSceneNode;
-            SceneNode_Child.push_back(new_node);
-            Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList);
-        } else if ((*it)->Type == X3DElemType::ENET_Shape) {
-            // shape can contain only one geometry and one appearance nodes.
-            Postprocess_BuildShape(*((CX3DImporter_NodeElement_Shape *)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList);
-        } else if (((*it)->Type == X3DElemType::ENET_DirectionalLight) || ((*it)->Type == X3DElemType::ENET_PointLight) ||
-                   ((*it)->Type == X3DElemType::ENET_SpotLight)) {
-            Postprocess_BuildLight(*((X3DElemType *)*it), pSceneLightList);
-        } else if (!PostprocessHelper_ElementIsMetadata((*it)->Type)) // skip metadata
-        {
-            throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + to_string((*it)->Type) + ".");
-        }
-    } // for(std::list<CX3DImporter_NodeElement*>::const_iterator it = chit_begin; it != chit_end; it++)
-
-    // copy data about children and meshes to aiNode.
-    if (!SceneNode_Child.empty()) {
-        std::list<aiNode *>::const_iterator it = SceneNode_Child.begin();
-
-        pSceneNode.mNumChildren = static_cast<unsigned int>(SceneNode_Child.size());
-        pSceneNode.mChildren = new aiNode *[pSceneNode.mNumChildren];
-        for (size_t i = 0; i < pSceneNode.mNumChildren; i++)
-            pSceneNode.mChildren[i] = *it++;
-    }
-
-    if (!SceneNode_Mesh.empty()) {
-        std::list<unsigned int>::const_iterator it = SceneNode_Mesh.begin();
-
-        pSceneNode.mNumMeshes = static_cast<unsigned int>(SceneNode_Mesh.size());
-        pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes];
-        for (size_t i = 0; i < pSceneNode.mNumMeshes; i++)
-            pSceneNode.mMeshes[i] = *it++;
+void X3DImporter::ParseHelper_Node_Exit() {
+    // check if we can walk up.
+    if (mNodeElementCur != nullptr) {
+        mNodeElementCur = mNodeElementCur->Parent;
     }
-
-    // that's all. return to previous deals
-}
-
-void X3DImporter::Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape &pShapeNodeElement, std::list<unsigned int> &pNodeMeshInd,
-        std::list<aiMesh *> &pSceneMeshList, std::list<aiMaterial *> &pSceneMaterialList) const {
-    aiMaterial *tmat = nullptr;
-    aiMesh *tmesh = nullptr;
-    X3DElemType mesh_type = X3DElemType::ENET_Invalid;
-    unsigned int mat_ind = 0;
-
-    for (X3DElementList::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); ++it) {
-        if (PostprocessHelper_ElementIsMesh((*it)->Type)) {
-            Postprocess_BuildMesh(**it, &tmesh);
-            if (tmesh != nullptr) {
-                // if mesh successfully built then add data about it to arrays
-                pNodeMeshInd.push_back(static_cast<unsigned int>(pSceneMeshList.size()));
-                pSceneMeshList.push_back(tmesh);
-                // keep mesh type. Need above for texture coordinate generation.
-                mesh_type = (*it)->Type;
-            }
-        } else if ((*it)->Type == X3DElemType::ENET_Appearance) {
-            Postprocess_BuildMaterial(**it, &tmat);
-            if (tmat != nullptr) {
-                // if material successfully built then add data about it to array
-                mat_ind = static_cast<unsigned int>(pSceneMaterialList.size());
-                pSceneMaterialList.push_back(tmat);
-            }
-        }
-    } // for(std::list<CX3DImporter_NodeElement*>::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++)
-
-    // associate read material with read mesh.
-    if ((tmesh != nullptr) && (tmat != nullptr)) {
-        tmesh->mMaterialIndex = mat_ind;
-        // Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates.
-        if ((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) {
-            int32_t tm;
-            aiVector3D tvec3;
-
-            switch (mesh_type) {
-            case X3DElemType::ENET_Box:
-                tm = aiTextureMapping_BOX;
-                break;
-            case X3DElemType::ENET_Cone:
-            case X3DElemType::ENET_Cylinder:
-                tm = aiTextureMapping_CYLINDER;
-                break;
-            case X3DElemType::ENET_Sphere:
-                tm = aiTextureMapping_SPHERE;
-                break;
-            default:
-                tm = aiTextureMapping_PLANE;
-                break;
-            } // switch(mesh_type)
-
-            tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0));
-        } // if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0))
-    } // if((tmesh != nullptr) && (tmat != nullptr))
-}
-
-void X3DImporter::Postprocess_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, aiNode &pSceneNode) const {
-    X3DElementList meta_list;
-    size_t meta_idx;
-
-    PostprocessHelper_CollectMetadata(pNodeElement, meta_list); // find metadata in current node element.
-    if (!meta_list.empty()) {
-        if (pSceneNode.mMetaData != nullptr) {
-            throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong.");
-        }
-
-        // copy collected metadata to output node.
-        pSceneNode.mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(meta_list.size()));
-        meta_idx = 0;
-        for (X3DElementList::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx) {
-            CX3DImporter_NodeElement_Meta *cur_meta = (CX3DImporter_NodeElement_Meta *)*it;
-
-            // due to limitations we can add only first element of value list.
-            // Add an element according to its type.
-            if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean) {
-                if (((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.size() > 0)
-                    pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.begin()));
-            } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble) {
-                if (((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.size() > 0)
-                    pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, (float)*(((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.begin()));
-            } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat) {
-                if (((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.size() > 0)
-                    pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.begin()));
-            } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger) {
-                if (((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.size() > 0)
-                    pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.begin()));
-            } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaString) {
-                if (((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.size() > 0) {
-                    aiString tstr(((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.begin()->data());
-
-                    pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, tstr);
-                }
-            } else {
-                throw DeadlyImportError("Postprocess. Unknown metadata type.");
-            } // if((*it)->Type == CX3DImporter_NodeElement::ENET_Meta*) else
-        } // for(std::list<CX3DImporter_NodeElement*>::const_iterator it = meta_list.begin(); it != meta_list.end(); it++)
-    } // if( !meta_list.empty() )
 }
 
 #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 119 - 410
code/AssetLib/X3D/X3DImporter.hpp

@@ -41,6 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef INCLUDED_AI_X3D_IMPORTER_H
 #define INCLUDED_AI_X3D_IMPORTER_H
 
+#include "X3DImporter_Node.hpp"
+
 #include <assimp/BaseImporter.h>
 #include <assimp/XmlParser.h>
 #include <assimp/importerdesc.h>
@@ -51,7 +53,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <list>
 #include <string>
-#include <vector>
 
 namespace Assimp {
 
@@ -68,6 +69,21 @@ inline void Throw_ConvertFail_Str2ArrF(const std::string &nodeName, const std::s
                             "\" from string to array of floats.");
 }
 
+inline void Throw_ConvertFail_Str2ArrD(const std::string &nodeName, const std::string &pAttrValue) {
+    throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue +
+                            "\" from string to array of doubles.");
+}
+
+inline void Throw_ConvertFail_Str2ArrB(const std::string &nodeName, const std::string &pAttrValue) {
+    throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue +
+                            "\" from string to array of booleans.");
+}
+
+inline void Throw_ConvertFail_Str2ArrI(const std::string &nodeName, const std::string &pAttrValue) {
+    throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue +
+                            "\" from string to array of integers.");
+}
+
 inline void Throw_DEF_And_USE(const std::string &nodeName) {
     throw DeadlyImportError("\"DEF\" and \"USE\" can not be defined both in <" + nodeName + ">.");
 }
@@ -225,410 +241,8 @@ inline void LogInfo(const std::string &message) {
 ///
 ///	That's all for now. Enjoy
 ///
-enum class X3DElemType {
-    ENET_Group, ///< Element has type "Group".
-    ENET_MetaBoolean, ///< Element has type "Metadata boolean".
-    ENET_MetaDouble, ///< Element has type "Metadata double".
-    ENET_MetaFloat, ///< Element has type "Metadata float".
-    ENET_MetaInteger, ///< Element has type "Metadata integer".
-    ENET_MetaSet, ///< Element has type "Metadata set".
-    ENET_MetaString, ///< Element has type "Metadata string".
-    ENET_Arc2D, ///< Element has type "Arc2D".
-    ENET_ArcClose2D, ///< Element has type "ArcClose2D".
-    ENET_Circle2D, ///< Element has type "Circle2D".
-    ENET_Disk2D, ///< Element has type "Disk2D".
-    ENET_Polyline2D, ///< Element has type "Polyline2D".
-    ENET_Polypoint2D, ///< Element has type "Polypoint2D".
-    ENET_Rectangle2D, ///< Element has type "Rectangle2D".
-    ENET_TriangleSet2D, ///< Element has type "TriangleSet2D".
-    ENET_Box, ///< Element has type "Box".
-    ENET_Cone, ///< Element has type "Cone".
-    ENET_Cylinder, ///< Element has type "Cylinder".
-    ENET_Sphere, ///< Element has type "Sphere".
-    ENET_ElevationGrid, ///< Element has type "ElevationGrid".
-    ENET_Extrusion, ///< Element has type "Extrusion".
-    ENET_Coordinate, ///< Element has type "Coordinate".
-    ENET_Normal, ///< Element has type "Normal".
-    ENET_TextureCoordinate, ///< Element has type "TextureCoordinate".
-    ENET_IndexedFaceSet, ///< Element has type "IndexedFaceSet".
-    ENET_IndexedLineSet, ///< Element has type "IndexedLineSet".
-    ENET_IndexedTriangleSet, ///< Element has type "IndexedTriangleSet".
-    ENET_IndexedTriangleFanSet, ///< Element has type "IndexedTriangleFanSet".
-    ENET_IndexedTriangleStripSet, ///< Element has type "IndexedTriangleStripSet".
-    ENET_LineSet, ///< Element has type "LineSet".
-    ENET_PointSet, ///< Element has type "PointSet".
-    ENET_TriangleSet, ///< Element has type "TriangleSet".
-    ENET_TriangleFanSet, ///< Element has type "TriangleFanSet".
-    ENET_TriangleStripSet, ///< Element has type "TriangleStripSet".
-    ENET_Color, ///< Element has type "Color".
-    ENET_ColorRGBA, ///< Element has type "ColorRGBA".
-    ENET_Shape, ///< Element has type "Shape".
-    ENET_Appearance, ///< Element has type "Appearance".
-    ENET_Material, ///< Element has type "Material".
-    ENET_ImageTexture, ///< Element has type "ImageTexture".
-    ENET_TextureTransform, ///< Element has type "TextureTransform".
-    ENET_DirectionalLight, ///< Element has type "DirectionalLight".
-    ENET_PointLight, ///< Element has type "PointLight".
-    ENET_SpotLight, ///< Element has type "SpotLight".
-
-    ENET_Invalid ///< Element has invalid type and possible contain invalid data.
-};
-
-struct X3DNodeElementBase {
-    X3DNodeElementBase *Parent;
-    std::string ID;
-    std::list<X3DNodeElementBase *> Children;
-    X3DElemType Type;
-
-protected:
-    X3DNodeElementBase(X3DElemType type, X3DNodeElementBase *pParent) :
-            Type(type), Parent(pParent) {
-        // empty
-    }
-};
-
-/// This struct hold <Color> value.
-struct CX3DImporter_NodeElement_Color : X3DNodeElementBase {
-    std::list<aiColor3D> Value; ///< Stored value.
-
-    /// Constructor
-    /// \param [in] pParent - pointer to parent node.
-    CX3DImporter_NodeElement_Color(X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(X3DElemType::ENET_Color, pParent) {}
-
-}; // struct CX3DImporter_NodeElement_Color
-
-/// This struct hold <ColorRGBA> value.
-struct CX3DImporter_NodeElement_ColorRGBA : X3DNodeElementBase {
-    std::list<aiColor4D> Value; ///< Stored value.
-
-    /// Constructor
-    /// \param [in] pParent - pointer to parent node.
-    CX3DImporter_NodeElement_ColorRGBA(X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(X3DElemType::ENET_ColorRGBA, pParent) {}
-
-}; // struct CX3DImporter_NodeElement_ColorRGBA
-
-/// This struct hold <Coordinate> value.
-struct CX3DImporter_NodeElement_Coordinate : public X3DNodeElementBase {
-    std::list<aiVector3D> Value; ///< Stored value.
-
-    /// Constructor
-    /// \param [in] pParent - pointer to parent node.
-    CX3DImporter_NodeElement_Coordinate(X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(X3DElemType::ENET_Coordinate, pParent) {}
-
-}; // struct CX3DImporter_NodeElement_Coordinate
-
-/// This struct hold <Normal> value.
-struct CX3DImporter_NodeElement_Normal : X3DNodeElementBase {
-    std::list<aiVector3D> Value; ///< Stored value.
-
-    /// Constructor
-    /// \param [in] pParent - pointer to parent node.
-    CX3DImporter_NodeElement_Normal(X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(X3DElemType::ENET_Normal, pParent) {}
-
-}; // struct CX3DImporter_NodeElement_Normal
-
-/// This struct hold <TextureCoordinate> value.
-struct CX3DImporter_NodeElement_TextureCoordinate : X3DNodeElementBase {
-    std::list<aiVector2D> Value; ///< Stored value.
-
-    /// Constructor
-    /// \param [in] pParent - pointer to parent node.
-    CX3DImporter_NodeElement_TextureCoordinate(X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(X3DElemType::ENET_TextureCoordinate, pParent) {}
-
-}; // struct CX3DImporter_NodeElement_TextureCoordinate
-
-/// Two-dimensional figure.
-struct CX3DImporter_NodeElement_Geometry2D : X3DNodeElementBase {
-    std::list<aiVector3D> Vertices; ///< Vertices list.
-    size_t NumIndices; ///< Number of indices in one face.
-    bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object.
-
-    /// Constructor.
-    /// \param [in] pParent - pointer to parent node.
-    /// \param [in] pType - type of geometry object.
-    CX3DImporter_NodeElement_Geometry2D(X3DElemType pType, X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(pType, pParent), Solid(true) {}
-
-}; // class CX3DImporter_NodeElement_Geometry2D
-
-/// Three-dimensional body.
-struct CX3DImporter_NodeElement_Geometry3D : X3DNodeElementBase {
-    std::list<aiVector3D> Vertices; ///< Vertices list.
-    size_t NumIndices; ///< Number of indices in one face.
-    bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object.
-
-    /// Constructor.
-    /// \param [in] pParent - pointer to parent node.
-    /// \param [in] pType - type of geometry object.
-    CX3DImporter_NodeElement_Geometry3D(X3DElemType pType, X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(pType, pParent), Vertices(), NumIndices(0), Solid(true) {
-        // empty
-    }
-}; // class CX3DImporter_NodeElement_Geometry3D
-
-/// Uniform rectangular grid of varying height.
-struct CX3DImporter_NodeElement_ElevationGrid : CX3DImporter_NodeElement_Geometry3D {
-    bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line).
-    bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line).
-    /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are
-    /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced.
-    float CreaseAngle;
-    std::vector<int32_t> CoordIdx; ///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces.
-
-    /// Constructor.
-    /// \param [in] pParent - pointer to parent node.
-    /// \param [in] pType - type of geometry object.
-    CX3DImporter_NodeElement_ElevationGrid(X3DElemType pType, X3DNodeElementBase *pParent) :
-            CX3DImporter_NodeElement_Geometry3D(pType, pParent) {}
-}; // class CX3DImporter_NodeElement_IndexedSet
-
-/// Shape with indexed vertices.
-struct CX3DImporter_NodeElement_IndexedSet : public CX3DImporter_NodeElement_Geometry3D {
-    /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors
-    /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to
-    /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the
-    /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite
-    /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the
-    /// ccw field, results are undefined.
-    bool CCW;
-    std::vector<int32_t> ColorIndex; ///< Field to specify the polygonal faces by indexing into the <Color> or <ColorRGBA>.
-    bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line).
-    /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself,
-    /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results
-    /// even if the convex field is FALSE.
-    bool Convex;
-    std::vector<int32_t> CoordIndex; ///< Field to specify the polygonal faces by indexing into the <Coordinate>.
-    /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are
-    /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced.
-    float CreaseAngle;
-    std::vector<int32_t> NormalIndex; ///< Field to specify the polygonal faces by indexing into the <Normal>.
-    bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line).
-    std::vector<int32_t> TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
-
-    /// Constructor.
-    /// \param [in] pParent - pointer to parent node.
-    /// \param [in] pType - type of geometry object.
-    CX3DImporter_NodeElement_IndexedSet(X3DElemType pType, X3DNodeElementBase *pParent) :
-            CX3DImporter_NodeElement_Geometry3D(pType, pParent) {}
-}; // class CX3DImporter_NodeElement_IndexedSet
-
-/// Shape with set of vertices.
-struct CX3DImporter_NodeElement_Set : CX3DImporter_NodeElement_Geometry3D {
-    /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors
-    /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to
-    /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the
-    /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite
-    /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the
-    /// ccw field, results are undefined.
-    bool CCW;
-    bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line).
-    bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line).
-    std::vector<int32_t> CoordIndex; ///< Field to specify the polygonal faces by indexing into the <Coordinate>.
-    std::vector<int32_t> NormalIndex; ///< Field to specify the polygonal faces by indexing into the <Normal>.
-    std::vector<int32_t> TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
-    std::vector<int32_t> VertexCount; ///< Field describes how many vertices are to be used in each polyline(polygon) from the <Coordinate> field.
-
-    /// Constructor.
-    /// \param [in] pParent - pointer to parent node.
-    /// \param [in] pType - type of geometry object.
-    CX3DImporter_NodeElement_Set(X3DElemType type, X3DNodeElementBase *pParent) :
-            CX3DImporter_NodeElement_Geometry3D(type, pParent) {}
-
-}; // class CX3DImporter_NodeElement_Set
-
-/// This struct hold <Shape> value.
-struct CX3DImporter_NodeElement_Shape : X3DNodeElementBase {
-
-    /// Constructor
-    /// \param [in] pParent - pointer to parent node.
-    CX3DImporter_NodeElement_Shape(X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(X3DElemType::ENET_Shape, pParent) {}
-}; // struct CX3DImporter_NodeElement_Shape
-
-/// This struct hold <Appearance> value.
-struct CX3DImporter_NodeElement_Appearance : public X3DNodeElementBase {
-
-    /// Constructor
-    /// \param [in] pParent - pointer to parent node.
-    CX3DImporter_NodeElement_Appearance(X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(X3DElemType::ENET_Appearance, pParent) {}
-
-}; // struct CX3DImporter_NodeElement_Appearance
-
-struct CX3DImporter_NodeElement_Material : public X3DNodeElementBase {
-    float AmbientIntensity; ///< Specifies how much ambient light from light sources this surface shall reflect.
-    aiColor3D DiffuseColor; ///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source.
-    aiColor3D EmissiveColor; ///< Models "glowing" objects. This can be useful for displaying pre-lit models.
-    float Shininess; ///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights.
-    aiColor3D SpecularColor; ///< The specularColor and shininess fields determine the specular highlights.
-    float Transparency; ///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque.
-
-    /// Constructor.
-    /// \param [in] pParent - pointer to parent node.
-    /// \param [in] pType - type of geometry object.
-    CX3DImporter_NodeElement_Material(X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(X3DElemType::ENET_Material, pParent),
-            AmbientIntensity(0.0f),
-            DiffuseColor(),
-            EmissiveColor(),
-            Shininess(0.0f),
-            SpecularColor(),
-            Transparency(1.0f) {
-        // empty
-    }
-}; // class CX3DImporter_NodeElement_Material
-
-/// This struct hold <ImageTexture> value.
-struct CX3DImporter_NodeElement_ImageTexture : X3DNodeElementBase {
-    /// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated
-    /// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are
-    /// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field.
-    bool RepeatS;
-    bool RepeatT; ///< See \ref RepeatS.
-    std::string URL; ///< URL of the texture.
-
-                     /// Constructor
-    /// \param [in] pParent - pointer to parent node.
-    CX3DImporter_NodeElement_ImageTexture(X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(X3DElemType::ENET_ImageTexture, pParent) {}
-
-}; // struct CX3DImporter_NodeElement_ImageTexture
-
-/// This struct hold <TextureTransform> value.
-struct CX3DImporter_NodeElement_TextureTransform : X3DNodeElementBase {
-    aiVector2D Center; ///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied.
-    float Rotation; ///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied.
-    aiVector2D Scale; ///< Specifies a scaling factor in S and T of the texture coordinates about the center point.
-    aiVector2D Translation; ///<  Specifies a translation of the texture coordinates.
-
-    /// Constructor
-    /// \param [in] pParent - pointer to parent node.
-    CX3DImporter_NodeElement_TextureTransform(X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(X3DElemType::ENET_TextureTransform, pParent) {}
-
-}; // struct CX3DImporter_NodeElement_TextureTransform
-
-struct CX3DNodeElementGroup : X3DNodeElementBase {
-    aiMatrix4x4 Transformation; ///< Transformation matrix.
-
-    /// As you know node elements can use already defined node elements when attribute "USE" is defined.
-    /// Standard search when looking for an element in the whole scene graph, existing at this moment.
-    /// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static.
-    bool Static;
-
-    bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept.
-    int32_t Choice; ///< Number of the child which will be kept.
-
-    /// Constructor.
-    /// \param [in] pParent - pointer to parent node.
-    /// \param [in] pStatic - static node flag.
-    CX3DNodeElementGroup(X3DNodeElementBase *pParent, const bool pStatic = false) :
-            X3DNodeElementBase(X3DElemType::ENET_Group, pParent), Static(pStatic), UseChoice(false) {}
-};
-
-struct X3DNodeElementMeta : X3DNodeElementBase {
-    std::string Name; ///< Name of metadata object.
-    std::string Reference;
-
-    virtual ~X3DNodeElementMeta() {
-        // empty
-    }
-
-protected:
-    X3DNodeElementMeta(X3DElemType type, X3DNodeElementBase *parent) :
-            X3DNodeElementBase(type, parent) {
-        // empty
-    }
-};
-
-struct X3DNodeElementMetaBoolean : X3DNodeElementMeta {
-    std::vector<bool> Value; ///< Stored value.
-
-    explicit X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) :
-            X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) {
-        // empty
-    }
-};
-
-struct X3DNodeElementMetaDouble : X3DNodeElementMeta {
-    std::vector<double> Value; ///< Stored value.
-
-    explicit X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) :
-            X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) {
-        // empty
-    }
-};
-
-struct X3DNodeElementMetaFloat : public X3DNodeElementMeta {
-    std::vector<float> Value; ///< Stored value.
-
-    explicit X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) :
-            X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) {
-        // empty
-    }
-};
-
-struct X3DNodeElementMetaInt : public X3DNodeElementMeta {
-    std::vector<int32_t> Value; ///< Stored value.
-
-    explicit X3DNodeElementMetaInt(X3DNodeElementBase *pParent) :
-            X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) {
-        // empty
-    }
-};
-
-struct X3DNodeElementMetaSet : public X3DNodeElementMeta {
-    std::list<X3DNodeElementMeta> Value; ///< Stored value.
-
-    explicit X3DNodeElementMetaSet(X3DNodeElementBase *pParent) :
-            X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) {
-        // empty
-    }
-};
-
-struct X3DNodeElementMetaString : X3DNodeElementMeta {
-    std::list<std::string> Value; ///< Stored value.
-
-    explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) :
-            X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) {
-        // empty
-    }
-};
-
-/// \struct CX3DImporter_NodeElement_Light
-/// This struct hold <TextureTransform> value.
-struct X3DNodeNodeElementLight : X3DNodeElementBase {
-    float AmbientIntensity; ///< Specifies the intensity of the ambient emission from the light.
-    aiColor3D Color; ///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value.
-    aiVector3D Direction; ///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system.
-    /// \var Global
-    /// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence.
-    /// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light.
-    bool Global;
-    float Intensity; ///< Specifies the brightness of the direct emission from the light.
-    /// \var Attenuation
-    /// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor
-    /// is: "1 / max(attenuation[0] + attenuation[1] * r + attenuation[2] * r2, 1)", where r is the distance from the light to the surface being illuminated.
-    aiVector3D Attenuation;
-    aiVector3D Location; ///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin.
-    float Radius; ///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source.
-    float BeamWidth; ///< Specifies an inner solid angle in which the light source emits light at uniform full intensity.
-    float CutOffAngle; ///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle).
-
-    /// Constructor
-    /// \param [in] pParent - pointer to parent node.
-    /// \param [in] pLightType - type of the light source.
-    X3DNodeNodeElementLight(X3DElemType pLightType, X3DNodeElementBase *pParent) :
-            X3DNodeElementBase(pLightType, pParent) {}
-
-}; // struct CX3DImporter_NodeElement_Light
-
-using X3DElementList = std::list<X3DNodeElementBase*>;
+
+using X3DElementList = std::list<X3DNodeElementBase *>;
 
 class X3DImporter : public BaseImporter {
 public:
@@ -654,18 +268,113 @@ public:
     void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
     const aiImporterDesc *GetInfo() const;
     void Clear();
-    void readMetadata(XmlNode &node);
+
+private:
+    bool isNodeEmpty(XmlNode &node);
+    void checkNodeMustBeEmpty(XmlNode &node);
+    void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node);
+    void readHead(XmlNode &node);
+    void readChildNodes(XmlNode &node, const std::string &pParentNodeName);
     void readScene(XmlNode &node);
-    void readViewpoint(XmlNode &node);
-    void readMetadataObject(XmlNode &node);
-    void ParseDirectionalLight(XmlNode &node);
+
+    bool FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement);
+    bool FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID,
+            const X3DElemType pType, X3DNodeElementBase **pElement);
+    bool FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement);
+    void ParseHelper_Group_Begin(const bool pStatic = false);
+    void ParseHelper_Node_Enter(X3DNodeElementBase *pNode);
+    void ParseHelper_Node_Exit();
+
+    // 2D geometry
+    void readArc2D(XmlNode &node);
+    void readArcClose2D(XmlNode &node);
+    void readCircle2D(XmlNode &node);
+    void readDisk2D(XmlNode &node);
+    void readPolyline2D(XmlNode &node);
+    void readPolypoint2D(XmlNode &node);
+    void readRectangle2D(XmlNode &node);
+    void readTriangleSet2D(XmlNode &node);
+
+    // 3D geometry
+    void readBox(XmlNode &node);
+    void readCone(XmlNode &node);
+    void readCylinder(XmlNode &node);
+    void readElevationGrid(XmlNode &node);
+    void readExtrusion(XmlNode &node);
+    void readIndexedFaceSet(XmlNode &node);
+    void readSphere(XmlNode &node);
+
+    // group
+    void startReadGroup(XmlNode &node);
+    void endReadGroup();
+    void startReadStaticGroup(XmlNode &node);
+    void endReadStaticGroup();
+    void startReadSwitch(XmlNode &node);
+    void endReadSwitch();
+    void startReadTransform(XmlNode &node);
+    void endReadTransform();
+
+    // light
+    void readDirectionalLight(XmlNode &node);
+    void readPointLight(XmlNode &node);
+    void readSpotLight(XmlNode &node);
+
+    // metadata
+    bool checkForMetadataNode(XmlNode &node);
+    void childrenReadMetadata(XmlNode &node, X3DNodeElementBase *pParentElement, const std::string &pNodeName);
+    void readMetadataBoolean(XmlNode &node);
+    void readMetadataDouble(XmlNode &node);
+    void readMetadataFloat(XmlNode &node);
+    void readMetadataInteger(XmlNode &node);
+    void readMetadataSet(XmlNode &node);
+    void readMetadataString(XmlNode &node);
+
+    // networking
+    void readInline(XmlNode &node);
+
+    // postprocessing
+    aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() const;
+    void PostprocessHelper_CollectMetadata(const X3DNodeElementBase &pNodeElement, std::list<X3DNodeElementBase *> &pList) const;
+    bool PostprocessHelper_ElementIsMetadata(const X3DElemType pType) const;
+    bool PostprocessHelper_ElementIsMesh(const X3DElemType pType) const;
+    void Postprocess_BuildLight(const X3DNodeElementBase &pNodeElement, std::list<aiLight *> &pSceneLightList) const;
+    void Postprocess_BuildMaterial(const X3DNodeElementBase &pNodeElement, aiMaterial **pMaterial) const;
+    void Postprocess_BuildMesh(const X3DNodeElementBase &pNodeElement, aiMesh **pMesh) const;
     void Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list<aiMesh *> &pSceneMeshList,
             std::list<aiMaterial *> &pSceneMaterialList, std::list<aiLight *> &pSceneLightList) const;
+    void Postprocess_BuildShape(const X3DNodeElementShape &pShapeNodeElement, std::list<unsigned int> &pNodeMeshInd,
+            std::list<aiMesh *> &pSceneMeshList, std::list<aiMaterial *> &pSceneMaterialList) const;
+    void Postprocess_CollectMetadata(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode) const;
+
+    // rendering
+    void readColor(XmlNode &node);
+    void readColorRGBA(XmlNode &node);
+    void readCoordinate(XmlNode &node);
+    void readIndexedLineSet(XmlNode &node);
+    void readIndexedTriangleFanSet(XmlNode &node);
+    void readIndexedTriangleSet(XmlNode &node);
+    void readIndexedTriangleStripSet(XmlNode &node);
+    void readLineSet(XmlNode &node);
+    void readPointSet(XmlNode &node);
+    void readTriangleFanSet(XmlNode &node);
+    void readTriangleSet(XmlNode &node);
+    void readTriangleStripSet(XmlNode &node);
+    void readNormal(XmlNode &node);
+
+    // shape
+    void readShape(XmlNode &node);
+    void readAppearance(XmlNode &node);
+    void readMaterial(XmlNode &node);
+
+    // texturing
+    void readImageTexture(XmlNode &node);
+    void readTextureCoordinate(XmlNode &node);
+    void readTextureTransform(XmlNode &node);
 
-private:
     static const aiImporterDesc Description;
     X3DNodeElementBase *mNodeElementCur;
     aiScene *mScene;
+    IOSystem *mpIOHandler;
 }; // class X3DImporter
 
 } // namespace Assimp

+ 467 - 0
code/AssetLib/X3D/X3DImporter_Geometry2D.cpp

@@ -0,0 +1,467 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file   X3DImporter_Geometry2D.cpp
+/// \brief  Parsing data from nodes of "Geometry2D" set of X3D.
+/// date   2015-2016
+/// author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+#include "X3DXmlHelper.h"
+#include "X3DGeoHelper.h"
+
+namespace Assimp {
+
+// <Arc2D
+// DEF=""              ID
+// USE=""              IDREF
+// endAngle="1.570796" SFFloat [initializeOnly]
+// radius="1"          SFFloat [initializeOnly]
+// startAngle="0"      SFFloat [initializeOnly]
+// />
+// The Arc2D node specifies a linear circular arc whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping
+// towards the positive y-axis. The radius field specifies the radius of the circle of which the arc is a portion. The arc extends from the startAngle
+// counterclockwise to the endAngle. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different
+// angle base unit has been specified). If startAngle and endAngle have the same value, a circle is specified.
+void X3DImporter::readArc2D(XmlNode &node) {
+    std::string def, use;
+    float endAngle = AI_MATH_HALF_PI_F;
+    float radius = 1;
+    float startAngle = 0;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getFloatAttribute(node, "endAngle", endAngle);
+    XmlParser::getFloatAttribute(node, "radius", radius);
+    XmlParser::getFloatAttribute(node, "startAngle", startAngle);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Arc2D, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Arc2D, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        // create point list of geometry object and convert it to line set.
+        std::list<aiVector3D> tlist;
+
+        X3DGeoHelper::make_arc2D(startAngle, endAngle, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg
+        X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices);
+        ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "Arc2D");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <ArcClose2D
+// DEF=""              ID
+// USE=""              IDREF
+// closureType="PIE"   SFString [initializeOnly], {"PIE", "CHORD"}
+// endAngle="1.570796" SFFloat  [initializeOnly]
+// radius="1"          SFFloat  [initializeOnly]
+// solid="false"       SFBool   [initializeOnly]
+// startAngle="0"      SFFloat  [initializeOnly]
+// />
+// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping
+// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius
+// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater
+// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has
+// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between
+// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center.
+// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then
+// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point
+// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when
+// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D.
+void X3DImporter::readArcClose2D(XmlNode &node) {
+    std::string def, use;
+    std::string closureType("PIE");
+    float endAngle = AI_MATH_HALF_PI_F;
+    float radius = 1;
+    bool solid = false;
+    float startAngle = 0;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getStdStrAttribute(node, "closureType", closureType);
+    XmlParser::getFloatAttribute(node, "endAngle", endAngle);
+    XmlParser::getFloatAttribute(node, "endAngle", endAngle);
+    XmlParser::getFloatAttribute(node, "radius", radius);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+    XmlParser::getFloatAttribute(node, "startAngle", startAngle);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ArcClose2D, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_ArcClose2D, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        ((X3DNodeElementGeometry2D *)ne)->Solid = solid;
+        // create point list of geometry object.
+        X3DGeoHelper::make_arc2D(startAngle, endAngle, radius, 10, ((X3DNodeElementGeometry2D *)ne)->Vertices); ///TODO: IME - AI_CONFIG for NumSeg
+        // add chord or two radiuses only if not a circle was defined
+        if (!((std::fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle))) {
+            std::list<aiVector3D> &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias.
+
+            if ((closureType == "PIE") || (closureType == "\"PIE\""))
+                vlist.push_back(aiVector3D(0, 0, 0)); // center point - first radial line
+            else if ((closureType != "CHORD") && (closureType != "\"CHORD\""))
+                Throw_IncorrectAttrValue("ArcClose2D", "closureType");
+
+            vlist.push_back(*vlist.begin()); // arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE).
+        }
+
+        ((X3DNodeElementGeometry2D *)ne)->NumIndices = ((X3DNodeElementGeometry2D *)ne)->Vertices.size();
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "ArcClose2D");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <Circle2D
+// DEF=""     ID
+// USE=""     IDREF
+// radius="1" SFFloat  [initializeOnly]
+// />
+void X3DImporter::readCircle2D(XmlNode &node) {
+    std::string def, use;
+    float radius = 1;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getFloatAttribute(node, "radius", radius);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Circle2D, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Circle2D, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        // create point list of geometry object and convert it to line set.
+        std::list<aiVector3D> tlist;
+
+        X3DGeoHelper::make_arc2D(0, 0, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg
+        X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices);
+        ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "Circle2D");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <Disk2D
+// DEF=""          ID
+// USE=""          IDREF
+// innerRadius="0" SFFloat  [initializeOnly]
+// outerRadius="1" SFFloat  [initializeOnly]
+// solid="false"   SFBool   [initializeOnly]
+// />
+// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the
+// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero.
+// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely
+// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall
+// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of
+// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D.
+void X3DImporter::readDisk2D(XmlNode &node) {
+    std::string def, use;
+    float innerRadius = 0;
+    float outerRadius = 1;
+    bool solid = false;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getFloatAttribute(node, "innerRadius", innerRadius);
+    XmlParser::getFloatAttribute(node, "outerRadius", outerRadius);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Disk2D, ne);
+    } else {
+        std::list<aiVector3D> tlist_o, tlist_i;
+
+        if (innerRadius > outerRadius) Throw_IncorrectAttrValue("Disk2D", "innerRadius");
+
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Disk2D, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        // create point list of geometry object.
+        ///TODO: IME - AI_CONFIG for NumSeg
+        X3DGeoHelper::make_arc2D(0, 0, outerRadius, 10, tlist_o); // outer circle
+        if (innerRadius == 0.0f) { // make filled disk
+            // in tlist_o we already have points of circle. just copy it and sign as polygon.
+            ((X3DNodeElementGeometry2D *)ne)->Vertices = tlist_o;
+            ((X3DNodeElementGeometry2D *)ne)->NumIndices = tlist_o.size();
+        } else if (innerRadius == outerRadius) { // make circle
+            // in tlist_o we already have points of circle. convert it to line set.
+            X3DGeoHelper::extend_point_to_line(tlist_o, ((X3DNodeElementGeometry2D *)ne)->Vertices);
+            ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2;
+        } else { // make disk
+            std::list<aiVector3D> &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias.
+
+            X3DGeoHelper::make_arc2D(0, 0, innerRadius, 10, tlist_i); // inner circle
+            //
+            // create quad list from two point lists
+            //
+            if (tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list."); // tlist_i and tlist_o has equal size.
+
+            // add all quads except last
+            for (std::list<aiVector3D>::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();) {
+                // do not forget - CCW direction
+                vlist.push_back(*it_i++); // 1st point
+                vlist.push_back(*it_o++); // 2nd point
+                vlist.push_back(*it_o); // 3rd point
+                vlist.push_back(*it_i); // 4th point
+            }
+
+            // add last quad
+            vlist.push_back(*tlist_i.end()); // 1st point
+            vlist.push_back(*tlist_o.end()); // 2nd point
+            vlist.push_back(*tlist_o.begin()); // 3rd point
+            vlist.push_back(*tlist_o.begin()); // 4th point
+
+            ((X3DNodeElementGeometry2D *)ne)->NumIndices = 4;
+        }
+
+        ((X3DNodeElementGeometry2D *)ne)->Solid = solid;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "Disk2D");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <Polyline2D
+// DEF=""          ID
+// USE=""          IDREF
+// lineSegments="" MFVec2F [intializeOnly]
+// />
+void X3DImporter::readPolyline2D(XmlNode &node) {
+    std::string def, use;
+    std::list<aiVector2D> lineSegments;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getVector2DListAttribute(node, "lineSegments", lineSegments);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Polyline2D, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Polyline2D, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        //
+        // convert read point list of geometry object to line set.
+        //
+        std::list<aiVector3D> tlist;
+
+        // convert vec2 to vec3
+        for (std::list<aiVector2D>::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2)
+            tlist.push_back(aiVector3D(it2->x, it2->y, 0));
+
+        // convert point set to line set
+        X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices);
+        ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "Polyline2D");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <Polypoint2D
+// DEF=""   ID
+// USE=""   IDREF
+// point="" MFVec2F [inputOutput]
+// />
+void X3DImporter::readPolypoint2D(XmlNode &node) {
+    std::string def, use;
+    std::list<aiVector2D> point;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getVector2DListAttribute(node, "point", point);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Polypoint2D, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Polypoint2D, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        // convert vec2 to vec3
+        for (std::list<aiVector2D>::iterator it2 = point.begin(); it2 != point.end(); ++it2) {
+            ((X3DNodeElementGeometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0));
+        }
+
+        ((X3DNodeElementGeometry2D *)ne)->NumIndices = 1;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "Polypoint2D");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <Rectangle2D
+// DEF=""        ID
+// USE=""        IDREF
+// size="2 2"    SFVec2f [initializeOnly]
+// solid="false" SFBool  [initializeOnly]
+// />
+void X3DImporter::readRectangle2D(XmlNode &node) {
+    std::string def, use;
+    aiVector2D size(2, 2);
+    bool solid = false;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getVector2DAttribute(node, "size", size);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Rectangle2D, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Rectangle2D, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        float x1 = -size.x / 2.0f;
+        float x2 = size.x / 2.0f;
+        float y1 = -size.y / 2.0f;
+        float y2 = size.y / 2.0f;
+        std::list<aiVector3D> &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias.
+
+        vlist.push_back(aiVector3D(x2, y1, 0)); // 1st point
+        vlist.push_back(aiVector3D(x2, y2, 0)); // 2nd point
+        vlist.push_back(aiVector3D(x1, y2, 0)); // 3rd point
+        vlist.push_back(aiVector3D(x1, y1, 0)); // 4th point
+        ((X3DNodeElementGeometry2D *)ne)->Solid = solid;
+        ((X3DNodeElementGeometry2D *)ne)->NumIndices = 4;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "Rectangle2D");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <TriangleSet2D
+// DEF=""        ID
+// USE=""        IDREF
+// solid="false" SFBool  [initializeOnly]
+// vertices=""   MFVec2F [inputOutput]
+// />
+void X3DImporter::readTriangleSet2D(XmlNode &node) {
+    std::string def, use;
+    bool solid = false;
+    std::list<aiVector2D> vertices;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getVector2DListAttribute(node, "vertices", vertices);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleSet2D, ne);
+    } else {
+        if (vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle.");
+
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_TriangleSet2D, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        // convert vec2 to vec3
+        for (std::list<aiVector2D>::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2) {
+            ((X3DNodeElementGeometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0));
+        }
+
+        ((X3DNodeElementGeometry2D *)ne)->Solid = solid;
+        ((X3DNodeElementGeometry2D *)ne)->NumIndices = 3;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "TriangleSet2D");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 981 - 0
code/AssetLib/X3D/X3DImporter_Geometry3D.cpp

@@ -0,0 +1,981 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file   X3DImporter_Geometry3D.cpp
+/// \brief  Parsing data from nodes of "Geometry3D" set of X3D.
+/// \date   2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+#include "X3DXmlHelper.h"
+#include "X3DGeoHelper.h"
+
+// Header files, Assimp.
+#include <assimp/StandardShapes.h>
+
+namespace Assimp
+{
+
+// <Box
+// DEF=""       ID
+// USE=""       IDREF
+// size="2 2 2" SFVec3f [initializeOnly]
+// solid="true" SFBool  [initializeOnly]
+// />
+// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes.
+// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes
+// respectively and each component value shall be greater than zero.
+void X3DImporter::readBox(XmlNode &node) {
+    std::string def, use;
+    bool solid = true;
+    aiVector3D size(2, 2, 2);
+    X3DNodeElementBase* ne( nullptr );
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getVector3DAttribute(node, "size", size);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Box, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Box, mNodeElementCur);
+		if(!def.empty()) ne->ID = def;
+
+		X3DGeoHelper::rect_parallel_epiped(size, ((X3DNodeElementGeometry3D *)ne)->Vertices); // get quad list
+        ((X3DNodeElementGeometry3D *)ne)->Solid = solid;
+        ((X3DNodeElementGeometry3D *)ne)->NumIndices = 4;
+		// check for X3DMetadataObject childs.
+		if(!isNodeEmpty(node))
+			childrenReadMetadata(node, ne, "Box");
+		else
+			mNodeElementCur->Children.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Cone
+// DEF=""           ID
+// USE=""           IDREF
+// bottom="true"    SFBool [initializeOnly]
+// bottomRadius="1" SFloat [initializeOnly]
+// height="2"       SFloat [initializeOnly]
+// side="true"      SFBool [initializeOnly]
+// solid="true"     SFBool [initializeOnly]
+// />
+void X3DImporter::readCone(XmlNode &node) {
+    std::string use, def;
+    bool bottom = true;
+    float bottomRadius = 1;
+    float height = 2;
+    bool side = true;
+    bool solid = true;
+    X3DNodeElementBase* ne( nullptr );
+
+	MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+    XmlParser::getBoolAttribute(node, "side", side);
+    XmlParser::getBoolAttribute(node, "bottom", bottom);
+    XmlParser::getFloatAttribute(node, "height", height);
+    XmlParser::getFloatAttribute(node, "bottomRadius", bottomRadius);
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Cone, ne);
+	}
+	else
+	{
+		const unsigned int tess = 30;///TODO: IME tessellation factor through ai_property
+
+		std::vector<aiVector3D> tvec;// temp array for vertices.
+
+		// create and if needed - define new geometry object.
+		ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Cone, mNodeElementCur);
+		if(!def.empty()) ne->ID = def;
+
+		// make cone or parts according to flags.
+		if(side)
+		{
+			StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom);
+		}
+		else if(bottom)
+		{
+			StandardShapes::MakeCircle(bottomRadius, tess, tvec);
+			height = -(height / 2);
+			for(std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); ++it) it->y = height;// y - because circle made in oXZ.
+		}
+
+		// copy data from temp array
+        for (std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); ++it)
+            ((X3DNodeElementGeometry3D*)ne)->Vertices.push_back(*it);
+
+		((X3DNodeElementGeometry3D *)ne)->Solid = solid;
+        ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3;
+		// check for X3DMetadataObject childs.
+		if(!isNodeEmpty(node))
+			childrenReadMetadata(node, ne, "Cone");
+		else
+			mNodeElementCur->Children.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Cylinder
+// DEF=""        ID
+// USE=""        IDREF
+// bottom="true" SFBool [initializeOnly]
+// height="2"    SFloat [initializeOnly]
+// radius="1"    SFloat [initializeOnly]
+// side="true"   SFBool [initializeOnly]
+// solid="true"  SFBool [initializeOnly]
+// top="true"    SFBool [initializeOnly]
+// />
+void X3DImporter::readCylinder(XmlNode &node) {
+    std::string use, def;
+    bool bottom = true;
+    float height = 2;
+    float radius = 1;
+    bool side = true;
+    bool solid = true;
+    bool top = true;
+    X3DNodeElementBase* ne( nullptr );
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getFloatAttribute(node, "radius", radius);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+    XmlParser::getBoolAttribute(node, "bottom", bottom);
+    XmlParser::getBoolAttribute(node, "top", top);
+    XmlParser::getBoolAttribute(node, "side", side);
+    XmlParser::getFloatAttribute(node, "height", height);
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Cylinder, ne);
+	}
+	else
+	{
+		const unsigned int tess = 30;///TODO: IME tessellation factor through ai_property
+
+		std::vector<aiVector3D> tside;// temp array for vertices of side.
+		std::vector<aiVector3D> tcir;// temp array for vertices of circle.
+
+		// create and if needed - define new geometry object.
+		ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Cylinder, mNodeElementCur);
+		if(!def.empty()) ne->ID = def;
+
+		// make cilynder or parts according to flags.
+		if(side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true);
+
+		height /= 2;// height defined for whole cylinder, when creating top and bottom circle we are using just half of height.
+		if(top || bottom) StandardShapes::MakeCircle(radius, tess, tcir);
+		// copy data from temp arrays
+        std::list<aiVector3D> &vlist = ((X3DNodeElementGeometry3D *)ne)->Vertices; // just short alias.
+
+		for(std::vector<aiVector3D>::iterator it = tside.begin(); it != tside.end(); ++it) vlist.push_back(*it);
+
+		if(top)
+		{
+			for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); ++it)
+			{
+				(*it).y = height;// y - because circle made in oXZ.
+				vlist.push_back(*it);
+			}
+		}// if(top)
+
+		if(bottom)
+		{
+			for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); ++it)
+			{
+				(*it).y = -height;// y - because circle made in oXZ.
+				vlist.push_back(*it);
+			}
+		}// if(top)
+
+		((X3DNodeElementGeometry3D *)ne)->Solid = solid;
+        ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3;
+		// check for X3DMetadataObject childs.
+		if(!isNodeEmpty(node))
+			childrenReadMetadata(node, ne, "Cylinder");
+		else
+			mNodeElementCur->Children.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <ElevationGrid
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// creaseAngle="0"        SFloat  [initializeOnly]
+// height=""              MFloat  [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// xDimension="0"         SFInt32 [initializeOnly]
+// xSpacing="1.0"         SFloat  [initializeOnly]
+// zDimension="0"         SFInt32 [initializeOnly]
+// zSpacing="1.0"         SFloat  [initializeOnly]
+// >
+//   <!-- ColorNormalTexCoordContentModel -->
+// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single
+// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </ElevationGrid>
+// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described
+// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate
+// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero.
+// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals.
+void X3DImporter::readElevationGrid(XmlNode &node) {
+    std::string use, def;
+    bool ccw = true;
+    bool colorPerVertex = true;
+    float creaseAngle = 0;
+    std::vector<float> height;
+    bool normalPerVertex = true;
+    bool solid = true;
+    int32_t xDimension = 0;
+    float xSpacing = 1;
+    int32_t zDimension = 0;
+    float zSpacing = 1;
+    X3DNodeElementBase* ne( nullptr );
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+    XmlParser::getBoolAttribute(node, "ccw", ccw);
+    XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
+    XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
+    XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle);
+    X3DXmlHelper::getFloatArrayAttribute(node, "height", height);
+    XmlParser::getIntAttribute(node, "xDimension", xDimension);
+    XmlParser::getFloatAttribute(node, "xSpacing", xSpacing);
+    XmlParser::getIntAttribute(node, "zDimension", zDimension);
+    XmlParser::getFloatAttribute(node, "zSpacing", zSpacing);
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ElevationGrid, ne);
+	}
+	else
+	{
+		if((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in <ElevationGrid> must be grater than zero.");
+		if((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in <ElevationGrid> must be grater than zero.");
+        if ((size_t)(xDimension * zDimension) != height.size()) DeadlyImportError("Heights count must be equal to \"xDimension * zDimension\" in <ElevationGrid>");
+
+		// create and if needed - define new geometry object.
+		ne = new X3DNodeElementElevationGrid(X3DElemType::ENET_ElevationGrid, mNodeElementCur);
+		if(!def.empty()) ne->ID = def;
+
+		X3DNodeElementElevationGrid &grid_alias = *((X3DNodeElementElevationGrid *)ne); // create alias for conveience
+
+		{// create grid vertices list
+			std::vector<float>::const_iterator he_it = height.begin();
+
+			for(int32_t zi = 0; zi < zDimension; zi++)// rows
+			{
+				for(int32_t xi = 0; xi < xDimension; xi++)// columns
+				{
+					aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi);
+
+					grid_alias.Vertices.push_back(tvec);
+					++he_it;
+				}
+			}
+		}// END: create grid vertices list
+		//
+		// create faces list. In "coordIdx" format
+		//
+		// check if we have quads
+		if((xDimension < 2) || (zDimension < 2))// only one element in dimension is set, create line set.
+		{
+            ((X3DNodeElementElevationGrid *)ne)->NumIndices = 2; // will be holded as line set.
+			for(size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++)
+			{
+				grid_alias.CoordIdx.push_back(static_cast<int32_t>(i));
+				grid_alias.CoordIdx.push_back(static_cast<int32_t>(i + 1));
+				grid_alias.CoordIdx.push_back(-1);
+			}
+		}
+		else// two or more elements in every dimension is set. create quad set.
+		{
+            ((X3DNodeElementElevationGrid *)ne)->NumIndices = 4;
+			for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)// rows
+			{
+				for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)// columns
+				{
+					// points direction in face.
+					if(ccw)
+					{
+						// CCW:
+						//	3 2
+						//	0 1
+						grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi);
+						grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1));
+						grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1));
+						grid_alias.CoordIdx.push_back(fzi * xDimension + fxi);
+					}
+					else
+					{
+						// CW:
+						//	0 1
+						//	3 2
+						grid_alias.CoordIdx.push_back(fzi * xDimension + fxi);
+						grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1));
+						grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1));
+						grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi);
+					}// if(ccw) else
+
+					grid_alias.CoordIdx.push_back(-1);
+				}// for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)
+			}// for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)
+		}// if((xDimension < 2) || (zDimension < 2)) else
+
+		grid_alias.ColorPerVertex = colorPerVertex;
+		grid_alias.NormalPerVertex = normalPerVertex;
+		grid_alias.CreaseAngle = creaseAngle;
+		grid_alias.Solid = solid;
+        // check for child nodes
+        if(!isNodeEmpty(node))
+        {
+			ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+            	// check for X3DComposedGeometryNodes
+                if (currentChildName == "Color") readColor(currentChildNode);
+                else if (currentChildName == "ColorRGBA") readColorRGBA(currentChildNode);
+                else if (currentChildName == "Normal") readNormal(currentChildNode);
+                else if (currentChildName == "TextureCoordinate") readTextureCoordinate(currentChildNode);
+				// check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("ElevationGrid", currentChildNode);
+            }
+            ParseHelper_Node_Exit();
+		}// if(!mReader->isEmptyElement())
+		else
+		{
+			mNodeElementCur->Children.push_back(ne);// add made object as child to current element
+		}// if(!mReader->isEmptyElement()) else
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+template<typename TVector>
+static void GeometryHelper_Extrusion_CurveIsClosed(std::vector<TVector>& pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool& pCurveIsClosed)
+{
+    size_t cur_sz = pCurve.size();
+
+	pCurveIsClosed = false;
+	// for curve with less than four points checking is have no sense,
+	if(cur_sz < 4) return;
+
+	for(size_t s = 3, s_e = cur_sz; s < s_e; s++)
+	{
+		// search for first point of duplicated part.
+		if(pCurve[0] == pCurve[s])
+		{
+			bool found = true;
+
+			// check if tail(indexed by b2) is duplicate of head(indexed by b1).
+			for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++)
+			{
+				if(pCurve[b1] != pCurve[b2])
+				{// points not match: clear flag and break loop.
+					found = false;
+
+					break;
+				}
+			}// for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++)
+
+			// if duplicate tail is found then drop or not it depending on flags.
+			if(found)
+			{
+				pCurveIsClosed = true;
+				if(pDropTail)
+				{
+					if(!pRemoveLastPoint) s++;// prepare value for iterator's arithmetics.
+
+					pCurve.erase(pCurve.begin() + s, pCurve.end());// remove tail
+				}
+
+				break;
+			}// if(found)
+		}// if(pCurve[0] == pCurve[s])
+	}// for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++)
+}
+
+static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector<aiVector3D>& pSpine, const bool pSpine_Closed)
+{
+    const size_t spine_idx_last = pSpine.size() - 1;
+    aiVector3D tvec;
+
+	if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))// at first special cases
+	{
+		if(pSpine_Closed)
+		{// If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis.
+			// As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0])
+			// in tail are removed.
+			// So, last point in pSpine is a spine[n - 2]
+			tvec = pSpine[1] - pSpine[spine_idx_last];
+		}
+		else if(pSpine_PointIdx == 0)
+		{// The Y-axis used for the first point is the vector from spine[0] to spine[1]
+			tvec = pSpine[1] - pSpine[0];
+		}
+		else
+		{// The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is
+			// the spine[0].
+			tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1];
+		}
+	}// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))
+	else
+	{// For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]).
+		tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1];
+	}// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else
+
+	return tvec.Normalize();
+}
+
+static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector<aiVector3D>& pSpine, const bool pSpine_Closed,
+													const aiVector3D pVecZ_Prev)
+{
+    const aiVector3D zero_vec(0);
+    const size_t spine_idx_last = pSpine.size() - 1;
+
+    aiVector3D tvec;
+
+	// at first special cases
+	if(pSpine.size() < 3)// spine have not enough points for vector calculations.
+	{
+		tvec.Set(0, 0, 1);
+	}
+	else if(pSpine_PointIdx == 0)// special case: first point
+	{
+		if(pSpine_Closed)// for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate.
+		{
+			tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]);
+		}
+		else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z.
+		{
+			bool found = false;
+
+			// As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear)
+			// then the Z-axis for the first spine point with a defined Z-axis is used."
+			// Walk through spine and find Z.
+			for(size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++)
+			{
+				// (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1])
+				tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]);
+				found = !tvec.Equal(zero_vec);
+			}
+
+			// if entire spine are collinear then use OZ axis.
+			if(!found) tvec.Set(0, 0, 1);
+		}// if(pSpine_Closed) else
+	}// else if(pSpine_PointIdx == 0)
+	else if(pSpine_PointIdx == spine_idx_last)// special case: last point
+	{
+		if(pSpine_Closed)
+		{// do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2].
+			tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]);
+			// if taken spine vectors are collinear then use previous vector Z.
+			if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev;
+		}
+		else
+		{// vector Z for last point of not closed curve is previous vector Z.
+			tvec = pVecZ_Prev;
+		}
+	}
+	else// regular point
+	{
+		tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]);
+		// if taken spine vectors are collinear then use previous vector Z.
+		if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev;
+	}
+
+	// After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis
+	// is flipped (multiplied by -1).
+	if((tvec * pVecZ_Prev) < 0) tvec = -tvec;
+
+	return tvec.Normalize();
+}
+
+// <Extrusion
+// DEF=""                                 ID
+// USE=""                                 IDREF
+// beginCap="true"                        SFBool     [initializeOnly]
+// ccw="true"                             SFBool     [initializeOnly]
+// convex="true"                          SFBool     [initializeOnly]
+// creaseAngle="0.0"                      SFloat     [initializeOnly]
+// crossSection="1 1 1 -1 -1 -1 -1 1 1 1" MFVec2f    [initializeOnly]
+// endCap="true"                          SFBool     [initializeOnly]
+// orientation="0 0 1 0"                  MFRotation [initializeOnly]
+// scale="1 1"                            MFVec2f    [initializeOnly]
+// solid="true"                           SFBool     [initializeOnly]
+// spine="0 0 0 0 1 0"                    MFVec3f    [initializeOnly]
+// />
+void X3DImporter::readExtrusion(XmlNode &node) {
+    std::string use, def;
+    bool beginCap = true;
+    bool ccw = true;
+    bool convex = true;
+    float creaseAngle = 0;
+    std::vector<aiVector2D> crossSection;
+    bool endCap = true;
+    std::vector<float> orientation;
+    std::vector<aiVector2D> scale;
+    bool solid = true;
+    std::vector<aiVector3D> spine;
+    X3DNodeElementBase* ne( nullptr );
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "beginCap", beginCap);
+    XmlParser::getBoolAttribute(node, "ccw", ccw);
+    XmlParser::getBoolAttribute(node, "convex", convex);
+    XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle);
+    X3DXmlHelper::getVector2DArrayAttribute(node, "crossSection", crossSection);
+    XmlParser::getBoolAttribute(node, "endCap", endCap);
+    X3DXmlHelper::getFloatArrayAttribute(node, "orientation", orientation);
+    X3DXmlHelper::getVector2DArrayAttribute(node, "scale", scale);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+    X3DXmlHelper::getVector3DArrayAttribute(node, "spine", spine);
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Extrusion, ne);
+	}
+	else
+	{
+		//
+		// check if default values must be assigned
+		//
+		if(spine.size() == 0)
+		{
+			spine.resize(2);
+			spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0);
+		}
+		else if(spine.size() == 1)
+		{
+			throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points.");
+		}
+
+		if(crossSection.size() == 0)
+		{
+			crossSection.resize(5);
+			crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1);
+		}
+
+		{// orientation
+			size_t ori_size = orientation.size() / 4;
+
+			if(ori_size < spine.size())
+			{
+				float add_ori[4];// values that will be added
+
+				if(ori_size == 1)// if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points.
+				{
+					add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3];
+				}
+				else// else - use default values
+				{
+					add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0;
+				}
+
+				orientation.reserve(spine.size() * 4);
+				for(size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++)
+					orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]);
+			}
+
+			if(orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in <Extrusion> must has multiple four quantity of numbers.");
+		}// END: orientation
+
+		{// scale
+			if(scale.size() < spine.size())
+			{
+				aiVector2D add_sc;
+
+				if(scale.size() == 1)// if "scale" has one element then use it value for all spine points.
+					add_sc = scale[0];
+				else// else - use default values
+					add_sc.Set(1, 1);
+
+				scale.reserve(spine.size());
+				for(size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) scale.push_back(add_sc);
+			}
+		}// END: scale
+		//
+		// create and if needed - define new geometry object.
+		//
+		ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_Extrusion, mNodeElementCur);
+		if(!def.empty()) ne->ID = def;
+
+		X3DNodeElementIndexedSet &ext_alias = *((X3DNodeElementIndexedSet *)ne); // create alias for conveience
+		// assign part of input data
+		ext_alias.CCW = ccw;
+		ext_alias.Convex = convex;
+		ext_alias.CreaseAngle = creaseAngle;
+		ext_alias.Solid = solid;
+
+		//
+		// How we done it at all?
+		// 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector
+		// are applied vor every basis.
+		// 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position
+		// using relative spine point.
+		// 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if
+		// needed. While createing CootdIdx is taking in account CCW flag.
+		// 4. The last step: create Vertices list.
+		//
+        bool spine_closed;// flag: true if spine curve is closed.
+        bool cross_closed;// flag: true if cross curve is closed.
+        std::vector<aiMatrix3x3> basis_arr;// array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z.
+        std::vector<std::vector<aiVector3D> > pointset_arr;// array of point sets: cross curves.
+
+        // detect closed curves
+        GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed);// true - drop tail, true - remove duplicate end.
+        GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed);// true - drop tail, true - remove duplicate end.
+        // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface.
+        if(spine_closed)
+        {
+			beginCap |= endCap;
+			endCap = false;
+		}
+
+        {// 1. Calculate array of basises.
+			aiMatrix4x4 rotmat;
+			aiVector3D vecX(0), vecY(0), vecZ(0);
+
+			basis_arr.resize(spine.size());
+			for(size_t i = 0, i_e = spine.size(); i < i_e; i++)
+			{
+				aiVector3D tvec;
+
+				// get axises of basis.
+				vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed);
+				vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ);
+				vecX = (vecY ^ vecZ).Normalize();
+				// get rotation matrix and apply "orientation" to basis
+				aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat);
+				tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z;
+				tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z;
+				tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z;
+			}// for(size_t i = 0, i_e = spine.size(); i < i_e; i++)
+		}// END: 1. Calculate array of basises
+
+		{// 2. Create array of point sets.
+			aiMatrix4x4 scmat;
+			std::vector<aiVector3D> tcross(crossSection.size());
+
+			pointset_arr.resize(spine.size());
+			for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++)
+			{
+				aiVector3D tc23vec;
+
+				tc23vec.Set(scale[spi].x, 0, scale[spi].y);
+				aiMatrix4x4::Scaling(tc23vec, scmat);
+				for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++)
+				{
+					aiVector3D tvecX, tvecY, tvecZ;
+
+					tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y);
+					// apply scaling to point
+					tcross[cri] = scmat * tc23vec;
+					//
+					// transfer point to new basis
+					// calculate coordinate in new basis
+					tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x;
+					tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y;
+					tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z;
+					// apply new coordinates and translate it to spine point.
+					tcross[cri] = tvecX + tvecY + tvecZ + spine[spi];
+				}// for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++)
+
+				pointset_arr[spi] = tcross;// store transferred point set
+			}// for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++)
+		}// END: 2. Create array of point sets.
+
+		{// 3. Create CoordIdx.
+			// add caps if needed
+			if(beginCap)
+			{
+				// add cap as polygon. vertices of cap are places at begin, so just add numbers from zero.
+				for(size_t i = 0, i_e = crossSection.size(); i < i_e; i++) ext_alias.CoordIndex.push_back(static_cast<int32_t>(i));
+
+				// add delimiter
+				ext_alias.CoordIndex.push_back(-1);
+			}// if(beginCap)
+
+			if(endCap)
+			{
+				// add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset.
+				size_t beg = (pointset_arr.size() - 1) * crossSection.size();
+
+				for(size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) ext_alias.CoordIndex.push_back(static_cast<int32_t>(i));
+
+				// add delimiter
+				ext_alias.CoordIndex.push_back(-1);
+			}// if(beginCap)
+
+			// add quads
+			for(size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++)
+			{
+				const size_t cr_sz = crossSection.size();
+				const size_t cr_last = crossSection.size() - 1;
+
+				size_t right_col;// hold index basis for points of quad placed in right column;
+
+				if(spi != spi_e)
+					right_col = spi + 1;
+				else if(spine_closed)// if spine curve is closed then one more quad is needed: between first and last points of curve.
+					right_col = 0;
+				else
+					break;// if spine curve is not closed then break the loop, because spi is out of range for that type of spine.
+
+				for(size_t cri = 0; cri < cr_sz; cri++)
+				{
+					if(cri != cr_last)
+					{
+						MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex,
+											static_cast<int32_t>(spi * cr_sz + cri), 
+                                            static_cast<int32_t>(right_col * cr_sz + cri), 
+                                            static_cast<int32_t>(right_col * cr_sz + cri + 1), 
+                                            static_cast<int32_t>(spi * cr_sz + cri + 1));
+						// add delimiter
+						ext_alias.CoordIndex.push_back(-1);
+					}
+					else if(cross_closed)// if cross curve is closed then one more quad is needed: between first and last points of curve.
+					{
+						MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex,
+                                            static_cast<int32_t>(spi * cr_sz + cri), 
+                                            static_cast<int32_t>(right_col * cr_sz + cri), 
+                                            static_cast<int32_t>(right_col * cr_sz + 0), 
+                                            static_cast<int32_t>(spi * cr_sz + 0));
+						// add delimiter
+						ext_alias.CoordIndex.push_back(-1);
+					}
+				}// for(size_t cri = 0; cri < cr_sz; cri++)
+			}// for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++)
+		}// END: 3. Create CoordIdx.
+
+		{// 4. Create vertices list.
+			// just copy all vertices
+			for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++)
+			{
+				for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++)
+				{
+					ext_alias.Vertices.push_back(pointset_arr[spi][cri]);
+				}
+			}
+		}// END: 4. Create vertices list.
+//PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex);
+//PrintVectorSet("Ext. Vertices", ext_alias.Vertices);
+		// check for child nodes
+		if(!isNodeEmpty(node))
+			childrenReadMetadata(node, ne, "Extrusion");
+		else
+			mNodeElementCur->Children.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <IndexedFaceSet
+// DEF=""                         ID
+// USE=""                         IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorIndex=""          MFInt32 [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// convex="true"          SFBool  [initializeOnly]
+// coordIndex=""          MFInt32 [initializeOnly]
+// creaseAngle="0"        SFFloat [initializeOnly]
+// normalIndex=""         MFInt32 [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// texCoordIndex=""       MFInt32 [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </IndexedFaceSet>
+void X3DImporter::readIndexedFaceSet(XmlNode &node) {
+    std::string use, def;
+    bool ccw = true;
+    std::vector<int32_t> colorIndex;
+    bool colorPerVertex = true;
+    bool convex = true;
+    std::vector<int32_t> coordIndex;
+    float creaseAngle = 0;
+    std::vector<int32_t> normalIndex;
+    bool normalPerVertex = true;
+    bool solid = true;
+    std::vector<int32_t> texCoordIndex;
+    X3DNodeElementBase* ne( nullptr );
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "ccw", ccw);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "colorIndex", colorIndex);
+    XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
+    XmlParser::getBoolAttribute(node, "convex", convex);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "coordIndex", coordIndex);
+    XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "normalIndex", normalIndex);
+    XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "texCoordIndex", texCoordIndex);
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedFaceSet, ne);
+	}
+	else
+	{
+		// check data
+		if(coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute.");
+
+		// create and if needed - define new geometry object.
+		ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedFaceSet, mNodeElementCur);
+		if(!def.empty()) ne->ID = def;
+
+		X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne);
+
+		ne_alias.CCW = ccw;
+		ne_alias.ColorIndex = colorIndex;
+		ne_alias.ColorPerVertex = colorPerVertex;
+		ne_alias.Convex = convex;
+		ne_alias.CoordIndex = coordIndex;
+		ne_alias.CreaseAngle = creaseAngle;
+		ne_alias.NormalIndex = normalIndex;
+		ne_alias.NormalPerVertex = normalPerVertex;
+		ne_alias.Solid = solid;
+		ne_alias.TexCoordIndex = texCoordIndex;
+        // check for child nodes
+        if(!isNodeEmpty(node))
+        {
+			ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                // check for X3DComposedGeometryNodes
+                if (currentChildName == "Color") readColor(currentChildNode);
+                else if (currentChildName == "ColorRGBA") readColorRGBA(currentChildNode);
+                else if (currentChildName == "Coordinate") readCoordinate(currentChildNode);
+                else if (currentChildName == "Normal") readNormal(currentChildNode);
+                else if (currentChildName == "TextureCoordinate") readTextureCoordinate(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("IndexedFaceSet", currentChildNode);
+            }
+			ParseHelper_Node_Exit();
+		}// if(!isNodeEmpty(node))
+		else
+		{
+			mNodeElementCur->Children.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Sphere
+// DEF=""       ID
+// USE=""       IDREF
+// radius="1"   SFloat [initializeOnly]
+// solid="true" SFBool [initializeOnly]
+// />
+void X3DImporter::readSphere(XmlNode &node) {
+    std::string use, def;
+    ai_real radius = 1;
+    bool solid = true;
+    X3DNodeElementBase* ne( nullptr );
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getFloatAttribute(node, "radius", radius);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Sphere, ne);
+	}
+	else
+	{
+		const unsigned int tess = 3;///TODO: IME tessellation factor through ai_property
+
+		std::vector<aiVector3D> tlist;
+
+		// create and if needed - define new geometry object.
+		ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Sphere, mNodeElementCur);
+		if(!def.empty()) ne->ID = def;
+
+		StandardShapes::MakeSphere(tess, tlist);
+		// copy data from temp array and apply scale
+		for(std::vector<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); ++it)
+		{
+            ((X3DNodeElementGeometry3D *)ne)->Vertices.push_back(*it * radius);
+		}
+
+		((X3DNodeElementGeometry3D *)ne)->Solid = solid;
+        ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3;
+		// check for X3DMetadataObject childs.
+		if(!isNodeEmpty(node))
+			childrenReadMetadata(node, ne, "Sphere");
+		else
+			mNodeElementCur->Children.push_back(ne);// add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 273 - 0
code/AssetLib/X3D/X3DImporter_Group.cpp

@@ -0,0 +1,273 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file   X3DImporter_Group.cpp
+/// \brief  Parsing data from nodes of "Grouping" set of X3D.
+/// date   2015-2016
+/// author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+#include "X3DXmlHelper.h"
+
+namespace Assimp {
+
+// <Group
+// DEF=""              ID
+// USE=""              IDREF
+// bboxCenter="0 0 0"  SFVec3f [initializeOnly]
+// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
+// >
+//    <\!-- ChildContentModel -->
+// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
+// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
+// precise palette of legal nodes that are available depends on assigned profile and components.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </Group>
+// A Group node contains children nodes without introducing a new transformation. It is equivalent to a Transform node containing an identity transform.
+void X3DImporter::startReadGroup(XmlNode &node) {
+    std::string def, use;
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        X3DNodeElementBase *ne;
+
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne);
+    } else {
+        ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children.
+        // at this place new group mode created and made current, so we can name it.
+        if (!def.empty()) mNodeElementCur->ID = def;
+        // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
+
+        // for empty element exit from node in that place
+        if (isNodeEmpty(node)) ParseHelper_Node_Exit();
+    } // if(!use.empty()) else
+}
+
+void X3DImporter::endReadGroup() {
+    ParseHelper_Node_Exit(); // go up in scene graph
+}
+
+// <StaticGroup
+// DEF=""              ID
+// USE=""              IDREF
+// bboxCenter="0 0 0"  SFVec3f [initializeOnly]
+// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
+// >
+//    <\!-- ChildContentModel -->
+// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
+// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
+// precise palette of legal nodes that are available depends on assigned profile and components.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </StaticGroup>
+// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or
+// contain any USE references outside the StaticGroup.
+void X3DImporter::startReadStaticGroup(XmlNode &node) {
+    std::string def, use;
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        X3DNodeElementBase *ne;
+
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne);
+    } else {
+        ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children.
+        // at this place new group mode created and made current, so we can name it.
+        if (!def.empty()) mNodeElementCur->ID = def;
+        // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
+
+        // for empty element exit from node in that place
+        if (isNodeEmpty(node)) ParseHelper_Node_Exit();
+    } // if(!use.empty()) else
+}
+
+void X3DImporter::endReadStaticGroup() {
+    ParseHelper_Node_Exit(); // go up in scene graph
+}
+
+// <Switch
+// DEF=""              ID
+// USE=""              IDREF
+// bboxCenter="0 0 0"  SFVec3f [initializeOnly]
+// bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
+// whichChoice="-1"    SFInt32 [inputOutput]
+// >
+//    <\!-- ChildContentModel -->
+// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
+// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
+// precise palette of legal nodes that are available depends on assigned profile and components.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </Switch>
+// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child
+// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing
+// is chosen.
+void X3DImporter::startReadSwitch(XmlNode &node) {
+    std::string def, use;
+    int32_t whichChoice = -1;
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getIntAttribute(node, "whichChoice", whichChoice);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        X3DNodeElementBase *ne;
+
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne);
+    } else {
+        ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children.
+        // at this place new group mode created and made current, so we can name it.
+        if (!def.empty()) mNodeElementCur->ID = def;
+
+        // also set values specific to this type of group
+        ((X3DNodeElementGroup *)mNodeElementCur)->UseChoice = true;
+        ((X3DNodeElementGroup *)mNodeElementCur)->Choice = whichChoice;
+        // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
+
+        // for empty element exit from node in that place
+        if (isNodeEmpty(node)) ParseHelper_Node_Exit();
+    } // if(!use.empty()) else
+}
+
+void X3DImporter::endReadSwitch() {
+    // just exit from node. Defined choice will be accepted at postprocessing stage.
+    ParseHelper_Node_Exit(); // go up in scene graph
+}
+
+// <Transform
+// DEF=""                     ID
+// USE=""                     IDREF
+// bboxCenter="0 0 0"         SFVec3f    [initializeOnly]
+// bboxSize="-1 -1 -1"        SFVec3f    [initializeOnly]
+// center="0 0 0"             SFVec3f    [inputOutput]
+// rotation="0 0 1 0"         SFRotation [inputOutput]
+// scale="1 1 1"              SFVec3f    [inputOutput]
+// scaleOrientation="0 0 1 0" SFRotation [inputOutput]
+// translation="0 0 0"        SFVec3f    [inputOutput]
+// >
+//    <\!-- ChildContentModel -->
+// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
+// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
+// precise palette of legal nodes that are available depends on assigned profile and components.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </Transform>
+// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors.
+// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate
+// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the
+// equivalent transformation matrices,
+//   P' = T * C * R * SR * S * -SR * -C * P
+void X3DImporter::startReadTransform(XmlNode &node) {
+    aiVector3D center(0, 0, 0);
+    float rotation[4] = { 0, 0, 1, 0 };
+    aiVector3D scale(1, 1, 1); // A value of zero indicates that any child geometry shall not be displayed
+    float scale_orientation[4] = { 0, 0, 1, 0 };
+    aiVector3D translation(0, 0, 0);
+    aiMatrix4x4 matr, tmatr;
+    std::string use, def;
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getVector3DAttribute(node, "center", center);
+    X3DXmlHelper::getVector3DAttribute(node, "scale", scale);
+    X3DXmlHelper::getVector3DAttribute(node, "translation", translation);
+    std::vector<float> tvec;
+    if (X3DXmlHelper::getFloatArrayAttribute(node, "rotation", tvec)) {
+        if (tvec.size() != 4) throw DeadlyImportError("<Transform>: rotation vector must have 4 elements.");
+        memcpy(rotation, tvec.data(), sizeof(rotation));
+        tvec.clear();
+    }
+    if (X3DXmlHelper::getFloatArrayAttribute(node, "scaleOrientation", tvec)) {
+        if (tvec.size() != 4) throw DeadlyImportError("<Transform>: scaleOrientation vector must have 4 elements.");
+        memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation));
+        tvec.clear();
+    }
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        X3DNodeElementBase *ne(nullptr);
+
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne);
+    } else {
+        ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children.
+        // at this place new group mode created and made current, so we can name it.
+        if (!def.empty()) {
+            mNodeElementCur->ID = def;
+        }
+
+        //
+        // also set values specific to this type of group
+        //
+        // calculate transformation matrix
+        aiMatrix4x4::Translation(translation, matr); // T
+        aiMatrix4x4::Translation(center, tmatr); // C
+        matr *= tmatr;
+        aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr); // R
+        matr *= tmatr;
+        aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // SR
+        matr *= tmatr;
+        aiMatrix4x4::Scaling(scale, tmatr); // S
+        matr *= tmatr;
+        aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // -SR
+        matr *= tmatr;
+        aiMatrix4x4::Translation(-center, tmatr); // -C
+        matr *= tmatr;
+        // and assign it
+        ((X3DNodeElementGroup *)mNodeElementCur)->Transformation = matr;
+        // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
+
+        // for empty element exit from node in that place
+        if (isNodeEmpty(node)) {
+            ParseHelper_Node_Exit();
+        }
+    } // if(!use.empty()) else
+}
+
+void X3DImporter::endReadTransform() {
+    ParseHelper_Node_Exit(); // go up in scene graph
+}
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 270 - 0
code/AssetLib/X3D/X3DImporter_Light.cpp

@@ -0,0 +1,270 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file   X3DImporter_Light.cpp
+/// \brief  Parsing data from nodes of "Lighting" set of X3D.
+/// date   2015-2016
+/// author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+#include "X3DXmlHelper.h"
+#include <assimp/StringUtils.h>
+
+namespace Assimp {
+
+// <DirectionalLight
+// DEF=""               ID
+// USE=""               IDREF
+// ambientIntensity="0" SFFloat [inputOutput]
+// color="1 1 1"        SFColor [inputOutput]
+// direction="0 0 -1"   SFVec3f [inputOutput]
+// global="false"       SFBool  [inputOutput]
+// intensity="1"        SFFloat [inputOutput]
+// on="true"            SFBool  [inputOutput]
+// />
+void X3DImporter::readDirectionalLight(XmlNode &node) {
+    std::string def, use;
+    float ambientIntensity = 0;
+    aiColor3D color(1, 1, 1);
+    aiVector3D direction(0, 0, -1);
+    bool global = false;
+    float intensity = 1;
+    bool on = true;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity);
+    X3DXmlHelper::getColor3DAttribute(node, "color", color);
+    X3DXmlHelper::getVector3DAttribute(node, "direction", direction);
+    XmlParser::getBoolAttribute(node, "global", global);
+    XmlParser::getFloatAttribute(node, "intensity", intensity);
+    XmlParser::getBoolAttribute(node, "on", on);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_DirectionalLight, ne);
+    } else {
+        if (on) {
+            // create and if needed - define new geometry object.
+            ne = new X3DNodeElementLight(X3DElemType::ENET_DirectionalLight, mNodeElementCur);
+            if (!def.empty())
+                ne->ID = def;
+            else
+                ne->ID = "DirectionalLight_" + ai_to_string((size_t)ne); // make random name
+
+            ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity;
+            ((X3DNodeElementLight *)ne)->Color = color;
+            ((X3DNodeElementLight *)ne)->Direction = direction;
+            ((X3DNodeElementLight *)ne)->Global = global;
+            ((X3DNodeElementLight *)ne)->Intensity = intensity;
+            // Assimp want a node with name similar to a light. "Why? I don't no." )
+            ParseHelper_Group_Begin(false);
+
+            mNodeElementCur->ID = ne->ID; // assign name to node and return to light element.
+            ParseHelper_Node_Exit();
+            // check for child nodes
+            if (!isNodeEmpty(node))
+                childrenReadMetadata(node, ne, "DirectionalLight");
+            else
+                mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+            NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+        } // if(on)
+    } // if(!use.empty()) else
+}
+
+// <PointLight
+// DEF=""               ID
+// USE=""               IDREF
+// ambientIntensity="0" SFFloat [inputOutput]
+// attenuation="1 0 0"  SFVec3f [inputOutput]
+// color="1 1 1"        SFColor [inputOutput]
+// global="true"        SFBool  [inputOutput]
+// intensity="1"        SFFloat [inputOutput]
+// location="0 0 0"     SFVec3f [inputOutput]
+// on="true"            SFBool  [inputOutput]
+// radius="100"         SFFloat [inputOutput]
+// />
+void X3DImporter::readPointLight(XmlNode &node) {
+    std::string def, use;
+    float ambientIntensity = 0;
+    aiVector3D attenuation(1, 0, 0);
+    aiColor3D color(1, 1, 1);
+    bool global = true;
+    float intensity = 1;
+    aiVector3D location(0, 0, 0);
+    bool on = true;
+    float radius = 100;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity);
+    X3DXmlHelper::getVector3DAttribute(node, "attenuation", attenuation);
+    X3DXmlHelper::getColor3DAttribute(node, "color", color);
+    XmlParser::getBoolAttribute(node, "global", global);
+    XmlParser::getFloatAttribute(node, "intensity", intensity);
+    X3DXmlHelper::getVector3DAttribute(node, "location", location);
+    XmlParser::getBoolAttribute(node, "on", on);
+    XmlParser::getFloatAttribute(node, "radius", radius);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_PointLight, ne);
+    } else {
+        if (on) {
+            // create and if needed - define new geometry object.
+            ne = new X3DNodeElementLight(X3DElemType::ENET_PointLight, mNodeElementCur);
+            if (!def.empty()) ne->ID = def;
+
+            ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity;
+            ((X3DNodeElementLight *)ne)->Attenuation = attenuation;
+            ((X3DNodeElementLight *)ne)->Color = color;
+            ((X3DNodeElementLight *)ne)->Global = global;
+            ((X3DNodeElementLight *)ne)->Intensity = intensity;
+            ((X3DNodeElementLight *)ne)->Location = location;
+            ((X3DNodeElementLight *)ne)->Radius = radius;
+            // Assimp want a node with name similar to a light. "Why? I don't no." )
+            ParseHelper_Group_Begin(false);
+            // make random name
+            if (ne->ID.empty()) ne->ID = "PointLight_" + ai_to_string((size_t)ne);
+
+            mNodeElementCur->ID = ne->ID; // assign name to node and return to light element.
+            ParseHelper_Node_Exit();
+            // check for child nodes
+            if (!isNodeEmpty(node))
+                childrenReadMetadata(node, ne, "PointLight");
+            else
+                mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+            NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+        } // if(on)
+    } // if(!use.empty()) else
+}
+
+// <SpotLight
+// DEF=""                 ID
+// USE=""                 IDREF
+// ambientIntensity="0"   SFFloat [inputOutput]
+// attenuation="1 0 0"    SFVec3f [inputOutput]
+// beamWidth="0.7854"     SFFloat [inputOutput]
+// color="1 1 1"          SFColor [inputOutput]
+// cutOffAngle="1.570796" SFFloat [inputOutput]
+// direction="0 0 -1"     SFVec3f [inputOutput]
+// global="true"          SFBool  [inputOutput]
+// intensity="1"          SFFloat [inputOutput]
+// location="0 0 0"       SFVec3f [inputOutput]
+// on="true"              SFBool  [inputOutput]
+// radius="100"           SFFloat [inputOutput]
+// />
+void X3DImporter::readSpotLight(XmlNode &node) {
+    std::string def, use;
+    float ambientIntensity = 0;
+    aiVector3D attenuation(1, 0, 0);
+    float beamWidth = 0.7854f;
+    aiColor3D color(1, 1, 1);
+    float cutOffAngle = 1.570796f;
+    aiVector3D direction(0, 0, -1);
+    bool global = true;
+    float intensity = 1;
+    aiVector3D location(0, 0, 0);
+    bool on = true;
+    float radius = 100;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity);
+    X3DXmlHelper::getVector3DAttribute(node, "attenuation", attenuation);
+    XmlParser::getFloatAttribute(node, "beamWidth", beamWidth);
+    X3DXmlHelper::getColor3DAttribute(node, "color", color);
+    XmlParser::getFloatAttribute(node, "cutOffAngle", cutOffAngle);
+    X3DXmlHelper::getVector3DAttribute(node, "direction", direction);
+    XmlParser::getBoolAttribute(node, "global", global);
+    XmlParser::getFloatAttribute(node, "intensity", intensity);
+    X3DXmlHelper::getVector3DAttribute(node, "location", location);
+    XmlParser::getBoolAttribute(node, "on", on);
+    XmlParser::getFloatAttribute(node, "radius", radius);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_SpotLight, ne);
+    } else {
+        if (on) {
+            // create and if needed - define new geometry object.
+            ne = new X3DNodeElementLight(X3DElemType::ENET_SpotLight, mNodeElementCur);
+            if (!def.empty()) ne->ID = def;
+
+            if (beamWidth > cutOffAngle) beamWidth = cutOffAngle;
+
+            ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity;
+            ((X3DNodeElementLight *)ne)->Attenuation = attenuation;
+            ((X3DNodeElementLight *)ne)->BeamWidth = beamWidth;
+            ((X3DNodeElementLight *)ne)->Color = color;
+            ((X3DNodeElementLight *)ne)->CutOffAngle = cutOffAngle;
+            ((X3DNodeElementLight *)ne)->Direction = direction;
+            ((X3DNodeElementLight *)ne)->Global = global;
+            ((X3DNodeElementLight *)ne)->Intensity = intensity;
+            ((X3DNodeElementLight *)ne)->Location = location;
+            ((X3DNodeElementLight *)ne)->Radius = radius;
+
+            // Assimp want a node with name similar to a light. "Why? I don't no." )
+            ParseHelper_Group_Begin(false);
+            // make random name
+            if (ne->ID.empty()) ne->ID = "SpotLight_" + ai_to_string((size_t)ne);
+
+            mNodeElementCur->ID = ne->ID; // assign name to node and return to light element.
+            ParseHelper_Node_Exit();
+            // check for child nodes
+            if (!isNodeEmpty(node))
+                childrenReadMetadata(node, ne, "SpotLight");
+            else
+                mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+            NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+        } // if(on)
+    } // if(!use.empty()) else
+}
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 110 - 0
code/AssetLib/X3D/X3DImporter_Macro.hpp

@@ -0,0 +1,110 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file X3DImporter_Macro.hpp
+/// \brief Useful macrodefines.
+/// \date 2015-2016
+/// \author [email protected]
+
+#ifndef X3DIMPORTER_MACRO_HPP_INCLUDED
+#define X3DIMPORTER_MACRO_HPP_INCLUDED
+
+/// \def MACRO_USE_CHECKANDAPPLY(pDEF, pUSE, pNE)
+/// Used for regular checking while attribute "USE" is defined.
+/// \param [in] pNode - pugi xml node to read.
+/// \param [in] pDEF - string holding "DEF" value.
+/// \param [in] pUSE - string holding "USE" value.
+/// \param [in] pType - type of element to find.
+/// \param [out] pNE - pointer to found node element.
+#define MACRO_USE_CHECKANDAPPLY(pNode, pDEF, pUSE, pType, pNE)                                    \
+    do {                                                                                          \
+    checkNodeMustBeEmpty(pNode);                                                                  \
+    if (!pDEF.empty()) Throw_DEF_And_USE(pNode.name());                                           \
+    if (!FindNodeElement(pUSE, X3DElemType::pType, &pNE)) Throw_USE_NotFound(pNode.name(), pUSE); \
+    mNodeElementCur->Children.push_back(pNE); /* add found object as child to current element */  \
+    } while (false)
+
+/// \def MACRO_ATTRREAD_CHECKUSEDEF_RET
+/// Compact variant for checking "USE" and "DEF".
+/// \param [in] pNode - pugi xml node to read.
+/// \param [out] pDEF_Var - output variable name for "DEF" value.
+/// \param [out] pUSE_Var - output variable name for "USE" value.
+#define MACRO_ATTRREAD_CHECKUSEDEF_RET(pNode, pDEF_Var, pUSE_Var) \
+    do {                                                          \
+    XmlParser::getStdStrAttribute(pNode, "def", pDEF_Var);        \
+    XmlParser::getStdStrAttribute(pNode, "use", pUSE_Var);        \
+    } while (false)
+
+/// \def MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4)
+/// Add points as quad. Means that pP1..pP4 set in CCW order.
+#define MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) \
+    do {                                                            \
+        if (pCCW) {                                                 \
+            pOut.push_back(pIn[pP1]);                               \
+            pOut.push_back(pIn[pP2]);                               \
+            pOut.push_back(pIn[pP3]);                               \
+            pOut.push_back(pIn[pP4]);                               \
+        } else {                                                    \
+            pOut.push_back(pIn[pP4]);                               \
+            pOut.push_back(pIn[pP3]);                               \
+            pOut.push_back(pIn[pP2]);                               \
+            pOut.push_back(pIn[pP1]);                               \
+        }                                                           \
+    } while (false)
+
+/// \def MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4)
+/// Add points as quad. Means that pP1..pP4 set in CCW order.
+#define MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4) \
+    do {                                                    \
+        if (pCCW) {                                         \
+            pOut.push_back(pP1);                            \
+            pOut.push_back(pP2);                            \
+            pOut.push_back(pP3);                            \
+            pOut.push_back(pP4);                            \
+        } else {                                            \
+            pOut.push_back(pP4);                            \
+            pOut.push_back(pP3);                            \
+            pOut.push_back(pP2);                            \
+            pOut.push_back(pP1);                            \
+        }                                                   \
+    } while (false)
+
+#endif // X3DIMPORTER_MACRO_HPP_INCLUDED

+ 255 - 0
code/AssetLib/X3D/X3DImporter_Metadata.cpp

@@ -0,0 +1,255 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file   X3DImporter_Metadata.cpp
+/// \brief  Parsing data from nodes of "Metadata" set of X3D.
+/// \date   2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+#include "X3DXmlHelper.h"
+
+namespace Assimp {
+
+bool X3DImporter::checkForMetadataNode(XmlNode &node) {
+    const std::string &name = node.name();
+    if (name == "MetadataBoolean") {
+        readMetadataBoolean(node);
+    } else if (name == "MetadataDouble") {
+        readMetadataDouble(node);
+    } else if (name == "MetadataFloat") {
+        readMetadataFloat(node);
+    } else if (name == "MetadataInteger") {
+        readMetadataInteger(node);
+    } else if (name == "MetadataSet") {
+        readMetadataSet(node);
+    } else if (name == "MetadataString") {
+        readMetadataString(node);
+    } else
+        return false;
+    return true;
+}
+
+void X3DImporter::childrenReadMetadata(XmlNode &node, X3DNodeElementBase *pParentElement, const std::string &pNodeName) {
+    ParseHelper_Node_Enter(pParentElement);
+    for (auto childNode : node.children()) {
+        if (!checkForMetadataNode(childNode)) skipUnsupportedNode(pNodeName, childNode);
+    }
+    ParseHelper_Node_Exit();
+}
+
+/// \def MACRO_METADATA_FINDCREATE(pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaName)
+/// Find element by "USE" or create new one.
+/// \param [in] pNode - pugi xml node to read.
+/// \param [in] pDEF_Var - variable name with "DEF" value.
+/// \param [in] pUSE_Var - variable name with "USE" value.
+/// \param [in] pReference - variable name with "reference" value.
+/// \param [in] pValue - variable name with "value" value.
+/// \param [in, out] pNE - pointer to node element.
+/// \param [in] pMetaClass - Class of node.
+/// \param [in] pMetaName - Name of node.
+/// \param [in] pType - type of element to find.
+#define MACRO_METADATA_FINDCREATE(pNode, pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaClass, pMetaName, pType)                            \
+    /* if "USE" defined then find already defined element. */                                                                                  \
+    if (!pUSE_Var.empty()) {                                                                                                                   \
+        MACRO_USE_CHECKANDAPPLY(pNode, pDEF_Var, pUSE_Var, pType, pNE);                                                                        \
+    } else {                                                                                                                                   \
+        pNE = new pMetaClass(mNodeElementCur);                                                                                                 \
+        if (!pDEF_Var.empty()) pNE->ID = pDEF_Var;                                                                                             \
+                                                                                                                                               \
+        ((pMetaClass *)pNE)->Reference = pReference;                                                                                           \
+        ((pMetaClass *)pNE)->Value = pValue;                                                                                                   \
+        /* also metadata node can contain childs */                                                                                            \
+        if (!isNodeEmpty(pNode))                                                                                                               \
+            childrenReadMetadata(pNode, pNE, pMetaName); /* in that case node element will be added to child elements list of current node. */ \
+        else                                                                                                                                   \
+            mNodeElementCur->Children.push_back(pNE); /* else - add element to child list manually */                                          \
+                                                                                                                                               \
+        NodeElement_List.push_back(pNE); /* add new element to elements list. */                                                               \
+    } /* if(!pUSE_Var.empty()) else */                                                                                                         \
+                                                                                                                                               \
+    do {                                                                                                                                       \
+    } while (false)
+
+// <MetadataBoolean
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString [inputOutput]
+// reference="" SFString [inputOutput]
+// value=""     MFBool   [inputOutput]
+// />
+void X3DImporter::readMetadataBoolean(XmlNode &node) {
+    std::string def, use;
+    std::string name, reference;
+    std::vector<bool> value;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getStdStrAttribute(node, "name", name);
+    XmlParser::getStdStrAttribute(node, "reference", reference);
+    X3DXmlHelper::getBooleanArrayAttribute(node, "value", value);
+
+    MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaBoolean, "MetadataBoolean", ENET_MetaBoolean);
+}
+
+// <MetadataDouble
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString [inputOutput]
+// reference="" SFString [inputOutput]
+// value=""     MFDouble [inputOutput]
+// />
+void X3DImporter::readMetadataDouble(XmlNode &node) {
+    std::string def, use;
+    std::string name, reference;
+    std::vector<double> value;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getStdStrAttribute(node, "name", name);
+    XmlParser::getStdStrAttribute(node, "reference", reference);
+    X3DXmlHelper::getDoubleArrayAttribute(node, "value", value);
+
+    MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaDouble, "MetadataDouble", ENET_MetaDouble);
+}
+
+// <MetadataFloat
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString [inputOutput]
+// reference="" SFString [inputOutput]
+// value=""     MFFloat  [inputOutput]
+// />
+void X3DImporter::readMetadataFloat(XmlNode &node) {
+    std::string def, use;
+    std::string name, reference;
+    std::vector<float> value;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getStdStrAttribute(node, "name", name);
+    XmlParser::getStdStrAttribute(node, "reference", reference);
+    X3DXmlHelper::getFloatArrayAttribute(node, "value", value);
+
+    MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaFloat, "MetadataFloat", ENET_MetaFloat);
+}
+
+// <MetadataInteger
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString  [inputOutput]
+// reference="" SFString  [inputOutput]
+// value=""     MFInteger [inputOutput]
+// />
+void X3DImporter::readMetadataInteger(XmlNode &node) {
+    std::string def, use;
+    std::string name, reference;
+    std::vector<int32_t> value;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getStdStrAttribute(node, "name", name);
+    XmlParser::getStdStrAttribute(node, "reference", reference);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "value", value);
+
+    MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaInt, "MetadataInteger", ENET_MetaInteger);
+}
+
+// <MetadataSet
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString [inputOutput]
+// reference="" SFString [inputOutput]
+// />
+void X3DImporter::readMetadataSet(XmlNode &node) {
+    std::string def, use;
+    std::string name, reference;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getStdStrAttribute(node, "name", name);
+    XmlParser::getStdStrAttribute(node, "reference", reference);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_MetaSet, ne);
+    } else {
+        ne = new X3DNodeElementMetaSet(mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        ((X3DNodeElementMetaSet *)ne)->Reference = reference;
+        // also metadata node can contain childs
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "MetadataSet");
+        else
+            mNodeElementCur->Children.push_back(ne); // made object as child to current element
+
+        NodeElement_List.push_back(ne); // add new element to elements list.
+    } // if(!use.empty()) else
+}
+
+// <MetadataString
+// DEF=""       ID
+// USE=""       IDREF
+// name=""      SFString [inputOutput]
+// reference="" SFString [inputOutput]
+// value=""     MFString [inputOutput]
+// />
+void X3DImporter::readMetadataString(XmlNode &node) {
+    std::string def, use;
+    std::string name, reference;
+    std::vector<std::string> value;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getStdStrAttribute(node, "name", name);
+    XmlParser::getStdStrAttribute(node, "reference", reference);
+    X3DXmlHelper::getStringArrayAttribute(node, "value", value);
+
+    MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaString, "MetadataString", ENET_MetaString);
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 125 - 0
code/AssetLib/X3D/X3DImporter_Networking.cpp

@@ -0,0 +1,125 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file   X3DImporter_Networking.cpp
+/// \brief  Parsing data from nodes of "Networking" set of X3D.
+/// date   2015-2016
+/// author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+#include "X3DXmlHelper.h"
+
+// Header files, Assimp.
+#include <assimp/DefaultIOSystem.h>
+
+//#include <regex>
+
+namespace Assimp {
+
+//static std::regex pattern_parentDir(R"((^|/)[^/]+/../)");
+static std::string parentDir("/../");
+
+// <Inline
+// DEF=""              ID
+// USE=""              IDREF
+// bboxCenter="0 0 0"  SFVec3f  [initializeOnly]
+// bboxSize="-1 -1 -1" SFVec3f  [initializeOnly]
+// load="true"         SFBool   [inputOutput]
+// url=""              MFString [inputOutput]
+// />
+void X3DImporter::readInline(XmlNode &node) {
+    std::string def, use;
+    bool load = true;
+    std::list<std::string> url;
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "load", load);
+    X3DXmlHelper::getStringListAttribute(node, "url", url);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        X3DNodeElementBase *ne;
+
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne);
+    } else {
+        ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children.
+        // at this place new group mode created and made current, so we can name it.
+        if (!def.empty()) mNodeElementCur->ID = def;
+
+        if (load && !url.empty()) {
+            std::string full_path = mpIOHandler->CurrentDirectory() + url.front();
+
+            //full_path = std::regex_replace(full_path, pattern_parentDir, "$1");
+            for (std::string::size_type pos = full_path.find(parentDir); pos != std::string::npos; pos = full_path.find(parentDir, pos)) {
+                if (pos > 0) {
+                    std::string::size_type pos2 = full_path.rfind('/', pos - 1);
+                    if (pos2 != std::string::npos) {
+                        full_path.erase(pos2, pos - pos2 + 3);
+                        pos = pos2;
+                    } else {
+                        full_path.erase(0, pos + 4);
+                        pos = 0;
+                    }
+                } else {
+                    pos += 3;
+                }
+            }
+            // Attribute "url" can contain list of strings. But we need only one - first.
+            std::string::size_type slashPos = full_path.find_last_of("\\/");
+            mpIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : full_path.substr(0, slashPos + 1));
+            ParseFile(full_path, mpIOHandler);
+            mpIOHandler->PopDirectory();
+        }
+
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node)) childrenReadMetadata(node, mNodeElementCur, "Inline");
+
+        // exit from node in that place
+        ParseHelper_Node_Exit();
+    } // if(!use.empty()) else
+}
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 459 - 0
code/AssetLib/X3D/X3DImporter_Node.hpp

@@ -0,0 +1,459 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file   X3DImporter_Node.hpp
+/// \brief  Elements of scene graph.
+/// \date   2015-2016
+/// \author [email protected]
+
+#ifndef INCLUDED_AI_X3D_IMPORTER_NODE_H
+#define INCLUDED_AI_X3D_IMPORTER_NODE_H
+
+// Header files, Assimp.
+#include <assimp/types.h>
+
+#include <list>
+#include <vector>
+
+enum X3DElemType {
+    ENET_Group, ///< Element has type "Group".
+    ENET_MetaBoolean, ///< Element has type "Metadata boolean".
+    ENET_MetaDouble, ///< Element has type "Metadata double".
+    ENET_MetaFloat, ///< Element has type "Metadata float".
+    ENET_MetaInteger, ///< Element has type "Metadata integer".
+    ENET_MetaSet, ///< Element has type "Metadata set".
+    ENET_MetaString, ///< Element has type "Metadata string".
+    ENET_Arc2D, ///< Element has type "Arc2D".
+    ENET_ArcClose2D, ///< Element has type "ArcClose2D".
+    ENET_Circle2D, ///< Element has type "Circle2D".
+    ENET_Disk2D, ///< Element has type "Disk2D".
+    ENET_Polyline2D, ///< Element has type "Polyline2D".
+    ENET_Polypoint2D, ///< Element has type "Polypoint2D".
+    ENET_Rectangle2D, ///< Element has type "Rectangle2D".
+    ENET_TriangleSet2D, ///< Element has type "TriangleSet2D".
+    ENET_Box, ///< Element has type "Box".
+    ENET_Cone, ///< Element has type "Cone".
+    ENET_Cylinder, ///< Element has type "Cylinder".
+    ENET_Sphere, ///< Element has type "Sphere".
+    ENET_ElevationGrid, ///< Element has type "ElevationGrid".
+    ENET_Extrusion, ///< Element has type "Extrusion".
+    ENET_Coordinate, ///< Element has type "Coordinate".
+    ENET_Normal, ///< Element has type "Normal".
+    ENET_TextureCoordinate, ///< Element has type "TextureCoordinate".
+    ENET_IndexedFaceSet, ///< Element has type "IndexedFaceSet".
+    ENET_IndexedLineSet, ///< Element has type "IndexedLineSet".
+    ENET_IndexedTriangleSet, ///< Element has type "IndexedTriangleSet".
+    ENET_IndexedTriangleFanSet, ///< Element has type "IndexedTriangleFanSet".
+    ENET_IndexedTriangleStripSet, ///< Element has type "IndexedTriangleStripSet".
+    ENET_LineSet, ///< Element has type "LineSet".
+    ENET_PointSet, ///< Element has type "PointSet".
+    ENET_TriangleSet, ///< Element has type "TriangleSet".
+    ENET_TriangleFanSet, ///< Element has type "TriangleFanSet".
+    ENET_TriangleStripSet, ///< Element has type "TriangleStripSet".
+    ENET_Color, ///< Element has type "Color".
+    ENET_ColorRGBA, ///< Element has type "ColorRGBA".
+    ENET_Shape, ///< Element has type "Shape".
+    ENET_Appearance, ///< Element has type "Appearance".
+    ENET_Material, ///< Element has type "Material".
+    ENET_ImageTexture, ///< Element has type "ImageTexture".
+    ENET_TextureTransform, ///< Element has type "TextureTransform".
+    ENET_DirectionalLight, ///< Element has type "DirectionalLight".
+    ENET_PointLight, ///< Element has type "PointLight".
+    ENET_SpotLight, ///< Element has type "SpotLight".
+
+    ENET_Invalid ///< Element has invalid type and possible contain invalid data.
+};
+
+struct X3DNodeElementBase {
+    X3DNodeElementBase *Parent;
+    std::string ID;
+    std::list<X3DNodeElementBase *> Children;
+    X3DElemType Type;
+
+protected:
+    X3DNodeElementBase(X3DElemType type, X3DNodeElementBase *pParent) :
+            Type(type), Parent(pParent) {
+        // empty
+    }
+};
+
+/// This struct hold <Color> value.
+struct X3DNodeElementColor : X3DNodeElementBase {
+    std::list<aiColor3D> Value; ///< Stored value.
+
+    /// Constructor
+    /// \param [in] pParent - pointer to parent node.
+    X3DNodeElementColor(X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(X3DElemType::ENET_Color, pParent) {}
+
+}; // struct X3DNodeElementColor
+
+/// This struct hold <ColorRGBA> value.
+struct X3DNodeElementColorRGBA : X3DNodeElementBase {
+    std::list<aiColor4D> Value; ///< Stored value.
+
+    /// Constructor
+    /// \param [in] pParent - pointer to parent node.
+    X3DNodeElementColorRGBA(X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(X3DElemType::ENET_ColorRGBA, pParent) {}
+
+}; // struct X3DNodeElementColorRGBA
+
+/// This struct hold <Coordinate> value.
+struct X3DNodeElementCoordinate : public X3DNodeElementBase {
+    std::list<aiVector3D> Value; ///< Stored value.
+
+    /// Constructor
+    /// \param [in] pParent - pointer to parent node.
+    X3DNodeElementCoordinate(X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(X3DElemType::ENET_Coordinate, pParent) {}
+
+}; // struct X3DNodeElementCoordinate
+
+/// This struct hold <Normal> value.
+struct X3DNodeElementNormal : X3DNodeElementBase {
+    std::list<aiVector3D> Value; ///< Stored value.
+
+    /// Constructor
+    /// \param [in] pParent - pointer to parent node.
+    X3DNodeElementNormal(X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(X3DElemType::ENET_Normal, pParent) {}
+
+}; // struct X3DNodeElementNormal
+
+/// This struct hold <TextureCoordinate> value.
+struct X3DNodeElementTextureCoordinate : X3DNodeElementBase {
+    std::list<aiVector2D> Value; ///< Stored value.
+
+    /// Constructor
+    /// \param [in] pParent - pointer to parent node.
+    X3DNodeElementTextureCoordinate(X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(X3DElemType::ENET_TextureCoordinate, pParent) {}
+
+}; // struct X3DNodeElementTextureCoordinate
+
+/// Two-dimensional figure.
+struct X3DNodeElementGeometry2D : X3DNodeElementBase {
+    std::list<aiVector3D> Vertices; ///< Vertices list.
+    size_t NumIndices; ///< Number of indices in one face.
+    bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object.
+
+    /// Constructor.
+    /// \param [in] pParent - pointer to parent node.
+    /// \param [in] pType - type of geometry object.
+    X3DNodeElementGeometry2D(X3DElemType pType, X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(pType, pParent), Solid(true) {}
+
+}; // class X3DNodeElementGeometry2D
+
+/// Three-dimensional body.
+struct X3DNodeElementGeometry3D : X3DNodeElementBase {
+    std::list<aiVector3D> Vertices; ///< Vertices list.
+    size_t NumIndices; ///< Number of indices in one face.
+    bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object.
+
+    /// Constructor.
+    /// \param [in] pParent - pointer to parent node.
+    /// \param [in] pType - type of geometry object.
+    X3DNodeElementGeometry3D(X3DElemType pType, X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(pType, pParent), Vertices(), NumIndices(0), Solid(true) {
+        // empty
+    }
+}; // class X3DNodeElementGeometry3D
+
+/// Uniform rectangular grid of varying height.
+struct X3DNodeElementElevationGrid : X3DNodeElementGeometry3D {
+    bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line).
+    bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line).
+    /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are
+    /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced.
+    float CreaseAngle;
+    std::vector<int32_t> CoordIdx; ///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces.
+
+    /// Constructor.
+    /// \param [in] pParent - pointer to parent node.
+    /// \param [in] pType - type of geometry object.
+    X3DNodeElementElevationGrid(X3DElemType pType, X3DNodeElementBase *pParent) :
+            X3DNodeElementGeometry3D(pType, pParent) {}
+}; // class X3DNodeElementIndexedSet
+
+/// Shape with indexed vertices.
+struct X3DNodeElementIndexedSet : public X3DNodeElementGeometry3D {
+    /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors
+    /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to
+    /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the
+    /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite
+    /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the
+    /// ccw field, results are undefined.
+    bool CCW;
+    std::vector<int32_t> ColorIndex; ///< Field to specify the polygonal faces by indexing into the <Color> or <ColorRGBA>.
+    bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line).
+    /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself,
+    /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results
+    /// even if the convex field is FALSE.
+    bool Convex;
+    std::vector<int32_t> CoordIndex; ///< Field to specify the polygonal faces by indexing into the <Coordinate>.
+    /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are
+    /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced.
+    float CreaseAngle;
+    std::vector<int32_t> NormalIndex; ///< Field to specify the polygonal faces by indexing into the <Normal>.
+    bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line).
+    std::vector<int32_t> TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
+
+    /// Constructor.
+    /// \param [in] pParent - pointer to parent node.
+    /// \param [in] pType - type of geometry object.
+    X3DNodeElementIndexedSet(X3DElemType pType, X3DNodeElementBase *pParent) :
+            X3DNodeElementGeometry3D(pType, pParent) {}
+}; // class X3DNodeElementIndexedSet
+
+/// Shape with set of vertices.
+struct X3DNodeElementSet : X3DNodeElementGeometry3D {
+    /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors
+    /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to
+    /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the
+    /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite
+    /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the
+    /// ccw field, results are undefined.
+    bool CCW;
+    bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line).
+    bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line).
+    std::vector<int32_t> CoordIndex; ///< Field to specify the polygonal faces by indexing into the <Coordinate>.
+    std::vector<int32_t> NormalIndex; ///< Field to specify the polygonal faces by indexing into the <Normal>.
+    std::vector<int32_t> TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
+    std::vector<int32_t> VertexCount; ///< Field describes how many vertices are to be used in each polyline(polygon) from the <Coordinate> field.
+
+    /// Constructor.
+    /// \param [in] pParent - pointer to parent node.
+    /// \param [in] pType - type of geometry object.
+    X3DNodeElementSet(X3DElemType type, X3DNodeElementBase *pParent) :
+            X3DNodeElementGeometry3D(type, pParent) {}
+
+}; // class X3DNodeElementSet
+
+/// This struct hold <Shape> value.
+struct X3DNodeElementShape : X3DNodeElementBase {
+
+    /// Constructor
+    /// \param [in] pParent - pointer to parent node.
+    X3DNodeElementShape(X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(X3DElemType::ENET_Shape, pParent) {}
+}; // struct X3DNodeElementShape
+
+/// This struct hold <Appearance> value.
+struct X3DNodeElementAppearance : public X3DNodeElementBase {
+
+    /// Constructor
+    /// \param [in] pParent - pointer to parent node.
+    X3DNodeElementAppearance(X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(X3DElemType::ENET_Appearance, pParent) {}
+
+}; // struct X3DNodeElementAppearance
+
+struct X3DNodeElementMaterial : public X3DNodeElementBase {
+    float AmbientIntensity; ///< Specifies how much ambient light from light sources this surface shall reflect.
+    aiColor3D DiffuseColor; ///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source.
+    aiColor3D EmissiveColor; ///< Models "glowing" objects. This can be useful for displaying pre-lit models.
+    float Shininess; ///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights.
+    aiColor3D SpecularColor; ///< The specularColor and shininess fields determine the specular highlights.
+    float Transparency; ///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque.
+
+    /// Constructor.
+    /// \param [in] pParent - pointer to parent node.
+    /// \param [in] pType - type of geometry object.
+    X3DNodeElementMaterial(X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(X3DElemType::ENET_Material, pParent),
+            AmbientIntensity(0.0f),
+            DiffuseColor(),
+            EmissiveColor(),
+            Shininess(0.0f),
+            SpecularColor(),
+            Transparency(1.0f) {
+        // empty
+    }
+}; // class X3DNodeElementMaterial
+
+/// This struct hold <ImageTexture> value.
+struct X3DNodeElementImageTexture : X3DNodeElementBase {
+    /// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated
+    /// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are
+    /// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field.
+    bool RepeatS;
+    bool RepeatT; ///< See \ref RepeatS.
+    std::string URL; ///< URL of the texture.
+
+    /// Constructor
+    /// \param [in] pParent - pointer to parent node.
+    X3DNodeElementImageTexture(X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(X3DElemType::ENET_ImageTexture, pParent) {}
+
+}; // struct X3DNodeElementImageTexture
+
+/// This struct hold <TextureTransform> value.
+struct X3DNodeElementTextureTransform : X3DNodeElementBase {
+    aiVector2D Center; ///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied.
+    float Rotation; ///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied.
+    aiVector2D Scale; ///< Specifies a scaling factor in S and T of the texture coordinates about the center point.
+    aiVector2D Translation; ///<  Specifies a translation of the texture coordinates.
+
+    /// Constructor
+    /// \param [in] pParent - pointer to parent node.
+    X3DNodeElementTextureTransform(X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(X3DElemType::ENET_TextureTransform, pParent) {}
+
+}; // struct X3DNodeElementTextureTransform
+
+struct X3DNodeElementGroup : X3DNodeElementBase {
+    aiMatrix4x4 Transformation; ///< Transformation matrix.
+
+    /// As you know node elements can use already defined node elements when attribute "USE" is defined.
+    /// Standard search when looking for an element in the whole scene graph, existing at this moment.
+    /// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static.
+    bool Static;
+
+    bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept.
+    int32_t Choice; ///< Number of the child which will be kept.
+
+    /// Constructor.
+    /// \param [in] pParent - pointer to parent node.
+    /// \param [in] pStatic - static node flag.
+    X3DNodeElementGroup(X3DNodeElementBase *pParent, const bool pStatic = false) :
+            X3DNodeElementBase(X3DElemType::ENET_Group, pParent), Static(pStatic), UseChoice(false) {}
+};
+
+struct X3DNodeElementMeta : X3DNodeElementBase {
+    std::string Name; ///< Name of metadata object.
+    std::string Reference;
+
+    virtual ~X3DNodeElementMeta() {
+        // empty
+    }
+
+protected:
+    X3DNodeElementMeta(X3DElemType type, X3DNodeElementBase *parent) :
+            X3DNodeElementBase(type, parent) {
+        // empty
+    }
+};
+
+struct X3DNodeElementMetaBoolean : X3DNodeElementMeta {
+    std::vector<bool> Value; ///< Stored value.
+
+    explicit X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) :
+            X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) {
+        // empty
+    }
+};
+
+struct X3DNodeElementMetaDouble : X3DNodeElementMeta {
+    std::vector<double> Value; ///< Stored value.
+
+    explicit X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) :
+            X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) {
+        // empty
+    }
+};
+
+struct X3DNodeElementMetaFloat : public X3DNodeElementMeta {
+    std::vector<float> Value; ///< Stored value.
+
+    explicit X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) :
+            X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) {
+        // empty
+    }
+};
+
+struct X3DNodeElementMetaInt : public X3DNodeElementMeta {
+    std::vector<int32_t> Value; ///< Stored value.
+
+    explicit X3DNodeElementMetaInt(X3DNodeElementBase *pParent) :
+            X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) {
+        // empty
+    }
+};
+
+struct X3DNodeElementMetaSet : public X3DNodeElementMeta {
+    std::list<X3DNodeElementMeta> Value; ///< Stored value.
+
+    explicit X3DNodeElementMetaSet(X3DNodeElementBase *pParent) :
+            X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) {
+        // empty
+    }
+};
+
+struct X3DNodeElementMetaString : X3DNodeElementMeta {
+    std::vector<std::string> Value; ///< Stored value.
+
+    explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) :
+            X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) {
+        // empty
+    }
+};
+
+/// \struct X3DNodeElementLight
+/// This struct hold <TextureTransform> value.
+struct X3DNodeElementLight : X3DNodeElementBase {
+    float AmbientIntensity; ///< Specifies the intensity of the ambient emission from the light.
+    aiColor3D Color; ///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value.
+    aiVector3D Direction; ///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system.
+    /// \var Global
+    /// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence.
+    /// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light.
+    bool Global;
+    float Intensity; ///< Specifies the brightness of the direct emission from the light.
+    /// \var Attenuation
+    /// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor
+    /// is: "1 / max(attenuation[0] + attenuation[1] * r + attenuation[2] * r2, 1)", where r is the distance from the light to the surface being illuminated.
+    aiVector3D Attenuation;
+    aiVector3D Location; ///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin.
+    float Radius; ///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source.
+    float BeamWidth; ///< Specifies an inner solid angle in which the light source emits light at uniform full intensity.
+    float CutOffAngle; ///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle).
+
+    /// Constructor
+    /// \param [in] pParent - pointer to parent node.
+    /// \param [in] pLightType - type of the light source.
+    X3DNodeElementLight(X3DElemType pLightType, X3DNodeElementBase *pParent) :
+            X3DNodeElementBase(pLightType, pParent) {}
+
+}; // struct X3DNodeElementLight
+
+#endif // INCLUDED_AI_X3D_IMPORTER_NODE_H

+ 731 - 0
code/AssetLib/X3D/X3DImporter_Postprocess.cpp

@@ -0,0 +1,731 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file   X3DImporter_Postprocess.cpp
+/// \brief  Convert built scenegraph and objects to Assimp scenegraph.
+/// \date   2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DGeoHelper.h"
+#include "X3DImporter.hpp"
+
+// Header files, Assimp.
+#include <assimp/StandardShapes.h>
+#include <assimp/StringUtils.h>
+#include <assimp/ai_assert.h>
+
+// Header files, stdlib.
+#include <algorithm>
+#include <iterator>
+#include <string>
+
+namespace Assimp {
+
+aiMatrix4x4 X3DImporter::PostprocessHelper_Matrix_GlobalToCurrent() const {
+    X3DNodeElementBase *cur_node;
+    std::list<aiMatrix4x4> matr;
+    aiMatrix4x4 out_matr;
+
+    // starting walk from current element to root
+    cur_node = mNodeElementCur;
+    if (cur_node != nullptr) {
+        do {
+            // if cur_node is group then store group transformation matrix in list.
+            if (cur_node->Type == X3DElemType::ENET_Group) matr.push_back(((X3DNodeElementGroup *)cur_node)->Transformation);
+
+            cur_node = cur_node->Parent;
+        } while (cur_node != nullptr);
+    }
+
+    // multiplicate all matrices in reverse order
+    for (std::list<aiMatrix4x4>::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit)
+        out_matr = out_matr * (*rit);
+
+    return out_matr;
+}
+
+void X3DImporter::PostprocessHelper_CollectMetadata(const X3DNodeElementBase &pNodeElement, std::list<X3DNodeElementBase *> &pList) const {
+    // walk through childs and find for metadata.
+    for (std::list<X3DNodeElementBase *>::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); ++el_it) {
+        if (((*el_it)->Type == X3DElemType::ENET_MetaBoolean) || ((*el_it)->Type == X3DElemType::ENET_MetaDouble) ||
+                ((*el_it)->Type == X3DElemType::ENET_MetaFloat) || ((*el_it)->Type == X3DElemType::ENET_MetaInteger) ||
+                ((*el_it)->Type == X3DElemType::ENET_MetaString)) {
+            pList.push_back(*el_it);
+        } else if ((*el_it)->Type == X3DElemType::ENET_MetaSet) {
+            PostprocessHelper_CollectMetadata(**el_it, pList);
+        }
+    } // for(std::list<X3DNodeElementBase*>::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); el_it++)
+}
+
+bool X3DImporter::PostprocessHelper_ElementIsMetadata(const X3DElemType pType) const {
+    if ((pType == X3DElemType::ENET_MetaBoolean) || (pType == X3DElemType::ENET_MetaDouble) ||
+            (pType == X3DElemType::ENET_MetaFloat) || (pType == X3DElemType::ENET_MetaInteger) ||
+            (pType == X3DElemType::ENET_MetaString) || (pType == X3DElemType::ENET_MetaSet)) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool X3DImporter::PostprocessHelper_ElementIsMesh(const X3DElemType pType) const {
+    if ((pType == X3DElemType::ENET_Arc2D) || (pType == X3DElemType::ENET_ArcClose2D) ||
+            (pType == X3DElemType::ENET_Box) || (pType == X3DElemType::ENET_Circle2D) ||
+            (pType == X3DElemType::ENET_Cone) || (pType == X3DElemType::ENET_Cylinder) ||
+            (pType == X3DElemType::ENET_Disk2D) || (pType == X3DElemType::ENET_ElevationGrid) ||
+            (pType == X3DElemType::ENET_Extrusion) || (pType == X3DElemType::ENET_IndexedFaceSet) ||
+            (pType == X3DElemType::ENET_IndexedLineSet) || (pType == X3DElemType::ENET_IndexedTriangleFanSet) ||
+            (pType == X3DElemType::ENET_IndexedTriangleSet) || (pType == X3DElemType::ENET_IndexedTriangleStripSet) ||
+            (pType == X3DElemType::ENET_PointSet) || (pType == X3DElemType::ENET_LineSet) ||
+            (pType == X3DElemType::ENET_Polyline2D) || (pType == X3DElemType::ENET_Polypoint2D) ||
+            (pType == X3DElemType::ENET_Rectangle2D) || (pType == X3DElemType::ENET_Sphere) ||
+            (pType == X3DElemType::ENET_TriangleFanSet) || (pType == X3DElemType::ENET_TriangleSet) ||
+            (pType == X3DElemType::ENET_TriangleSet2D) || (pType == X3DElemType::ENET_TriangleStripSet)) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void X3DImporter::Postprocess_BuildLight(const X3DNodeElementBase &pNodeElement, std::list<aiLight *> &pSceneLightList) const {
+    const X3DNodeElementLight &ne = *((X3DNodeElementLight *)&pNodeElement);
+    aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent();
+    aiLight *new_light = new aiLight;
+
+    new_light->mName = ne.ID;
+    new_light->mColorAmbient = ne.Color * ne.AmbientIntensity;
+    new_light->mColorDiffuse = ne.Color * ne.Intensity;
+    new_light->mColorSpecular = ne.Color * ne.Intensity;
+    switch (pNodeElement.Type) {
+    case X3DElemType::ENET_DirectionalLight:
+        new_light->mType = aiLightSource_DIRECTIONAL;
+        new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr;
+
+        break;
+    case X3DElemType::ENET_PointLight:
+        new_light->mType = aiLightSource_POINT;
+        new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr;
+        new_light->mAttenuationConstant = ne.Attenuation.x;
+        new_light->mAttenuationLinear = ne.Attenuation.y;
+        new_light->mAttenuationQuadratic = ne.Attenuation.z;
+
+        break;
+    case X3DElemType::ENET_SpotLight:
+        new_light->mType = aiLightSource_SPOT;
+        new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr;
+        new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr;
+        new_light->mAttenuationConstant = ne.Attenuation.x;
+        new_light->mAttenuationLinear = ne.Attenuation.y;
+        new_light->mAttenuationQuadratic = ne.Attenuation.z;
+        new_light->mAngleInnerCone = ne.BeamWidth;
+        new_light->mAngleOuterCone = ne.CutOffAngle;
+
+        break;
+    default:
+        throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + ai_to_string(pNodeElement.Type) + ".");
+    }
+
+    pSceneLightList.push_back(new_light);
+}
+
+void X3DImporter::Postprocess_BuildMaterial(const X3DNodeElementBase &pNodeElement, aiMaterial **pMaterial) const {
+    // check argument
+    if (pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr.");
+    if (*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr.");
+
+    *pMaterial = new aiMaterial;
+    aiMaterial &taimat = **pMaterial; // creating alias for convenience.
+
+    // at this point pNodeElement point to <Appearance> node. Walk through childs and add all stored data.
+    for (std::list<X3DNodeElementBase *>::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); ++el_it) {
+        if ((*el_it)->Type == X3DElemType::ENET_Material) {
+            aiColor3D tcol3;
+            float tvalf;
+            X3DNodeElementMaterial &tnemat = *((X3DNodeElementMaterial *)*el_it);
+
+            tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity;
+            taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT);
+            taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE);
+            taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE);
+            taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR);
+            tvalf = 1;
+            taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH);
+            taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS);
+            tvalf = 1.0f - tnemat.Transparency;
+            taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY);
+        } // if((*el_it)->Type == X3DElemType::ENET_Material)
+        else if ((*el_it)->Type == X3DElemType::ENET_ImageTexture) {
+            X3DNodeElementImageTexture &tnetex = *((X3DNodeElementImageTexture *)*el_it);
+            aiString url_str(tnetex.URL.c_str());
+            int mode = aiTextureOp_Multiply;
+
+            taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0));
+            taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
+            taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
+            taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0));
+        } // else if((*el_it)->Type == X3DElemType::ENET_ImageTexture)
+        else if ((*el_it)->Type == X3DElemType::ENET_TextureTransform) {
+            aiUVTransform trans;
+            X3DNodeElementTextureTransform &tnetextr = *((X3DNodeElementTextureTransform *)*el_it);
+
+            trans.mTranslation = tnetextr.Translation - tnetextr.Center;
+            trans.mScaling = tnetextr.Scale;
+            trans.mRotation = tnetextr.Rotation;
+            taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
+        } // else if((*el_it)->Type == X3DElemType::ENET_TextureTransform)
+    } // for(std::list<X3DNodeElementBase*>::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); el_it++)
+}
+
+void X3DImporter::Postprocess_BuildMesh(const X3DNodeElementBase &pNodeElement, aiMesh **pMesh) const {
+    // check argument
+    if (pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr.");
+    if (*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr.");
+
+    /************************************************************************************************************************************/
+    /************************************************************ Geometry2D ************************************************************/
+    /************************************************************************************************************************************/
+    if ((pNodeElement.Type == X3DElemType::ENET_Arc2D) || (pNodeElement.Type == X3DElemType::ENET_ArcClose2D) ||
+            (pNodeElement.Type == X3DElemType::ENET_Circle2D) || (pNodeElement.Type == X3DElemType::ENET_Disk2D) ||
+            (pNodeElement.Type == X3DElemType::ENET_Polyline2D) || (pNodeElement.Type == X3DElemType::ENET_Polypoint2D) ||
+            (pNodeElement.Type == X3DElemType::ENET_Rectangle2D) || (pNodeElement.Type == X3DElemType::ENET_TriangleSet2D)) {
+        X3DNodeElementGeometry2D &tnemesh = *((X3DNodeElementGeometry2D *)&pNodeElement); // create alias for convenience
+        std::vector<aiVector3D> tarr;
+
+        tarr.reserve(tnemesh.Vertices.size());
+        for (std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it)
+            tarr.push_back(*it);
+        *pMesh = StandardShapes::MakeMesh(tarr, static_cast<unsigned int>(tnemesh.NumIndices)); // create mesh from vertices using Assimp help.
+
+        return; // mesh is build, nothing to do anymore.
+    }
+    /************************************************************************************************************************************/
+    /************************************************************ Geometry3D ************************************************************/
+    /************************************************************************************************************************************/
+    //
+    // Predefined figures
+    //
+    if ((pNodeElement.Type == X3DElemType::ENET_Box) || (pNodeElement.Type == X3DElemType::ENET_Cone) ||
+            (pNodeElement.Type == X3DElemType::ENET_Cylinder) || (pNodeElement.Type == X3DElemType::ENET_Sphere)) {
+        X3DNodeElementGeometry3D &tnemesh = *((X3DNodeElementGeometry3D *)&pNodeElement); // create alias for convenience
+        std::vector<aiVector3D> tarr;
+
+        tarr.reserve(tnemesh.Vertices.size());
+        for (std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it)
+            tarr.push_back(*it);
+
+        *pMesh = StandardShapes::MakeMesh(tarr, static_cast<unsigned int>(tnemesh.NumIndices)); // create mesh from vertices using Assimp help.
+
+        return; // mesh is build, nothing to do anymore.
+    }
+    //
+    // Parametric figures
+    //
+    if (pNodeElement.Type == X3DElemType::ENET_ElevationGrid) {
+        X3DNodeElementElevationGrid &tnemesh = *((X3DNodeElementElevationGrid *)&pNodeElement); // create alias for convenience
+
+        // at first create mesh from existing vertices.
+        *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIdx, tnemesh.Vertices);
+        // copy additional information from children
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            if ((*ch_it)->Type == X3DElemType::ENET_Color)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_Normal)
+                X3DGeoHelper::add_normal(**pMesh, ((X3DNodeElementNormal *)*ch_it)->Value, tnemesh.NormalPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate)
+                X3DGeoHelper::add_tex_coord(**pMesh, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value);
+            else
+                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + ai_to_string((*ch_it)->Type) + ".");
+        } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it)
+
+        return; // mesh is build, nothing to do anymore.
+    } // if(pNodeElement.Type == X3DElemType::ENET_ElevationGrid)
+    //
+    // Indexed primitives sets
+    //
+    if (pNodeElement.Type == X3DElemType::ENET_IndexedFaceSet) {
+        X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience
+
+        // at first search for <Coordinate> node and create mesh.
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+                *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value);
+            }
+        }
+
+        // copy additional information from children
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            if ((*ch_it)->Type == X3DElemType::ENET_Color)
+                X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA)
+                X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value,
+                        tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+            } // skip because already read when mesh created.
+            else if ((*ch_it)->Type == X3DElemType::ENET_Normal)
+                X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value,
+                        tnemesh.NormalPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate)
+                X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value);
+            else
+                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + ai_to_string((*ch_it)->Type) + ".");
+        } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it)
+
+        return; // mesh is build, nothing to do anymore.
+    } // if(pNodeElement.Type == X3DElemType::ENET_IndexedFaceSet)
+
+    if (pNodeElement.Type == X3DElemType::ENET_IndexedLineSet) {
+        X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience
+
+        // at first search for <Coordinate> node and create mesh.
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+                *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value);
+            }
+        }
+
+        // copy additional information from children
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            ai_assert(*pMesh);
+            if ((*ch_it)->Type == X3DElemType::ENET_Color)
+                X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA)
+                X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value,
+                        tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+            } // skip because already read when mesh created.
+            else
+                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + ai_to_string((*ch_it)->Type) + ".");
+        } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it)
+
+        return; // mesh is build, nothing to do anymore.
+    } // if(pNodeElement.Type == X3DElemType::ENET_IndexedLineSet)
+
+    if ((pNodeElement.Type == X3DElemType::ENET_IndexedTriangleSet) ||
+            (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleFanSet) ||
+            (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleStripSet)) {
+        X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience
+
+        // at first search for <Coordinate> node and create mesh.
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+                *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value);
+            }
+        }
+
+        // copy additional information from children
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            ai_assert(*pMesh);
+            if ((*ch_it)->Type == X3DElemType::ENET_Color)
+                X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA)
+                X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value,
+                        tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+            } // skip because already read when mesh created.
+            else if ((*ch_it)->Type == X3DElemType::ENET_Normal)
+                X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value,
+                        tnemesh.NormalPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate)
+                X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value);
+            else
+                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \
+                                                                    IndexedTriangleStripSet: " +
+                                        ai_to_string((*ch_it)->Type) + ".");
+        } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it)
+
+        return; // mesh is build, nothing to do anymore.
+    } // if((pNodeElement.Type == X3DElemType::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleStripSet))
+
+    if (pNodeElement.Type == X3DElemType::ENET_Extrusion) {
+        X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience
+
+        *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, tnemesh.Vertices);
+
+        return; // mesh is build, nothing to do anymore.
+    } // if(pNodeElement.Type == X3DElemType::ENET_Extrusion)
+
+    //
+    // Primitives sets
+    //
+    if (pNodeElement.Type == X3DElemType::ENET_PointSet) {
+        X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience
+
+        // at first search for <Coordinate> node and create mesh.
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+                std::vector<aiVector3D> vec_copy;
+
+                vec_copy.reserve(((X3DNodeElementCoordinate *)*ch_it)->Value.size());
+                for (std::list<aiVector3D>::const_iterator it = ((X3DNodeElementCoordinate *)*ch_it)->Value.begin();
+                        it != ((X3DNodeElementCoordinate *)*ch_it)->Value.end(); ++it) {
+                    vec_copy.push_back(*it);
+                }
+
+                *pMesh = StandardShapes::MakeMesh(vec_copy, 1);
+            }
+        }
+
+        // copy additional information from children
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            ai_assert(*pMesh);
+            if ((*ch_it)->Type == X3DElemType::ENET_Color)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, true);
+            else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, true);
+            else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+            } // skip because already read when mesh created.
+            else
+                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + ai_to_string((*ch_it)->Type) + ".");
+        } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it)
+
+        return; // mesh is build, nothing to do anymore.
+    } // if(pNodeElement.Type == X3DElemType::ENET_PointSet)
+
+    if (pNodeElement.Type == X3DElemType::ENET_LineSet) {
+        X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience
+
+        // at first search for <Coordinate> node and create mesh.
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+                *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value);
+            }
+        }
+
+        // copy additional information from children
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            ai_assert(*pMesh);
+            if ((*ch_it)->Type == X3DElemType::ENET_Color)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, true);
+            else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, true);
+            else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+            } // skip because already read when mesh created.
+            else
+                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + ai_to_string((*ch_it)->Type) + ".");
+        } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it)
+
+        return; // mesh is build, nothing to do anymore.
+    } // if(pNodeElement.Type == X3DElemType::ENET_LineSet)
+
+    if (pNodeElement.Type == X3DElemType::ENET_TriangleFanSet) {
+        X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience
+
+        // at first search for <Coordinate> node and create mesh.
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+                *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value);
+            }
+        }
+
+        // copy additional information from children
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            if (nullptr == *pMesh) {
+                break;
+            }
+            if ((*ch_it)->Type == X3DElemType::ENET_Color)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+            } // skip because already read when mesh created.
+            else if ((*ch_it)->Type == X3DElemType::ENET_Normal)
+                X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value,
+                        tnemesh.NormalPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate)
+                X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value);
+            else
+                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + ai_to_string((*ch_it)->Type) + ".");
+        } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it)
+
+        return; // mesh is build, nothing to do anymore.
+    } // if(pNodeElement.Type == X3DElemType::ENET_TriangleFanSet)
+
+    if (pNodeElement.Type == X3DElemType::ENET_TriangleSet) {
+        X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience
+
+        // at first search for <Coordinate> node and create mesh.
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+                std::vector<aiVector3D> vec_copy;
+
+                vec_copy.reserve(((X3DNodeElementCoordinate *)*ch_it)->Value.size());
+                for (std::list<aiVector3D>::const_iterator it = ((X3DNodeElementCoordinate *)*ch_it)->Value.begin();
+                        it != ((X3DNodeElementCoordinate *)*ch_it)->Value.end(); ++it) {
+                    vec_copy.push_back(*it);
+                }
+
+                *pMesh = StandardShapes::MakeMesh(vec_copy, 3);
+            }
+        }
+
+        // copy additional information from children
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            ai_assert(*pMesh);
+            if ((*ch_it)->Type == X3DElemType::ENET_Color)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+            } // skip because already read when mesh created.
+            else if ((*ch_it)->Type == X3DElemType::ENET_Normal)
+                X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value,
+                        tnemesh.NormalPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate)
+                X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value);
+            else
+                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + ai_to_string((*ch_it)->Type) + ".");
+        } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it)
+
+        return; // mesh is build, nothing to do anymore.
+    } // if(pNodeElement.Type == X3DElemType::ENET_TriangleSet)
+
+    if (pNodeElement.Type == X3DElemType::ENET_TriangleStripSet) {
+        X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience
+
+        // at first search for <Coordinate> node and create mesh.
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+                *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value);
+            }
+        }
+
+        // copy additional information from children
+        for (std::list<X3DNodeElementBase *>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) {
+            ai_assert(*pMesh);
+            if ((*ch_it)->Type == X3DElemType::ENET_Color)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA)
+                X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) {
+            } // skip because already read when mesh created.
+            else if ((*ch_it)->Type == X3DElemType::ENET_Normal)
+                X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value,
+                        tnemesh.NormalPerVertex);
+            else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate)
+                X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value);
+            else
+                throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + ai_to_string((*ch_it)->Type) + ".");
+        } // for(std::list<X3DNodeElementBase*>::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it)
+
+        return; // mesh is build, nothing to do anymore.
+    } // if(pNodeElement.Type == X3DElemType::ENET_TriangleStripSet)
+
+    throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + ai_to_string(pNodeElement.Type) + ".");
+}
+
+void X3DImporter::Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list<aiMesh *> &pSceneMeshList,
+        std::list<aiMaterial *> &pSceneMaterialList, std::list<aiLight *> &pSceneLightList) const {
+    std::list<X3DNodeElementBase *>::const_iterator chit_begin = pNodeElement.Children.begin();
+    std::list<X3DNodeElementBase *>::const_iterator chit_end = pNodeElement.Children.end();
+    std::list<aiNode *> SceneNode_Child;
+    std::list<unsigned int> SceneNode_Mesh;
+
+    // At first read all metadata
+    Postprocess_CollectMetadata(pNodeElement, pSceneNode);
+    // check if we have deal with grouping node. Which can contain transformation or switch
+    if (pNodeElement.Type == X3DElemType::ENET_Group) {
+        const X3DNodeElementGroup &tne_group = *((X3DNodeElementGroup *)&pNodeElement); // create alias for convenience
+
+        pSceneNode.mTransformation = tne_group.Transformation;
+        if (tne_group.UseChoice) {
+            // If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen.
+            if ((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Children.size())) {
+                chit_begin = pNodeElement.Children.end();
+                chit_end = pNodeElement.Children.end();
+            } else {
+                for (size_t i = 0; i < (size_t)tne_group.Choice; i++)
+                    ++chit_begin; // forward iterator to chosen node.
+
+                chit_end = chit_begin;
+                ++chit_end; // point end iterator to next element after chosen node.
+            }
+        } // if(tne_group.UseChoice)
+    } // if(pNodeElement.Type == X3DElemType::ENET_Group)
+
+    // Reserve memory for fast access and check children.
+    for (std::list<X3DNodeElementBase *>::const_iterator it = chit_begin; it != chit_end; ++it) { // in this loop we do not read metadata because it's already read at begin.
+        if ((*it)->Type == X3DElemType::ENET_Group) {
+            // if child is group then create new node and do recursive call.
+            aiNode *new_node = new aiNode;
+
+            new_node->mName = (*it)->ID;
+            new_node->mParent = &pSceneNode;
+            SceneNode_Child.push_back(new_node);
+            Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList);
+        } else if ((*it)->Type == X3DElemType::ENET_Shape) {
+            // shape can contain only one geometry and one appearance nodes.
+            Postprocess_BuildShape(*((X3DNodeElementShape *)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList);
+        } else if (((*it)->Type == X3DElemType::ENET_DirectionalLight) || ((*it)->Type == X3DElemType::ENET_PointLight) ||
+                   ((*it)->Type == X3DElemType::ENET_SpotLight)) {
+            Postprocess_BuildLight(*((X3DNodeElementLight *)*it), pSceneLightList);
+        } else if (!PostprocessHelper_ElementIsMetadata((*it)->Type)) // skip metadata
+        {
+            throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + ai_to_string((*it)->Type) + ".");
+        }
+    } // for(std::list<X3DNodeElementBase*>::const_iterator it = chit_begin; it != chit_end; it++)
+
+    // copy data about children and meshes to aiNode.
+    if (!SceneNode_Child.empty()) {
+        std::list<aiNode *>::const_iterator it = SceneNode_Child.begin();
+
+        pSceneNode.mNumChildren = static_cast<unsigned int>(SceneNode_Child.size());
+        pSceneNode.mChildren = new aiNode *[pSceneNode.mNumChildren];
+        for (size_t i = 0; i < pSceneNode.mNumChildren; i++)
+            pSceneNode.mChildren[i] = *it++;
+    }
+
+    if (!SceneNode_Mesh.empty()) {
+        std::list<unsigned int>::const_iterator it = SceneNode_Mesh.begin();
+
+        pSceneNode.mNumMeshes = static_cast<unsigned int>(SceneNode_Mesh.size());
+        pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes];
+        for (size_t i = 0; i < pSceneNode.mNumMeshes; i++)
+            pSceneNode.mMeshes[i] = *it++;
+    }
+
+    // that's all. return to previous deals
+}
+
+void X3DImporter::Postprocess_BuildShape(const X3DNodeElementShape &pShapeNodeElement, std::list<unsigned int> &pNodeMeshInd,
+        std::list<aiMesh *> &pSceneMeshList, std::list<aiMaterial *> &pSceneMaterialList) const {
+    aiMaterial *tmat = nullptr;
+    aiMesh *tmesh = nullptr;
+    X3DElemType mesh_type = X3DElemType::ENET_Invalid;
+    unsigned int mat_ind = 0;
+
+    for (std::list<X3DNodeElementBase *>::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); ++it) {
+        if (PostprocessHelper_ElementIsMesh((*it)->Type)) {
+            Postprocess_BuildMesh(**it, &tmesh);
+            if (tmesh != nullptr) {
+                // if mesh successfully built then add data about it to arrays
+                pNodeMeshInd.push_back(static_cast<unsigned int>(pSceneMeshList.size()));
+                pSceneMeshList.push_back(tmesh);
+                // keep mesh type. Need above for texture coordinate generation.
+                mesh_type = (*it)->Type;
+            }
+        } else if ((*it)->Type == X3DElemType::ENET_Appearance) {
+            Postprocess_BuildMaterial(**it, &tmat);
+            if (tmat != nullptr) {
+                // if material successfully built then add data about it to array
+                mat_ind = static_cast<unsigned int>(pSceneMaterialList.size());
+                pSceneMaterialList.push_back(tmat);
+            }
+        }
+    } // for(std::list<X3DNodeElementBase*>::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); it++)
+
+    // associate read material with read mesh.
+    if ((tmesh != nullptr) && (tmat != nullptr)) {
+        tmesh->mMaterialIndex = mat_ind;
+        // Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates.
+        if ((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) {
+            int32_t tm;
+            aiVector3D tvec3;
+
+            switch (mesh_type) {
+            case X3DElemType::ENET_Box:
+                tm = aiTextureMapping_BOX;
+                break;
+            case X3DElemType::ENET_Cone:
+            case X3DElemType::ENET_Cylinder:
+                tm = aiTextureMapping_CYLINDER;
+                break;
+            case X3DElemType::ENET_Sphere:
+                tm = aiTextureMapping_SPHERE;
+                break;
+            default:
+                tm = aiTextureMapping_PLANE;
+                break;
+            } // switch(mesh_type)
+
+            tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0));
+        } // if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0))
+    } // if((tmesh != nullptr) && (tmat != nullptr))
+}
+
+void X3DImporter::Postprocess_CollectMetadata(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode) const {
+    std::list<X3DNodeElementBase *> meta_list;
+    size_t meta_idx;
+
+    PostprocessHelper_CollectMetadata(pNodeElement, meta_list); // find metadata in current node element.
+    if (!meta_list.empty()) {
+        if (pSceneNode.mMetaData != nullptr) {
+            throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong.");
+        }
+
+        // copy collected metadata to output node.
+        pSceneNode.mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(meta_list.size()));
+        meta_idx = 0;
+        for (std::list<X3DNodeElementBase *>::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx) {
+            X3DNodeElementMeta *cur_meta = (X3DNodeElementMeta *)*it;
+
+            // due to limitations we can add only first element of value list.
+            // Add an element according to its type.
+            if ((*it)->Type == X3DElemType::ENET_MetaBoolean) {
+                if (((X3DNodeElementMetaBoolean *)cur_meta)->Value.size() > 0)
+                    pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaBoolean *)cur_meta)->Value.begin()));
+            } else if ((*it)->Type == X3DElemType::ENET_MetaDouble) {
+                if (((X3DNodeElementMetaDouble *)cur_meta)->Value.size() > 0)
+                    pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, (float)*(((X3DNodeElementMetaDouble *)cur_meta)->Value.begin()));
+            } else if ((*it)->Type == X3DElemType::ENET_MetaFloat) {
+                if (((X3DNodeElementMetaFloat *)cur_meta)->Value.size() > 0)
+                    pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaFloat *)cur_meta)->Value.begin()));
+            } else if ((*it)->Type == X3DElemType::ENET_MetaInteger) {
+                if (((X3DNodeElementMetaInt *)cur_meta)->Value.size() > 0)
+                    pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaInt *)cur_meta)->Value.begin()));
+            } else if ((*it)->Type == X3DElemType::ENET_MetaString) {
+                if (((X3DNodeElementMetaString *)cur_meta)->Value.size() > 0) {
+                    aiString tstr(((X3DNodeElementMetaString *)cur_meta)->Value.begin()->data());
+
+                    pSceneNode.mMetaData->Set(static_cast<unsigned int>(meta_idx), cur_meta->Name, tstr);
+                }
+            } else {
+                throw DeadlyImportError("Postprocess. Unknown metadata type.");
+            } // if((*it)->Type == X3DElemType::ENET_Meta*) else
+        } // for(std::list<X3DNodeElementBase*>::const_iterator it = meta_list.begin(); it != meta_list.end(); it++)
+    } // if( !meta_list.empty() )
+}
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 987 - 0
code/AssetLib/X3D/X3DImporter_Rendering.cpp

@@ -0,0 +1,987 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file   X3DImporter_Rendering.cpp
+/// \brief  Parsing data from nodes of "Rendering" set of X3D.
+/// \date   2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+#include "X3DXmlHelper.h"
+
+namespace Assimp {
+
+// <Color
+// DEF=""           ID
+// USE=""           IDREF
+// color="" MFColor [inputOutput]
+// />
+void X3DImporter::readColor(XmlNode &node) {
+    std::string use, def;
+    std::list<aiColor3D> color;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getColor3DListAttribute(node, "color", color);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Color, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementColor(mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        ((X3DNodeElementColor *)ne)->Value = color;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "Color");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <ColorRGBA
+// DEF=""               ID
+// USE=""               IDREF
+// color="" MFColorRGBA [inputOutput]
+// />
+void X3DImporter::readColorRGBA(XmlNode &node) {
+    std::string use, def;
+    std::list<aiColor4D> color;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getColor4DListAttribute(node, "color", color);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ColorRGBA, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementColorRGBA(mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        ((X3DNodeElementColorRGBA *)ne)->Value = color;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "ColorRGBA");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <Coordinate
+// DEF=""      ID
+// USE=""      IDREF
+// point=""    MFVec3f [inputOutput]
+// />
+void X3DImporter::readCoordinate(XmlNode &node) {
+    std::string use, def;
+    std::list<aiVector3D> point;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getVector3DListAttribute(node, "point", point);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Coordinate, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementCoordinate(mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        ((X3DNodeElementCoordinate *)ne)->Value = point;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "Coordinate");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <IndexedLineSet
+// DEF=""                ID
+// USE=""                IDREF
+// colorIndex=""         MFInt32 [initializeOnly]
+// colorPerVertex="true" SFBool  [initializeOnly]
+// coordIndex=""         MFInt32 [initializeOnly]
+// >
+//    <!-- ColorCoordinateContentModel -->
+// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
+// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </IndexedLineSet>
+void X3DImporter::readIndexedLineSet(XmlNode &node) {
+    std::string use, def;
+    std::vector<int32_t> colorIndex;
+    bool colorPerVertex = true;
+    std::vector<int32_t> coordIndex;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "colorIndex", colorIndex);
+    XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "coordIndex", coordIndex);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedLineSet, ne);
+    } else {
+        // check data
+        if ((coordIndex.size() < 2) || ((coordIndex.back() == (-1)) && (coordIndex.size() < 3)))
+            throw DeadlyImportError("IndexedLineSet must contain not empty \"coordIndex\" attribute.");
+
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedLineSet, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne);
+
+        ne_alias.ColorIndex = colorIndex;
+        ne_alias.ColorPerVertex = colorPerVertex;
+        ne_alias.CoordIndex = coordIndex;
+        // check for child nodes
+        if (!isNodeEmpty(node)) {
+            ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                // check for Color and Coordinate nodes
+                if (currentChildName == "Color")
+                    readColor(currentChildNode);
+                else if (currentChildName == "ColorRGBA")
+                    readColorRGBA(currentChildNode);
+                else if (currentChildName == "Coordinate")
+                    readCoordinate(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode))
+                    skipUnsupportedNode("IndexedLineSet", currentChildNode);
+            }
+            ParseHelper_Node_Exit();
+        } // if(!isNodeEmpty(node))
+        else {
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+        }
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <IndexedTriangleFanSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// index=""               MFInt32 [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </IndexedTriangleFanSet>
+void X3DImporter::readIndexedTriangleFanSet(XmlNode &node) {
+    std::string use, def;
+    bool ccw = true;
+    bool colorPerVertex = true;
+    std::vector<int32_t> index;
+    bool normalPerVertex = true;
+    bool solid = true;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "ccw", ccw);
+    XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "index", index);
+    XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleFanSet, ne);
+    } else {
+        // check data
+        if (index.size() == 0) throw DeadlyImportError("IndexedTriangleFanSet must contain not empty \"index\" attribute.");
+
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleFanSet, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne);
+
+        ne_alias.CCW = ccw;
+        ne_alias.ColorPerVertex = colorPerVertex;
+        ne_alias.NormalPerVertex = normalPerVertex;
+        ne_alias.Solid = solid;
+
+        ne_alias.CoordIndex.clear();
+        int counter = 0;
+        int32_t idx[3];
+        for (std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) {
+            idx[2] = *idx_it;
+            if (idx[2] < 0) {
+                counter = 0;
+            } else {
+                if (counter >= 2) {
+                    if (ccw) {
+                        ne_alias.CoordIndex.push_back(idx[0]);
+                        ne_alias.CoordIndex.push_back(idx[1]);
+                        ne_alias.CoordIndex.push_back(idx[2]);
+                    } else {
+                        ne_alias.CoordIndex.push_back(idx[0]);
+                        ne_alias.CoordIndex.push_back(idx[2]);
+                        ne_alias.CoordIndex.push_back(idx[1]);
+                    }
+                    ne_alias.CoordIndex.push_back(-1);
+                    idx[1] = idx[2];
+                } else {
+                    idx[counter] = idx[2];
+                }
+                ++counter;
+            }
+        } // for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++)
+
+        // check for child nodes
+        if (!isNodeEmpty(node)) {
+            ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                // check for X3DComposedGeometryNodes
+                if (currentChildName == "Color")
+                    readColor(currentChildNode);
+                else if (currentChildName == "ColorRGBA")
+                    readColorRGBA(currentChildNode);
+                else if (currentChildName == "Coordinate")
+                    readCoordinate(currentChildNode);
+                else if (currentChildName == "Normal")
+                    readNormal(currentChildNode);
+                else if (currentChildName == "TextureCoordinate")
+                    readTextureCoordinate(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode))
+                    skipUnsupportedNode("IndexedTriangleFanSet", currentChildNode);
+            }
+            ParseHelper_Node_Exit();
+        } // if(!isNodeEmpty(node))
+        else {
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+        }
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <IndexedTriangleSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// index=""               MFInt32 [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </IndexedTriangleSet>
+void X3DImporter::readIndexedTriangleSet(XmlNode &node) {
+    std::string use, def;
+    bool ccw = true;
+    bool colorPerVertex = true;
+    std::vector<int32_t> index;
+    bool normalPerVertex = true;
+    bool solid = true;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "ccw", ccw);
+    XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "index", index);
+    XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleSet, ne);
+    } else {
+        // check data
+        if (index.size() == 0) throw DeadlyImportError("IndexedTriangleSet must contain not empty \"index\" attribute.");
+
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleSet, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne);
+
+        ne_alias.CCW = ccw;
+        ne_alias.ColorPerVertex = colorPerVertex;
+        ne_alias.NormalPerVertex = normalPerVertex;
+        ne_alias.Solid = solid;
+
+        ne_alias.CoordIndex.clear();
+        int counter = 0;
+        int32_t idx[3];
+        for (std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) {
+            idx[counter++] = *idx_it;
+            if (counter > 2) {
+                counter = 0;
+                if (ccw) {
+                    ne_alias.CoordIndex.push_back(idx[0]);
+                    ne_alias.CoordIndex.push_back(idx[1]);
+                    ne_alias.CoordIndex.push_back(idx[2]);
+                } else {
+                    ne_alias.CoordIndex.push_back(idx[0]);
+                    ne_alias.CoordIndex.push_back(idx[2]);
+                    ne_alias.CoordIndex.push_back(idx[1]);
+                }
+                ne_alias.CoordIndex.push_back(-1);
+            }
+        } // for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++)
+
+        // check for child nodes
+        if (!isNodeEmpty(node)) {
+            ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                // check for X3DComposedGeometryNodes
+                if (currentChildName == "Color")
+                    readColor(currentChildNode);
+                else if (currentChildName == "ColorRGBA")
+                    readColorRGBA(currentChildNode);
+                else if (currentChildName == "Coordinate")
+                    readCoordinate(currentChildNode);
+                else if (currentChildName == "Normal")
+                    readNormal(currentChildNode);
+                else if (currentChildName == "TextureCoordinate")
+                    readTextureCoordinate(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode))
+                    skipUnsupportedNode("IndexedTriangleSet", currentChildNode);
+            }
+            ParseHelper_Node_Exit();
+        } // if(!isNodeEmpty(node))
+        else {
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+        }
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <IndexedTriangleStripSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// index=""               MFInt32 [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </IndexedTriangleStripSet>
+void X3DImporter::readIndexedTriangleStripSet(XmlNode &node) {
+    std::string use, def;
+    bool ccw = true;
+    bool colorPerVertex = true;
+    std::vector<int32_t> index;
+    bool normalPerVertex = true;
+    bool solid = true;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "ccw", ccw);
+    XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "index", index);
+    XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleStripSet, ne);
+    } else {
+        // check data
+        if (index.size() == 0) throw DeadlyImportError("IndexedTriangleStripSet must contain not empty \"index\" attribute.");
+
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleStripSet, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne);
+
+        ne_alias.CCW = ccw;
+        ne_alias.ColorPerVertex = colorPerVertex;
+        ne_alias.NormalPerVertex = normalPerVertex;
+        ne_alias.Solid = solid;
+
+        ne_alias.CoordIndex.clear();
+        int counter = 0;
+        int32_t idx[3];
+        for (std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) {
+            idx[2] = *idx_it;
+            if (idx[2] < 0) {
+                counter = 0;
+            } else {
+                if (counter >= 2) {
+                    if (ccw) {
+                        ne_alias.CoordIndex.push_back(idx[0]);
+                        ne_alias.CoordIndex.push_back(idx[1]);
+                        ne_alias.CoordIndex.push_back(idx[2]);
+                    } else {
+                        ne_alias.CoordIndex.push_back(idx[0]);
+                        ne_alias.CoordIndex.push_back(idx[2]);
+                        ne_alias.CoordIndex.push_back(idx[1]);
+                    }
+                    ne_alias.CoordIndex.push_back(-1);
+                }
+                idx[counter & 1] = idx[2];
+                ++counter;
+            }
+        } // for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++)
+
+        // check for child nodes
+        if (!isNodeEmpty(node)) {
+            ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                // check for X3DComposedGeometryNodes
+                if (currentChildName == "Color")
+                    readColor(currentChildNode);
+                else if (currentChildName == "ColorRGBA")
+                    readColorRGBA(currentChildNode);
+                else if (currentChildName == "Coordinate")
+                    readCoordinate(currentChildNode);
+                else if (currentChildName == "Normal")
+                    readNormal(currentChildNode);
+                else if (currentChildName == "TextureCoordinate")
+                    readTextureCoordinate(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode))
+                    skipUnsupportedNode("IndexedTriangleStripSet", currentChildNode);
+            }
+            ParseHelper_Node_Exit();
+        } // if(!isNodeEmpty(node))
+        else {
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+        }
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <LineSet
+// DEF=""         ID
+// USE=""         IDREF
+// vertexCount="" MFInt32 [initializeOnly]
+// >
+//    <!-- ColorCoordinateContentModel -->
+// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
+// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </LineSet>
+void X3DImporter::readLineSet(XmlNode &node) {
+    std::string use, def;
+    std::vector<int32_t> vertexCount;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "vertexCount", vertexCount);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_LineSet, ne);
+    } else {
+        // check data
+        if (vertexCount.size() == 0) throw DeadlyImportError("LineSet must contain not empty \"vertexCount\" attribute.");
+
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementSet(X3DElemType::ENET_LineSet, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne);
+
+        ne_alias.VertexCount = vertexCount;
+        // create CoordIdx
+        size_t coord_num = 0;
+
+        ne_alias.CoordIndex.clear();
+        for (std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) {
+            if (*vc_it < 2) throw DeadlyImportError("LineSet. vertexCount shall be greater than or equal to two.");
+
+            for (int32_t i = 0; i < *vc_it; i++)
+                ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num++)); // add vertices indices
+
+            ne_alias.CoordIndex.push_back(-1); // add face delimiter.
+        }
+
+        // check for child nodes
+        if (!isNodeEmpty(node)) {
+            ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                // check for X3DComposedGeometryNodes
+                if (currentChildName == "Color")
+                    readColor(currentChildNode);
+                else if (currentChildName == "ColorRGBA")
+                    readColorRGBA(currentChildNode);
+                else if (currentChildName == "Coordinate")
+                    readCoordinate(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode))
+                    skipUnsupportedNode("LineSet", currentChildNode);
+            }
+            ParseHelper_Node_Exit();
+        } // if(!isNodeEmpty(node))
+        else {
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+        }
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <PointSet
+// DEF="" ID
+// USE="" IDREF
+// >
+//    <!-- ColorCoordinateContentModel -->
+// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can
+// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </PointSet>
+void X3DImporter::readPointSet(XmlNode &node) {
+    std::string use, def;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_PointSet, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_PointSet, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        // check for child nodes
+        if (!isNodeEmpty(node)) {
+            ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                // check for X3DComposedGeometryNodes
+                if (currentChildName == "Color")
+                    readColor(currentChildNode);
+                else if (currentChildName == "ColorRGBA")
+                    readColorRGBA(currentChildNode);
+                else if (currentChildName == "Coordinate")
+                    readCoordinate(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode))
+                    skipUnsupportedNode("PointSet", currentChildNode);
+            }
+            ParseHelper_Node_Exit();
+        } // if(!isNodeEmpty(node))
+        else {
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+        }
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <TriangleFanSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// fanCount=""            MFInt32 [inputOutput]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </TriangleFanSet>
+void X3DImporter::readTriangleFanSet(XmlNode &node) {
+    std::string use, def;
+    bool ccw = true;
+    bool colorPerVertex = true;
+    std::vector<int32_t> fanCount;
+    bool normalPerVertex = true;
+    bool solid = true;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "ccw", ccw);
+    XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "fanCount", fanCount);
+    XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleFanSet, ne);
+    } else {
+        // check data
+        if (fanCount.size() == 0) throw DeadlyImportError("TriangleFanSet must contain not empty \"fanCount\" attribute.");
+
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleFanSet, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne);
+
+        ne_alias.CCW = ccw;
+        ne_alias.ColorPerVertex = colorPerVertex;
+        ne_alias.VertexCount = fanCount;
+        ne_alias.NormalPerVertex = normalPerVertex;
+        ne_alias.Solid = solid;
+        // create CoordIdx
+        size_t coord_num_first, coord_num_prev;
+
+        ne_alias.CoordIndex.clear();
+        // assign indices for first triangle
+        coord_num_first = 0;
+        coord_num_prev = 1;
+        for (std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) {
+            if (*vc_it < 3) throw DeadlyImportError("TriangleFanSet. fanCount shall be greater than or equal to three.");
+
+            for (int32_t vc = 2; vc < *vc_it; vc++) {
+                if (ccw) {
+                    // 2 1
+                    //  0
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_first)); // first vertex is a center and always is [0].
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev++));
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev));
+                } else {
+                    // 1 2
+                    //  0
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_first)); // first vertex is a center and always is [0].
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev + 1));
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num_prev++));
+                } // if(ccw) else
+
+                ne_alias.CoordIndex.push_back(-1); // add face delimiter.
+            } // for(int32_t vc = 2; vc < *vc_it; vc++)
+
+            coord_num_prev++; // that index will be center of next fan
+            coord_num_first = coord_num_prev++; // forward to next point - second point of fan
+        } // for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+        // check for child nodes
+        if (!isNodeEmpty(node)) {
+            ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                // check for X3DComposedGeometryNodes
+                if (currentChildName == "Color")
+                    readColor(currentChildNode);
+                else if (currentChildName == "ColorRGBA")
+                    readColorRGBA(currentChildNode);
+                else if (currentChildName == "Coordinate")
+                    readCoordinate(currentChildNode);
+                else if (currentChildName == "Normal")
+                    readNormal(currentChildNode);
+                else if (currentChildName == "TextureCoordinate")
+                    readTextureCoordinate(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode))
+                    skipUnsupportedNode("TriangleFanSet", currentChildNode);
+            }
+            ParseHelper_Node_Exit();
+        } // if(!isNodeEmpty(node))
+        else {
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+        }
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <TriangleSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </TriangleSet>
+void X3DImporter::readTriangleSet(XmlNode &node) {
+    std::string use, def;
+    bool ccw = true;
+    bool colorPerVertex = true;
+    bool normalPerVertex = true;
+    bool solid = true;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "ccw", ccw);
+    XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
+    XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleSet, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_TriangleSet, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne);
+
+        ne_alias.CCW = ccw;
+        ne_alias.ColorPerVertex = colorPerVertex;
+        ne_alias.NormalPerVertex = normalPerVertex;
+        ne_alias.Solid = solid;
+        // check for child nodes
+        if (!isNodeEmpty(node)) {
+            ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                // check for X3DComposedGeometryNodes
+                if (currentChildName == "Color")
+                    readColor(currentChildNode);
+                else if (currentChildName == "ColorRGBA")
+                    readColorRGBA(currentChildNode);
+                else if (currentChildName == "Coordinate")
+                    readCoordinate(currentChildNode);
+                else if (currentChildName == "Normal")
+                    readNormal(currentChildNode);
+                else if (currentChildName == "TextureCoordinate")
+                    readTextureCoordinate(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode))
+                    skipUnsupportedNode("TriangleSet", currentChildNode);
+            }
+            ParseHelper_Node_Exit();
+        } // if(!isNodeEmpty(node))
+        else {
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+        }
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <TriangleStripSet
+// DEF=""                 ID
+// USE=""                 IDREF
+// ccw="true"             SFBool  [initializeOnly]
+// colorPerVertex="true"  SFBool  [initializeOnly]
+// normalPerVertex="true" SFBool  [initializeOnly]
+// solid="true"           SFBool  [initializeOnly]
+// stripCount=""          MFInt32 [inputOutput]
+// >
+//    <!-- ComposedGeometryContentModel -->
+// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate,
+// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute,
+// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained.
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
+// </TriangleStripSet>
+void X3DImporter::readTriangleStripSet(XmlNode &node) {
+    std::string use, def;
+    bool ccw = true;
+    bool colorPerVertex = true;
+    std::vector<int32_t> stripCount;
+    bool normalPerVertex = true;
+    bool solid = true;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "ccw", ccw);
+    XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex);
+    X3DXmlHelper::getInt32ArrayAttribute(node, "stripCount", stripCount);
+    XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex);
+    XmlParser::getBoolAttribute(node, "solid", solid);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleStripSet, ne);
+    } else {
+        // check data
+        if (stripCount.size() == 0) throw DeadlyImportError("TriangleStripSet must contain not empty \"stripCount\" attribute.");
+
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleStripSet, mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne);
+
+        ne_alias.CCW = ccw;
+        ne_alias.ColorPerVertex = colorPerVertex;
+        ne_alias.VertexCount = stripCount;
+        ne_alias.NormalPerVertex = normalPerVertex;
+        ne_alias.Solid = solid;
+        // create CoordIdx
+        size_t coord_num0, coord_num1, coord_num2; // indices of current triangle
+        bool odd_tri; // sequence of current triangle
+        size_t coord_num_sb; // index of first point of strip
+
+        ne_alias.CoordIndex.clear();
+        coord_num_sb = 0;
+        for (std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) {
+            if (*vc_it < 3) throw DeadlyImportError("TriangleStripSet. stripCount shall be greater than or equal to three.");
+
+            // set initial values for first triangle
+            coord_num0 = coord_num_sb;
+            coord_num1 = coord_num_sb + 1;
+            coord_num2 = coord_num_sb + 2;
+            odd_tri = true;
+
+            for (int32_t vc = 2; vc < *vc_it; vc++) {
+                if (ccw) {
+                    // 0 2
+                    //  1
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num0));
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num1));
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num2));
+                } else {
+                    // 0 1
+                    //  2
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num0));
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num2));
+                    ne_alias.CoordIndex.push_back(static_cast<int32_t>(coord_num1));
+                } // if(ccw) else
+
+                ne_alias.CoordIndex.push_back(-1); // add face delimiter.
+                // prepare values for next triangle
+                if (odd_tri) {
+                    coord_num0 = coord_num2;
+                    coord_num2++;
+                } else {
+                    coord_num1 = coord_num2;
+                    coord_num2 = coord_num1 + 1;
+                }
+
+                odd_tri = !odd_tri;
+                coord_num_sb = coord_num2; // that index will be start of next strip
+            } // for(int32_t vc = 2; vc < *vc_it; vc++)
+        } // for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+        // check for child nodes
+        if (!isNodeEmpty(node)) {
+            ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                // check for X3DComposedGeometryNodes
+                if (currentChildName == "Color")
+                    readColor(currentChildNode);
+                else if (currentChildName == "ColorRGBA")
+                    readColorRGBA(currentChildNode);
+                else if (currentChildName == "Coordinate")
+                    readCoordinate(currentChildNode);
+                else if (currentChildName == "Normal")
+                    readNormal(currentChildNode);
+                else if (currentChildName == "TextureCoordinate")
+                    readTextureCoordinate(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode))
+                    skipUnsupportedNode("TriangleStripSet", currentChildNode);
+            }
+            ParseHelper_Node_Exit();
+        } // if(!isNodeEmpty(node))
+        else {
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+        }
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <Normal
+// DEF=""      ID
+// USE=""      IDREF
+// vector=""   MFVec3f [inputOutput]
+// />
+void X3DImporter::readNormal(XmlNode &node) {
+    std::string use, def;
+    std::list<aiVector3D> vector;
+    X3DNodeElementBase *ne;
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getVector3DListAttribute(node, "vector", vector);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Normal, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementNormal(mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        ((X3DNodeElementNormal *)ne)->Value = vector;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "Normal");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 221 - 0
code/AssetLib/X3D/X3DImporter_Shape.cpp

@@ -0,0 +1,221 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file   X3DImporter_Shape.cpp
+/// \brief  Parsing data from nodes of "Shape" set of X3D.
+/// \date   2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+#include "X3DXmlHelper.h"
+
+namespace Assimp {
+
+void X3DImporter::readShape(XmlNode &node) {
+    std::string use, def;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Shape, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementShape(mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        // check for child nodes
+        if (!isNodeEmpty(node)) {
+            ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                // check for appearance node
+                if (currentChildName == "Appearance") readAppearance(currentChildNode);
+                // check for X3DGeometryNodes
+                else if (currentChildName == "Arc2D") readArc2D(currentChildNode);
+                else if (currentChildName == "ArcClose2D") readArcClose2D(currentChildNode);
+                else if (currentChildName == "Circle2D") readCircle2D(currentChildNode);
+                else if (currentChildName == "Disk2D") readDisk2D(currentChildNode);
+                else if (currentChildName == "Polyline2D") readPolyline2D(currentChildNode);
+                else if (currentChildName == "Polypoint2D") readPolypoint2D(currentChildNode);
+                else if (currentChildName == "Rectangle2D") readRectangle2D(currentChildNode);
+                else if (currentChildName == "TriangleSet2D") readTriangleSet2D(currentChildNode);
+                else if (currentChildName == "Box") readBox(currentChildNode);
+                else if (currentChildName == "Cone") readCone(currentChildNode);
+                else if (currentChildName == "Cylinder") readCylinder(currentChildNode);
+                else if (currentChildName == "ElevationGrid") readElevationGrid(currentChildNode);
+                else if (currentChildName == "Extrusion") readExtrusion(currentChildNode);
+                else if (currentChildName == "IndexedFaceSet") readIndexedFaceSet(currentChildNode);
+                else if (currentChildName == "Sphere") readSphere(currentChildNode);
+                else if (currentChildName == "IndexedLineSet") readIndexedLineSet(currentChildNode);
+                else if (currentChildName == "LineSet") readLineSet(currentChildNode);
+                else if (currentChildName == "PointSet") readPointSet(currentChildNode);
+                else if (currentChildName == "IndexedTriangleFanSet") readIndexedTriangleFanSet(currentChildNode);
+                else if (currentChildName == "IndexedTriangleSet") readIndexedTriangleSet(currentChildNode);
+                else if (currentChildName == "IndexedTriangleStripSet") readIndexedTriangleStripSet(currentChildNode);
+                else if (currentChildName == "TriangleFanSet") readTriangleFanSet(currentChildNode);
+                else if (currentChildName == "TriangleSet") readTriangleSet(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("Shape", currentChildNode);
+            }
+
+            ParseHelper_Node_Exit();
+        } // if (!isNodeEmpty(node))
+        else {
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+        }
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <Appearance
+// DEF="" ID
+// USE="" IDREF
+// >
+// <!-- AppearanceChildContentModel -->
+// "Child-node content model corresponding to X3DAppearanceChildNode. Appearance can contain FillProperties, LineProperties, Material, any Texture node and
+// any TextureTransform node, in any order. No more than one instance of these nodes is allowed. Appearance may also contain multiple shaders (ComposedShader,
+// PackagedShader, ProgramShader).
+// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model."
+// </Appearance>
+void X3DImporter::readAppearance(XmlNode &node) {
+    std::string use, def;
+    X3DNodeElementBase* ne( nullptr );
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+
+	// if "USE" defined then find already defined element.
+    if (!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Appearance, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new X3DNodeElementAppearance(mNodeElementCur);
+		if(!def.empty()) ne->ID = def;
+
+        // check for child nodes
+        if(!isNodeEmpty(node))
+        {
+			ParseHelper_Node_Enter(ne);
+            for (auto currentChildNode : node.children()) {
+                const std::string &currentChildName = currentChildNode.name();
+                if (currentChildName == "Material") readMaterial(currentChildNode);
+                else if (currentChildName == "ImageTexture") readImageTexture(currentChildNode);
+                else if (currentChildName == "TextureTransform") readTextureTransform(currentChildNode);
+                // check for X3DMetadataObject
+                else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("Appearance", currentChildNode);
+            }
+			ParseHelper_Node_Exit();
+		}// if(!isNodeEmpty(node))
+		else
+		{
+			mNodeElementCur->Children.push_back(ne);// add made object as child to current element
+		}
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+// <Material
+// DEF=""                     ID
+// USE=""                     IDREF
+// ambientIntensity="0.2"     SFFloat [inputOutput]
+// diffuseColor="0.8 0.8 0.8" SFColor [inputOutput]
+// emissiveColor="0 0 0"      SFColor [inputOutput]
+// shininess="0.2"            SFFloat [inputOutput]
+// specularColor="0 0 0"      SFColor [inputOutput]
+// transparency="0"           SFFloat [inputOutput]
+// />
+void X3DImporter::readMaterial(XmlNode &node) {
+    std::string use, def;
+    float ambientIntensity = 0.2f;
+    float shininess = 0.2f;
+    float transparency = 0;
+    aiColor3D diffuseColor(0.8f, 0.8f, 0.8f);
+    aiColor3D emissiveColor(0, 0, 0);
+    aiColor3D specularColor(0, 0, 0);
+    X3DNodeElementBase* ne( nullptr );
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity);
+    XmlParser::getFloatAttribute(node, "shininess", shininess);
+    XmlParser::getFloatAttribute(node, "transparency", transparency);
+    X3DXmlHelper::getColor3DAttribute(node, "diffuseColor", diffuseColor);
+    X3DXmlHelper::getColor3DAttribute(node, "emissiveColor", emissiveColor);
+    X3DXmlHelper::getColor3DAttribute(node, "specularColor", specularColor);
+
+	// if "USE" defined then find already defined element.
+	if(!use.empty())
+	{
+		MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Material, ne);
+	}
+	else
+	{
+		// create and if needed - define new geometry object.
+		ne = new X3DNodeElementMaterial(mNodeElementCur);
+		if(!def.empty()) ne->ID = def;
+
+		((X3DNodeElementMaterial *)ne)->AmbientIntensity = ambientIntensity;
+        ((X3DNodeElementMaterial *)ne)->Shininess = shininess;
+        ((X3DNodeElementMaterial *)ne)->Transparency = transparency;
+        ((X3DNodeElementMaterial *)ne)->DiffuseColor = diffuseColor;
+        ((X3DNodeElementMaterial *)ne)->EmissiveColor = emissiveColor;
+        ((X3DNodeElementMaterial *)ne)->SpecularColor = specularColor;
+        // check for child nodes
+		if(!isNodeEmpty(node))
+			childrenReadMetadata(node, ne, "Material");
+		else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+		NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph
+	}// if(!use.empty()) else
+}
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 179 - 0
code/AssetLib/X3D/X3DImporter_Texturing.cpp

@@ -0,0 +1,179 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/// \file   X3DImporter_Texturing.cpp
+/// \brief  Parsing data from nodes of "Texturing" set of X3D.
+/// \date   2015-2016
+/// \author [email protected]
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "X3DImporter.hpp"
+#include "X3DImporter_Macro.hpp"
+#include "X3DXmlHelper.h"
+
+namespace Assimp {
+
+// <ImageTexture
+// DEF=""         ID
+// USE=""         IDREF
+// repeatS="true" SFBool
+// repeatT="true" SFBool
+// url=""         MFString
+// />
+// When the url field contains no values ([]), texturing is disabled.
+void X3DImporter::readImageTexture(XmlNode &node) {
+    std::string use, def;
+    bool repeatS = true;
+    bool repeatT = true;
+    std::list<std::string> url;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    XmlParser::getBoolAttribute(node, "repeatS", repeatS);
+    XmlParser::getBoolAttribute(node, "repeatT", repeatT);
+    X3DXmlHelper::getStringListAttribute(node, "url", url);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ImageTexture, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementImageTexture(mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        ((X3DNodeElementImageTexture *)ne)->RepeatS = repeatS;
+        ((X3DNodeElementImageTexture *)ne)->RepeatT = repeatT;
+        // Attribute "url" can contain list of strings. But we need only one - first.
+        if (!url.empty())
+            ((X3DNodeElementImageTexture *)ne)->URL = url.front();
+        else
+            ((X3DNodeElementImageTexture *)ne)->URL = "";
+
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "ImageTexture");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <TextureCoordinate
+// DEF=""      ID
+// USE=""      IDREF
+// point=""    MFVec3f [inputOutput]
+// />
+void X3DImporter::readTextureCoordinate(XmlNode &node) {
+    std::string use, def;
+    std::list<aiVector2D> point;
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getVector2DListAttribute(node, "point", point);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TextureCoordinate, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementTextureCoordinate(mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        ((X3DNodeElementTextureCoordinate *)ne)->Value = point;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "TextureCoordinate");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+// <TextureTransform
+// DEF=""            ID
+// USE=""            IDREF
+// center="0 0"      SFVec2f [inputOutput]
+// rotation="0"      SFFloat [inputOutput]
+// scale="1 1"       SFVec2f [inputOutput]
+// translation="0 0" SFVec2f [inputOutput]
+// />
+void X3DImporter::readTextureTransform(XmlNode &node) {
+    std::string use, def;
+    aiVector2D center(0, 0);
+    float rotation = 0;
+    aiVector2D scale(1, 1);
+    aiVector2D translation(0, 0);
+    X3DNodeElementBase *ne(nullptr);
+
+    MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use);
+    X3DXmlHelper::getVector2DAttribute(node, "center", center);
+    XmlParser::getFloatAttribute(node, "rotation", rotation);
+    X3DXmlHelper::getVector2DAttribute(node, "scale", scale);
+    X3DXmlHelper::getVector2DAttribute(node, "translation", translation);
+
+    // if "USE" defined then find already defined element.
+    if (!use.empty()) {
+        MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TextureTransform, ne);
+    } else {
+        // create and if needed - define new geometry object.
+        ne = new X3DNodeElementTextureTransform(mNodeElementCur);
+        if (!def.empty()) ne->ID = def;
+
+        ((X3DNodeElementTextureTransform *)ne)->Center = center;
+        ((X3DNodeElementTextureTransform *)ne)->Rotation = rotation;
+        ((X3DNodeElementTextureTransform *)ne)->Scale = scale;
+        ((X3DNodeElementTextureTransform *)ne)->Translation = translation;
+        // check for X3DMetadataObject childs.
+        if (!isNodeEmpty(node))
+            childrenReadMetadata(node, ne, "TextureTransform");
+        else
+            mNodeElementCur->Children.push_back(ne); // add made object as child to current element
+
+        NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph
+    } // if(!use.empty()) else
+}
+
+} // namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 294 - 0
code/AssetLib/X3D/X3DXmlHelper.cpp

@@ -0,0 +1,294 @@
+#include "X3DXmlHelper.h"
+#include "X3DImporter.hpp"
+
+#include <assimp/ParsingUtils.h>
+
+namespace Assimp {
+
+bool X3DXmlHelper::getColor3DAttribute(XmlNode &node, const char *attributeName, aiColor3D &color) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        if (values.size() != 3) {
+            Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
+            return false;
+        }
+        auto it = values.begin();
+        color.r = stof(*it++);
+        color.g = stof(*it++);
+        color.b = stof(*it);
+        return true;
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getVector2DAttribute(XmlNode &node, const char *attributeName, aiVector2D &color) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        if (values.size() != 2) {
+            Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
+            return false;
+        }
+        auto it = values.begin();
+        color.x = stof(*it++);
+        color.y = stof(*it);
+        return true;
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getVector3DAttribute(XmlNode &node, const char *attributeName, aiVector3D &color) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        if (values.size() != 3) {
+            Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
+            return false;
+        }
+        auto it = values.begin();
+        color.x = stof(*it++);
+        color.y = stof(*it++);
+        color.z = stof(*it);
+        return true;
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getBooleanArrayAttribute(XmlNode &node, const char *attributeName, std::vector<bool> &boolArray) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        auto it = values.begin();
+        while (it != values.end()) {
+            auto s = *it++;
+            if (!s.empty())
+                boolArray.push_back(s[0] == 't' || s[0] == '1');
+            else
+                Throw_ConvertFail_Str2ArrB(node.name(), attributeName);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getDoubleArrayAttribute(XmlNode &node, const char *attributeName, std::vector<double> &doubleArray) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        auto it = values.begin();
+        while (it != values.end()) {
+            auto s = *it++;
+            if (!s.empty())
+                doubleArray.push_back(atof(s.c_str()));
+            else
+                Throw_ConvertFail_Str2ArrD(node.name(), attributeName);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getFloatArrayAttribute(XmlNode &node, const char *attributeName, std::vector<float> &floatArray) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        auto it = values.begin();
+        while (it != values.end()) {
+            auto s = *it++;
+            if (!s.empty())
+                floatArray.push_back((float)atof(s.c_str()));
+            else
+                Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getInt32ArrayAttribute(XmlNode &node, const char *attributeName, std::vector<int32_t> &intArray) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        auto it = values.begin();
+        while (it != values.end()) {
+            auto s = *it++;
+            if (!s.empty())
+                intArray.push_back((int32_t)atof(s.c_str()));
+            else
+                Throw_ConvertFail_Str2ArrI(node.name(), attributeName);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getStringListAttribute(XmlNode &node, const char *attributeName, std::list<std::string> &stringList) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        auto it = values.begin();
+        std::string currentConcat = "";
+        bool inQuotes = false;
+        while (it != values.end()) {
+            auto s = *it++;
+            if (!s.empty()) {
+                if (inQuotes) {
+                    if (*(s.rbegin()) == '"') {
+                        stringList.push_back(currentConcat + s.substr(0, s.length() - 1));
+                        currentConcat = "";
+                        inQuotes = false;
+                    } else {
+                        currentConcat += " " + s;
+                    }
+                } else {
+                    if (s[0] == '"') {
+                        currentConcat = s.substr(1);
+                        inQuotes = true;
+                    } else {
+                        stringList.push_back(s);
+                    }
+                }
+            } else if (!inQuotes)
+                Throw_ConvertFail_Str2ArrI(node.name(), attributeName);
+        }
+        if (inQuotes) Throw_ConvertFail_Str2ArrI(node.name(), attributeName);
+        return true;
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getStringArrayAttribute(XmlNode &node, const char *attributeName, std::vector<std::string> &stringArray) {
+    std::list<std::string> tlist;
+
+    if (getStringListAttribute(node, attributeName, tlist)) {
+        if (!tlist.empty()) {
+            stringArray.reserve(tlist.size());
+            for (std::list<std::string>::iterator it = tlist.begin(); it != tlist.end(); ++it) {
+                stringArray.push_back(*it);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getVector2DListAttribute(XmlNode &node, const char *attributeName, std::list<aiVector2D> &vectorList) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        if (values.size() % 2) Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
+        auto it = values.begin();
+        while (it != values.end()) {
+            aiVector2D tvec;
+
+            tvec.x = (float)atof((*it++).c_str());
+            tvec.y = (float)atof((*it++).c_str());
+            vectorList.push_back(tvec);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getVector2DArrayAttribute(XmlNode &node, const char *attributeName, std::vector<aiVector2D> &vectorArray) {
+    std::list<aiVector2D> tlist;
+
+    if (getVector2DListAttribute(node, attributeName, tlist)) {
+        if (!tlist.empty()) {
+            vectorArray.reserve(tlist.size());
+            for (std::list<aiVector2D>::iterator it = tlist.begin(); it != tlist.end(); ++it) {
+                vectorArray.push_back(*it);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getVector3DListAttribute(XmlNode &node, const char *attributeName, std::list<aiVector3D> &vectorList) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        if (values.size() % 3 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
+        auto it = values.begin();
+        while (it != values.end()) {
+            aiVector3D tvec;
+
+            tvec.x = (float)atof((*it++).c_str());
+            tvec.y = (float)atof((*it++).c_str());
+            tvec.z = (float)atof((*it++).c_str());
+            vectorList.push_back(tvec);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getVector3DArrayAttribute(XmlNode &node, const char *attributeName, std::vector<aiVector3D> &vectorArray) {
+    std::list<aiVector3D> tlist;
+
+    if (getVector3DListAttribute(node, attributeName, tlist)) {
+        if (!tlist.empty()) {
+            vectorArray.reserve(tlist.size());
+            for (std::list<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); ++it) {
+                vectorArray.push_back(*it);
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getColor3DListAttribute(XmlNode &node, const char *attributeName, std::list<aiColor3D> &colorList) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        if (values.size() % 3 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
+        auto it = values.begin();
+        while (it != values.end()) {
+            aiColor3D tvec;
+
+            tvec.r = (float)atof((*it++).c_str());
+            tvec.g = (float)atof((*it++).c_str());
+            tvec.b = (float)atof((*it++).c_str());
+            colorList.push_back(tvec);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool X3DXmlHelper::getColor4DListAttribute(XmlNode &node, const char *attributeName, std::list<aiColor4D> &colorList) {
+    std::string val;
+    if (XmlParser::getStdStrAttribute(node, attributeName, val)) {
+        std::vector<std::string> values;
+        tokenize<std::string>(val, values, " ");
+        if (values.size() % 4 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
+        auto it = values.begin();
+        while (it != values.end()) {
+            aiColor4D tvec;
+
+            tvec.r = (float)atof((*it++).c_str());
+            tvec.g = (float)atof((*it++).c_str());
+            tvec.b = (float)atof((*it++).c_str());
+            tvec.a = (float)atof((*it++).c_str());
+            colorList.push_back(tvec);
+        }
+        return true;
+    }
+    return false;
+}
+
+} // namespace Assimp

+ 30 - 0
code/AssetLib/X3D/X3DXmlHelper.h

@@ -0,0 +1,30 @@
+#pragma once
+
+#include <assimp/XmlParser.h>
+#include <assimp/types.h>
+#include <list>
+
+namespace Assimp {
+
+class X3DXmlHelper {
+public:
+    static bool getColor3DAttribute(XmlNode &node, const char *attributeName, aiColor3D &color);
+    static bool getVector2DAttribute(XmlNode &node, const char *attributeName, aiVector2D &vector);
+    static bool getVector3DAttribute(XmlNode &node, const char *attributeName, aiVector3D &vector);
+
+    static bool getBooleanArrayAttribute(XmlNode &node, const char *attributeName, std::vector<bool> &boolArray);
+    static bool getDoubleArrayAttribute(XmlNode &node, const char *attributeName, std::vector<double> &doubleArray);
+    static bool getFloatArrayAttribute(XmlNode &node, const char *attributeName, std::vector<float> &floatArray);
+    static bool getInt32ArrayAttribute(XmlNode &node, const char *attributeName, std::vector<int32_t> &intArray);
+    static bool getStringListAttribute(XmlNode &node, const char *attributeName, std::list<std::string> &stringArray);
+    static bool getStringArrayAttribute(XmlNode &node, const char *attributeName, std::vector<std::string> &stringArray);
+
+    static bool getVector2DListAttribute(XmlNode &node, const char *attributeName, std::list<aiVector2D> &vectorList);
+    static bool getVector2DArrayAttribute(XmlNode &node, const char *attributeName, std::vector<aiVector2D> &vectorArray);
+    static bool getVector3DListAttribute(XmlNode &node, const char *attributeName, std::list<aiVector3D> &vectorList);
+    static bool getVector3DArrayAttribute(XmlNode &node, const char *attributeName, std::vector<aiVector3D> &vectorArray);
+    static bool getColor3DListAttribute(XmlNode &node, const char *attributeName, std::list<aiColor3D> &colorList);
+    static bool getColor4DListAttribute(XmlNode &node, const char *attributeName, std::list<aiColor4D> &colorList);
+};
+
+} // namespace Assimp

+ 14 - 0
code/CMakeLists.txt

@@ -800,9 +800,23 @@ ADD_ASSIMP_IMPORTER( X
 
 ADD_ASSIMP_IMPORTER( X3D
   AssetLib/X3D/X3DImporter.cpp
+  AssetLib/X3D/X3DImporter_Geometry2D.cpp
+  AssetLib/X3D/X3DImporter_Geometry3D.cpp
+  AssetLib/X3D/X3DImporter_Group.cpp
+  AssetLib/X3D/X3DImporter_Light.cpp
+  AssetLib/X3D/X3DImporter_Metadata.cpp
+  AssetLib/X3D/X3DImporter_Networking.cpp
+  AssetLib/X3D/X3DImporter_Postprocess.cpp
+  AssetLib/X3D/X3DImporter_Rendering.cpp
+  AssetLib/X3D/X3DImporter_Shape.cpp
+  AssetLib/X3D/X3DImporter_Texturing.cpp
   AssetLib/X3D/X3DImporter.hpp
+  AssetLib/X3D/X3DImporter_Macro.hpp
+  AssetLib/X3D/X3DImporter_Node.hpp
   AssetLib/X3D/X3DGeoHelper.cpp
   AssetLib/X3D/X3DGeoHelper.h
+  AssetLib/X3D/X3DXmlHelper.cpp
+  AssetLib/X3D/X3DXmlHelper.h
 )
 
 ADD_ASSIMP_IMPORTER( GLTF

+ 1 - 3
code/Common/ImporterRegistry.cpp

@@ -365,9 +365,7 @@ void GetImporterInstanceList(std::vector<BaseImporter *> &out) {
     out.push_back(new D3MFImporter());
 #endif
 #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
-    if (devImportersEnabled) { // https://github.com/assimp/assimp/issues/3647
-        out.push_back(new X3DImporter());
-    }
+    out.push_back(new X3DImporter());
 #endif
 #ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
     out.push_back(new MMDImporter());