From 394651e640699e09470ab7664576bfc3a543edf9 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 10 Aug 2020 22:13:45 +0200 Subject: [PATCH] x3d: migration of goups. --- code/AssetLib/Obj/ObjTools.h | 29 - code/AssetLib/X3D/X3DImporter.cpp | 39 -- code/AssetLib/X3D/X3DImporter.hpp | 49 +- code/AssetLib/X3D/X3DImporter_Group.cpp | 181 ++++-- code/AssetLib/XGL/XGLLoader.cpp | 740 +----------------------- code/AssetLib/XGL/XGLLoader.h | 5 +- include/assimp/ParsingUtils.h | 31 + 7 files changed, 215 insertions(+), 859 deletions(-) diff --git a/code/AssetLib/Obj/ObjTools.h b/code/AssetLib/Obj/ObjTools.h index 16bcee5c4..61efb98b2 100644 --- a/code/AssetLib/Obj/ObjTools.h +++ b/code/AssetLib/Obj/ObjTools.h @@ -234,35 +234,6 @@ inline char_t getFloat(char_t it, char_t end, ai_real &value) { return it; } -/** @brief Will perform a simple tokenize. - * @param str String to tokenize. - * @param tokens Array with tokens, will be empty if no token was found. - * @param delimiters Delimiter for tokenize. - * @return Number of found token. - */ -template -unsigned int tokenize(const string_type &str, std::vector &tokens, - const string_type &delimiters) { - // Skip delimiters at beginning. - typename string_type::size_type lastPos = str.find_first_not_of(delimiters, 0); - - // Find first "non-delimiter". - typename string_type::size_type pos = str.find_first_of(delimiters, lastPos); - while (string_type::npos != pos || string_type::npos != lastPos) { - // Found a token, add it to the vector. - string_type tmp = str.substr(lastPos, pos - lastPos); - if (!tmp.empty() && ' ' != tmp[0]) - tokens.push_back(tmp); - - // Skip delimiters. Note the "not_of" - lastPos = str.find_first_not_of(delimiters, pos); - - // Find next "non-delimiter" - pos = str.find_first_of(delimiters, lastPos); - } - - return static_cast(tokens.size()); -} template string_type trim_whitespaces(string_type str) { diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index f382ea10e..fd61fc62c 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -79,46 +79,7 @@ const aiImporterDesc X3DImporter::Description = { //const std::regex X3DImporter::pattern_true(R"(^\s*(?:true|1)\s*$)", std::regex::icase); namespace { -static void Throw_ArgOutOfRange(const std::string &argument) { - throw DeadlyImportError("Argument value is out of range for: \"" + argument + "\"."); -} -static void Throw_CloseNotFound(const std::string &node) { - throw DeadlyImportError("Close tag for node <" + node + "> not found. Seems file is corrupt."); -} - -static void Throw_ConvertFail_Str2ArrF(const std::string &nodeName, const std::string &pAttrValue) { - throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + - "\" from string to array of floats."); -} - -static void Throw_DEF_And_USE(const std::string &nodeName) { - throw DeadlyImportError("\"DEF\" and \"USE\" can not be defined both in <" + nodeName + ">."); -} - -static void Throw_IncorrectAttr(const std::string &nodeName, const std::string &pAttrName) { - throw DeadlyImportError("Node <" + nodeName + "> has incorrect attribute \"" + pAttrName + "\"."); -} - -static void Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &pAttrName) { - throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + nodeName + "> has incorrect value."); -} - -static void Throw_MoreThanOnceDefined(const std::string &nodeName, const std::string &pNodeType, const std::string &pDescription) { - throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + nodeName + ". Description: " + pDescription); -} - -static void Throw_TagCountIncorrect(const std::string &pNode) { - throw DeadlyImportError("Count of open and close tags for node <" + pNode + "> are not equivalent. Seems file is corrupt."); -} - -static void Throw_USE_NotFound(const std::string &nodeName, const std::string &pAttrValue) { - throw DeadlyImportError("Not found node with name \"" + pAttrValue + "\" in <" + nodeName + ">."); -} - -static void LogInfo(const std::string &message) { - DefaultLogger::get()->info(message); -} } // namespace diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index e4401f27d..1e8127ab8 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -62,6 +62,49 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { +inline void Throw_ArgOutOfRange(const std::string &argument) { + throw DeadlyImportError("Argument value is out of range for: \"" + argument + "\"."); +} + +inline void Throw_CloseNotFound(const std::string &node) { + throw DeadlyImportError("Close tag for node <" + node + "> not found. Seems file is corrupt."); +} + +inline void Throw_ConvertFail_Str2ArrF(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of floats."); +} + +inline void Throw_DEF_And_USE(const std::string &nodeName) { + throw DeadlyImportError("\"DEF\" and \"USE\" can not be defined both in <" + nodeName + ">."); +} + +inline void Throw_IncorrectAttr(const std::string &nodeName, const std::string &pAttrName) { + throw DeadlyImportError("Node <" + nodeName + "> has incorrect attribute \"" + pAttrName + "\"."); +} + +inline void Throw_IncorrectAttrValue(const std::string &nodeName, const std::string &pAttrName) { + throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + nodeName + "> has incorrect value."); +} + +inline void Throw_MoreThanOnceDefined(const std::string &nodeName, const std::string &pNodeType, const std::string &pDescription) { + throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + nodeName + ". Description: " + pDescription); +} + +inline void Throw_TagCountIncorrect(const std::string &pNode) { + throw DeadlyImportError("Count of open and close tags for node <" + pNode + "> are not equivalent. Seems file is corrupt."); +} + +inline void Throw_USE_NotFound(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("Not found node with name \"" + pAttrValue + "\" in <" + nodeName + ">."); +} + +inline void LogInfo(const std::string &message) { + DefaultLogger::get()->info(message); +} + + + /// \class X3DImporter /// Class that holding scene graph which include: groups, geometry, metadata etc. /// @@ -650,19 +693,19 @@ private: void ParseNode_Grouping_GroupEnd(); /// Parse node of the file. And create new node in scene graph. - void ParseNode_Grouping_StaticGroup(); + void ParseNode_Grouping_StaticGroup(XmlNode &node); /// Doing actions at an exit from . Walk up in scene graph. void ParseNode_Grouping_StaticGroupEnd(); /// Parse node of the file. And create new node in scene graph. - void ParseNode_Grouping_Switch(); + void ParseNode_Grouping_Switch(XmlNode &node); /// Doing actions at an exit from . Walk up in scene graph. void ParseNode_Grouping_SwitchEnd(); /// Parse node of the file. And create new node in scene graph. - void ParseNode_Grouping_Transform(); + void ParseNode_Grouping_Transform(XmlNode &node); /// Doing actions at an exit from . Walk up in scene graph. void ParseNode_Grouping_TransformEnd(); diff --git a/code/AssetLib/X3D/X3DImporter_Group.cpp b/code/AssetLib/X3D/X3DImporter_Group.cpp index 5409a88b3..60dfc4cc0 100644 --- a/code/AssetLib/X3D/X3DImporter_Group.cpp +++ b/code/AssetLib/X3D/X3DImporter_Group.cpp @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -49,6 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "X3DImporter.hpp" #include "X3DImporter_Macro.hpp" +#include + namespace Assimp { @@ -65,30 +66,41 @@ namespace Assimp // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. // // A Group node contains children nodes without introducing a new transformation. It is equivalent to a Transform node containing an identity transform. -void X3DImporter::ParseNode_Grouping_Group() -{ - std::string def, use; +void X3DImporter::ParseNode_Grouping_Group(XmlNode &node) { + //std::string def, use; - MACRO_ATTRREAD_LOOPBEG; + std::string def = node.attribute("DEF").as_string(); + std::string use = node.attribute("USE").as_string(); + /*MACRO_ATTRREAD_LOOPBEG; MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_LOOPEND; + MACRO_ATTRREAD_LOOPEND;*/ // if "USE" defined then find already defined element. if(!use.empty()) { - X3DNodeElementBase* ne; - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } - else - { + X3DNodeElementBase *ne = nullptr; + if (def.empty()) { + Throw_DEF_And_USE(node.name()); + } + if (!FindNodeElement(use, X3DNodeElementBase::ENET_Group, &ne)) { + Throw_USE_NotFound(node.name(), use); + } + mNodeElementCur->Child.push_back(ne); + //MACRO_USE_CHECKANDAPPLY(def, use, X3DNodeElementBase::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; + + // 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 parser function. // for empty element exit from node in that place - if(mReader->isEmptyElement()) ParseHelper_Node_Exit(); + //if(mReader->isEmptyElement()) + if (node.empty()) { + ParseHelper_Node_Exit(); + } }// if(!use.empty()) else } @@ -111,20 +123,25 @@ void X3DImporter::ParseNode_Grouping_GroupEnd() // // 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; +void X3DImporter::ParseNode_Grouping_StaticGroup(XmlNode &node) { +// std::string def, use; + std::string def = node.attribute("DEF").as_string(); + std::string use = node.attribute("USE").as_string(); - MACRO_ATTRREAD_LOOPBEG; +/* MACRO_ATTRREAD_LOOPBEG; MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_LOOPEND; + MACRO_ATTRREAD_LOOPEND;*/ // if "USE" defined then find already defined element. if(!use.empty()) { - X3DNodeElementBase* ne; + X3DNodeElementBase* ne = nullptr; + if (!FindNodeElement(use, X3DNodeElementBase::ENET_Group, &ne)) { + Throw_USE_NotFound(node.name(), use); + } + mNodeElementCur->Child.push_back(ne); - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); +// MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); } else { @@ -134,7 +151,11 @@ void X3DImporter::ParseNode_Grouping_StaticGroup() // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. // for empty element exit from node in that place - if(mReader->isEmptyElement()) ParseHelper_Node_Exit(); + if (node.empty()) { + ParseHelper_Node_Exit(); + } + +// if(mReader->isEmptyElement()) ParseHelper_Node_Exit(); }// if(!use.empty()) else } @@ -159,22 +180,29 @@ void X3DImporter::ParseNode_Grouping_StaticGroupEnd() // 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; +void X3DImporter::ParseNode_Grouping_Switch(XmlNode &node) { +// std::string def, use; int32_t whichChoice = -1; - - MACRO_ATTRREAD_LOOPBEG; + std::string def = node.attribute("DEF").as_string(); + std::string use = node.attribute("USE").as_string(); + pugi::xml_attribute attr = node.attribute("whichChoise"); + whichChoice = attr.as_int(); + /*MACRO_ATTRREAD_LOOPBEG; MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); MACRO_ATTRREAD_CHECK_RET("whichChoice", whichChoice, XML_ReadNode_GetAttrVal_AsI32); - MACRO_ATTRREAD_LOOPEND; + MACRO_ATTRREAD_LOOPEND;*/ // if "USE" defined then find already defined element. if(!use.empty()) { - X3DNodeElementBase* ne; + X3DNodeElementBase* ne = nullptr; + if (!FindNodeElement(use, X3DNodeElementBase::ENET_Group, &ne)) { + Throw_USE_NotFound(node.name(), use); + } + mNodeElementCur->Child.push_back(ne); - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); + +// MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); } else { @@ -188,16 +216,46 @@ void X3DImporter::ParseNode_Grouping_Switch() // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. // for empty element exit from node in that place - if(mReader->isEmptyElement()) ParseHelper_Node_Exit(); +// if(mReader->isEmptyElement()) ParseHelper_Node_Exit(); + if (node.empty()) { + ParseHelper_Node_Exit(); + } + }// if(!use.empty()) else } void X3DImporter::ParseNode_Grouping_SwitchEnd() { - // just exit from node. Defined choice will be accepted at postprocessing stage. + // just exit from node. Defined choice will be accepted at post-processing stage. ParseHelper_Node_Exit();// go up in scene graph } +void ReadAttrAsVec3f(pugi::xml_node &node, const std::string &attrName, aiVector3D &vec) { + const pugi::xml_attribute &attr = node.attribute(attrName.c_str()); + if (attr.empty()) { + return; + } + + std::string data = attr.as_string(); + std::vector token; + tokenize(data, token, " "); + vec.x = (ai_real)std::atof(token[0].c_str()); + vec.y = (ai_real)std::atof(token[1].c_str()); + vec.z = (ai_real)std::atof(token[2].c_str()); +} + + +void ReadAttrAsFloatArray(pugi::xml_node &node, const std::string &attrName, size_t numComponents, std::vector &tvec) { + pugi::xml_attribute attr = node.attribute(attrName.c_str()); + std::string data = attr.as_string(); + std::vector token; + tokenize(data, token, " "); + if (token.size() != numComponents) throw DeadlyImportError(": rotation vector must have 4 elements."); + for (size_t i = 0; i < numComponents; ++i) { + tvec.push_back((ai_real)std::atof(token[i].c_str())); + } +} + // tvec; + ReadAttrAsFloatArray(node, "rotation", 4, tvec); + memcpy(rotation, tvec.data(), sizeof(rotation)); + } + if (hasAttribute(node, "scaleOrientation")) { + std::vector tvec; + ReadAttrAsFloatArray(node, "rotation", 4, tvec); + ::memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation)); + } + /*if(an == "rotation") { std::vector tvec; @@ -247,8 +320,8 @@ void X3DImporter::ParseNode_Grouping_Transform() continue; } - if(an == "scaleOrientation") - { + if(an == "scaleOrientation"){ + std::vector tvec; XML_ReadNode_GetAttrVal_AsArrF(idx, tvec); if ( tvec.size() != 4 ) @@ -261,14 +334,17 @@ void X3DImporter::ParseNode_Grouping_Transform() continue; } - MACRO_ATTRREAD_LOOPEND; + MACRO_ATTRREAD_LOOPEND;*/ // if "USE" defined then find already defined element. - if(!use.empty()) - { - X3DNodeElementBase* ne( nullptr ); + if(!use.empty()) { + X3DNodeElementBase* ne = nullptr; + if (!FindNodeElement(use, X3DNodeElementBase::ENET_Group, &ne)) { + Throw_USE_NotFound(node.name(), use); + } + mNodeElementCur->Child.push_back(ne); - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); + //MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); } else { @@ -301,8 +377,7 @@ void X3DImporter::ParseNode_Grouping_Transform() // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. // for empty element exit from node in that place - if ( mReader->isEmptyElement() ) - { + if ( node.empty() ) { ParseHelper_Node_Exit(); } }// if(!use.empty()) else diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index fe5fff3c1..ba9e34be1 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -69,15 +69,11 @@ using namespace Assimp; #endif namespace Assimp { // this has to be in here because LogFunctions is in ::Assimp + template <> const char *LogFunctions::Prefix() { -<<<<<<< HEAD - static auto prefix = "XGL: "; - return prefix; -======= static auto prefix = "XGL: "; - return prefix; ->>>>>>> master + return prefix; } } // namespace Assimp @@ -97,13 +93,9 @@ static const aiImporterDesc desc = { // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer XGLImporter::XGLImporter() : -<<<<<<< HEAD - m_xmlParser(nullptr), m_scene(nullptr) { - // empty -======= - m_reader(nullptr), m_scene(nullptr) { + mXmlParser(nullptr), + m_scene(nullptr) { // empty ->>>>>>> master } // ------------------------------------------------------------------------------------------------ @@ -115,18 +107,13 @@ XGLImporter::~XGLImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. bool XGLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { -<<<<<<< HEAD /* NOTE: A simple check for the file extension is not enough -======= - /* NOTE: A simple check for the file extension is not enough ->>>>>>> master * here. XGL and ZGL are ok, but xml is too generic * and might be collada as well. So open the file and * look for typical signal tokens. */ const std::string extension = GetExtension(pFile); -<<<<<<< HEAD if (extension == "xgl" || extension == "zgl") { return true; } else if (extension == "xml" || checkSig) { @@ -136,42 +123,22 @@ bool XGLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 3); } return false; -======= - if (extension == "xgl" || extension == "zgl") { - return true; - } else if (extension == "xml" || checkSig) { - ai_assert(pIOHandler != nullptr); - - const char *tokens[] = { "", "", "" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 3); - } - return false; ->>>>>>> master } // ------------------------------------------------------------------------------------------------ // Get a list of all file extensions which are handled by this class const aiImporterDesc *XGLImporter::GetInfo() const { -<<<<<<< HEAD return &desc; -======= - return &desc; ->>>>>>> master } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void XGLImporter::InternReadFile(const std::string &pFile, -<<<<<<< HEAD aiScene *pScene, IOSystem *pIOHandler) { -======= - aiScene *pScene, IOSystem *pIOHandler) { ->>>>>>> master #ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL std::vector uncompressed; #endif -<<<<<<< HEAD m_scene = pScene; std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); @@ -179,22 +146,12 @@ void XGLImporter::InternReadFile(const std::string &pFile, if (stream.get() == NULL) { throw DeadlyImportError("Failed to open XGL/ZGL file " + pFile + ""); } -======= - m_scene = pScene; - std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); - - // check whether we can read from the file - if (stream.get() == nullptr) { - throw DeadlyImportError("Failed to open XGL/ZGL file " + pFile + ""); - } ->>>>>>> master // see if its compressed, if so uncompress it if (GetExtension(pFile) == "zgl") { #ifdef ASSIMP_BUILD_NO_COMPRESSED_XGL ThrowException("Cannot read ZGL file since Assimp was built without compression support"); #else -<<<<<<< HEAD std::unique_ptr raw_reader(new StreamReaderLE(stream)); // build a zlib stream @@ -211,7 +168,7 @@ void XGLImporter::InternReadFile(const std::string &pFile, raw_reader->IncPtr(2); zstream.next_in = reinterpret_cast(raw_reader->GetPtr()); - zstream.avail_in = raw_reader->GetRemainingSize(); + zstream.avail_in = (uInt) raw_reader->GetRemainingSize(); size_t total = 0l; @@ -245,8 +202,8 @@ void XGLImporter::InternReadFile(const std::string &pFile, // construct the irrXML parser /*CIrrXML_IOStreamReader st(stream.get()); m_reader.reset( createIrrXMLReader( ( IFileReadCallBack* ) &st ) );*/ - m_xmlParser = new XmlParser; - XmlNode *root = m_xmlParser->parse(stream.get()); + mXmlParser = new XmlParser; + XmlNode *root = mXmlParser->parse(stream.get()); if (nullptr == root) { return; } @@ -259,69 +216,10 @@ void XGLImporter::InternReadFile(const std::string &pFile, /* while (ReadElement()) { if (!ASSIMP_stricmp(m_reader->getNodeName(),"world")) { -======= - std::unique_ptr raw_reader(new StreamReaderLE(stream)); - - // build a zlib stream - z_stream zstream; - zstream.opaque = Z_NULL; - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.data_type = Z_BINARY; - - // raw decompression without a zlib or gzip header - inflateInit2(&zstream, -MAX_WBITS); - - // skip two extra bytes, zgl files do carry a crc16 upfront (I think) - raw_reader->IncPtr(2); - - zstream.next_in = reinterpret_cast(raw_reader->GetPtr()); - zstream.avail_in = (uInt)raw_reader->GetRemainingSize(); - - size_t total = 0l; - - // TODO: be smarter about this, decompress directly into heap buffer - // and decompress the data .... do 1k chunks in the hope that we won't kill the stack -#define MYBLOCK 1024 - Bytef block[MYBLOCK]; - int ret; - do { - zstream.avail_out = MYBLOCK; - zstream.next_out = block; - ret = inflate(&zstream, Z_NO_FLUSH); - - if (ret != Z_STREAM_END && ret != Z_OK) { - ThrowException("Failure decompressing this file using gzip, seemingly it is NOT a compressed .XGL file"); - } - const size_t have = MYBLOCK - zstream.avail_out; - total += have; - uncompressed.resize(total); - memcpy(uncompressed.data() + total - have, block, have); - } while (ret != Z_STREAM_END); - - // terminate zlib - inflateEnd(&zstream); - - // replace the input stream with a memory stream - stream.reset(new MemoryIOStream(reinterpret_cast(uncompressed.data()), total)); -#endif - } - - // construct the irrXML parser - CIrrXML_IOStreamReader st(stream.get()); - m_reader.reset(createIrrXMLReader((IFileReadCallBack *)&st)); - - // parse the XML file - TempScope scope; - - while (ReadElement()) { - if (!ASSIMP_stricmp(m_reader->getNodeName(), "world")) { ->>>>>>> master ReadWorld(scope); } }*/ -<<<<<<< HEAD std::vector &meshes = scope.meshes_linear; std::vector &materials = scope.materials_linear; if (!meshes.size() || !materials.size()) { @@ -342,28 +240,6 @@ void XGLImporter::InternReadFile(const std::string &pFile, m_scene->mNumLights = 1; m_scene->mLights = new aiLight *[1]; m_scene->mLights[0] = scope.light; -======= - std::vector &meshes = scope.meshes_linear; - std::vector &materials = scope.materials_linear; - if (!meshes.size() || !materials.size()) { - ThrowException("failed to extract data from XGL file, no meshes loaded"); - } - - // copy meshes - m_scene->mNumMeshes = static_cast(meshes.size()); - m_scene->mMeshes = new aiMesh *[m_scene->mNumMeshes](); - std::copy(meshes.begin(), meshes.end(), m_scene->mMeshes); - - // copy materials - m_scene->mNumMaterials = static_cast(materials.size()); - m_scene->mMaterials = new aiMaterial *[m_scene->mNumMaterials](); - std::copy(materials.begin(), materials.end(), m_scene->mMaterials); - - if (scope.light) { - m_scene->mNumLights = 1; - m_scene->mLights = new aiLight *[1]; - m_scene->mLights[0] = scope.light; ->>>>>>> master scope.light->mName = m_scene->mRootNode->mName; } @@ -372,7 +248,6 @@ void XGLImporter::InternReadFile(const std::string &pFile, } // ------------------------------------------------------------------------------------------------ -<<<<<<< HEAD void XGLImporter::ReadWorld(TempScope &scope) { XmlNode *root = m_xmlParser->getRootNode(); for (XmlNode &node : root->children()) { @@ -549,223 +424,6 @@ aiMatrix4x4 XGLImporter::ReadTrafo(XmlNode &node) { if (forward.SquareLength() < 1e-4 || up.SquareLength() < 1e-4) { LogError("A direction vector in is zero, ignoring trafo"); return m; -======= -bool XGLImporter::ReadElement() { - while (m_reader->read()) { - if (m_reader->getNodeType() == EXN_ELEMENT) { - return true; - } - } - return false; -} - -// ------------------------------------------------------------------------------------------------ -bool XGLImporter::ReadElementUpToClosing(const char *closetag) { - while (m_reader->read()) { - if (m_reader->getNodeType() == EXN_ELEMENT) { - return true; - } else if (m_reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(m_reader->getNodeName(), closetag)) { - return false; - } - } - LogError("unexpected EOF, expected closing <" + std::string(closetag) + "> tag"); - return false; -} - -// ------------------------------------------------------------------------------------------------ -bool XGLImporter::SkipToText() { - while (m_reader->read()) { - if (m_reader->getNodeType() == EXN_TEXT) { - return true; - } else if (m_reader->getNodeType() == EXN_ELEMENT || m_reader->getNodeType() == EXN_ELEMENT_END) { - ThrowException("expected text contents but found another element (or element end)"); - } - } - return false; -} - -// ------------------------------------------------------------------------------------------------ -std::string XGLImporter::GetElementName() { - const char *s = m_reader->getNodeName(); - size_t len = strlen(s); - - std::string ret; - ret.resize(len); - std::transform(s, s + len, ret.begin(), ::ToLower); - return ret; -} - -// ------------------------------------------------------------------------------------------------ -void XGLImporter::ReadWorld(TempScope &scope) { - while (ReadElementUpToClosing("world")) { - const std::string &s = GetElementName(); - // XXX right now we'd skip if it comes after - // or - if (s == "lighting") { - ReadLighting(scope); - } else if (s == "object" || s == "mesh" || s == "mat") { - break; - } - } - - aiNode *const nd = ReadObject(scope, true, "world"); - if (!nd) { - ThrowException("failure reading "); - } - if (!nd->mName.length) { - nd->mName.Set("WORLD"); - } - - m_scene->mRootNode = nd; -} - -// ------------------------------------------------------------------------------------------------ -void XGLImporter::ReadLighting(TempScope &scope) { - while (ReadElementUpToClosing("lighting")) { - const std::string &s = GetElementName(); - if (s == "directionallight") { - scope.light = ReadDirectionalLight(); - } else if (s == "ambient") { - LogWarn("ignoring tag"); - } else if (s == "spheremap") { - LogWarn("ignoring tag"); - } - } -} - -// ------------------------------------------------------------------------------------------------ -aiLight *XGLImporter::ReadDirectionalLight() { - std::unique_ptr l(new aiLight()); - l->mType = aiLightSource_DIRECTIONAL; - - while (ReadElementUpToClosing("directionallight")) { - const std::string &s = GetElementName(); - if (s == "direction") { - l->mDirection = ReadVec3(); - } else if (s == "diffuse") { - l->mColorDiffuse = ReadCol3(); - } else if (s == "specular") { - l->mColorSpecular = ReadCol3(); - } - } - return l.release(); -} - -// ------------------------------------------------------------------------------------------------ -aiNode *XGLImporter::ReadObject(TempScope &scope, bool skipFirst, const char *closetag) { - aiNode *nd = new aiNode; - std::vector children; - std::vector meshes; - - try { - while (skipFirst || ReadElementUpToClosing(closetag)) { - skipFirst = false; - - const std::string &s = GetElementName(); - if (s == "mesh") { - const size_t prev = scope.meshes_linear.size(); - if (ReadMesh(scope)) { - const size_t newc = scope.meshes_linear.size(); - for (size_t i = 0; i < newc - prev; ++i) { - meshes.push_back(static_cast(i + prev)); - } - } - } else if (s == "mat") { - ReadMaterial(scope); - } else if (s == "object") { - children.push_back(ReadObject(scope)); - } else if (s == "objectref") { - // XXX - } else if (s == "meshref") { - const unsigned int id = static_cast(ReadIndexFromText()); - - std::multimap::iterator it = scope.meshes.find(id), end = scope.meshes.end(); - if (it == end) { - ThrowException(" index out of range"); - } - - for (; it != end && (*it).first == id; ++it) { - // ok, this is n^2 and should get optimized one day - aiMesh *const m = (*it).second; - - unsigned int i = 0, mcount = static_cast(scope.meshes_linear.size()); - for (; i < mcount; ++i) { - if (scope.meshes_linear[i] == m) { - meshes.push_back(i); - break; - } - } - - ai_assert(i < mcount); - } - } else if (s == "transform") { - nd->mTransformation = ReadTrafo(); - } - } - - } catch (...) { - for (aiNode *ch : children) { - delete ch; - } - throw; - } - - // FIX: since we used std::multimap<> to keep meshes by id, mesh order now depends on the behaviour - // of the multimap implementation with respect to the ordering of entries with same values. - // C++11 gives the guarantee that it uses insertion order, before it is implementation-specific. - // Sort by material id to always guarantee a deterministic result. - std::sort(meshes.begin(), meshes.end(), SortMeshByMaterialId(scope)); - - // link meshes to node - nd->mNumMeshes = static_cast(meshes.size()); - if (nd->mNumMeshes) { - nd->mMeshes = new unsigned int[nd->mNumMeshes](); - for (unsigned int i = 0; i < nd->mNumMeshes; ++i) { - nd->mMeshes[i] = meshes[i]; - } - } - - // link children to parent - nd->mNumChildren = static_cast(children.size()); - if (nd->mNumChildren) { - nd->mChildren = new aiNode *[nd->mNumChildren](); - for (unsigned int i = 0; i < nd->mNumChildren; ++i) { - nd->mChildren[i] = children[i]; - children[i]->mParent = nd; - } - } - - return nd; -} - -// ------------------------------------------------------------------------------------------------ -aiMatrix4x4 XGLImporter::ReadTrafo() { - aiVector3D forward, up, right, position; - float scale = 1.0f; - - while (ReadElementUpToClosing("transform")) { - const std::string &s = GetElementName(); - if (s == "forward") { - forward = ReadVec3(); - } else if (s == "up") { - up = ReadVec3(); - } else if (s == "position") { - position = ReadVec3(); - } - if (s == "scale") { - scale = ReadFloat(); - if (scale < 0.f) { - // this is wrong, but we can leave the value and pass it to the caller - LogError("found negative scaling in , ignoring"); - } - } - } - - aiMatrix4x4 m; - if (forward.SquareLength() < 1e-4 || up.SquareLength() < 1e-4) { - LogError("A direction vector in is zero, ignoring trafo"); - return m; ->>>>>>> master } forward.Normalize(); @@ -804,7 +462,6 @@ aiMatrix4x4 XGLImporter::ReadTrafo() { // ------------------------------------------------------------------------------------------------ aiMesh *XGLImporter::ToOutputMesh(const TempMaterialMesh &m) { -<<<<<<< HEAD std::unique_ptr mesh(new aiMesh()); mesh->mNumVertices = static_cast(m.positions.size()); @@ -839,40 +496,6 @@ aiMesh *XGLImporter::ToOutputMesh(const TempMaterialMesh &m) { } ai_assert(idx == mesh->mNumVertices); -======= - std::unique_ptr mesh(new aiMesh()); - - mesh->mNumVertices = static_cast(m.positions.size()); - mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - std::copy(m.positions.begin(), m.positions.end(), mesh->mVertices); - - if (m.normals.size()) { - mesh->mNormals = new aiVector3D[mesh->mNumVertices]; - std::copy(m.normals.begin(), m.normals.end(), mesh->mNormals); - } - - if (m.uvs.size()) { - mesh->mNumUVComponents[0] = 2; - mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; - - for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { - mesh->mTextureCoords[0][i] = aiVector3D(m.uvs[i].x, m.uvs[i].y, 0.f); - } - } - - mesh->mNumFaces = static_cast(m.vcounts.size()); - mesh->mFaces = new aiFace[m.vcounts.size()]; - - unsigned int idx = 0; - for (unsigned int i = 0; i < mesh->mNumFaces; ++i) { - aiFace &f = mesh->mFaces[i]; - f.mNumIndices = m.vcounts[i]; - f.mIndices = new unsigned int[f.mNumIndices]; - for (unsigned int c = 0; c < f.mNumIndices; ++c) { - f.mIndices[c] = idx++; - } - } ->>>>>>> master mesh->mPrimitiveTypes = m.pflags; mesh->mMaterialIndex = m.matid; @@ -881,7 +504,6 @@ aiMesh *XGLImporter::ToOutputMesh(const TempMaterialMesh &m) { } // ------------------------------------------------------------------------------------------------ -<<<<<<< HEAD bool XGLImporter::ReadMesh(XmlNode &node, TempScope &scope) { TempMesh t; @@ -1009,137 +631,9 @@ unsigned int XGLImporter::ResolveMaterialRef(XmlNode &node, TempScope &scope) { ReadMaterial(node, scope); return static_cast(scope.materials_linear.size() - 1); } -======= -bool XGLImporter::ReadMesh(TempScope &scope) { - TempMesh t; - - std::map bymat; - const unsigned int mesh_id = ReadIDAttr(); - - while (ReadElementUpToClosing("mesh")) { - const std::string &s = GetElementName(); - - if (s == "mat") { - ReadMaterial(scope); - } else if (s == "p") { - if (!m_reader->getAttributeValue("ID")) { - LogWarn("no ID attribute on

, ignoring"); - } else { - int id = m_reader->getAttributeValueAsInt("ID"); - t.points[id] = ReadVec3(); - } - } else if (s == "n") { - if (!m_reader->getAttributeValue("ID")) { - LogWarn("no ID attribute on , ignoring"); - } else { - int id = m_reader->getAttributeValueAsInt("ID"); - t.normals[id] = ReadVec3(); - } - } else if (s == "tc") { - if (!m_reader->getAttributeValue("ID")) { - LogWarn("no ID attribute on , ignoring"); - } else { - int id = m_reader->getAttributeValueAsInt("ID"); - t.uvs[id] = ReadVec2(); - } - } else if (s == "f" || s == "l" || s == "p") { - const unsigned int vcount = s == "f" ? 3 : (s == "l" ? 2 : 1); - - unsigned int mid = ~0u; - TempFace tf[3]; - bool has[3] = { 0 }; - - while (ReadElementUpToClosing(s.c_str())) { - const std::string &elemName = GetElementName(); - if (elemName == "fv1" || elemName == "lv1" || elemName == "pv1") { - ReadFaceVertex(t, tf[0]); - has[0] = true; - } else if (elemName == "fv2" || elemName == "lv2") { - ReadFaceVertex(t, tf[1]); - has[1] = true; - } else if (elemName == "fv3") { - ReadFaceVertex(t, tf[2]); - has[2] = true; - } else if (elemName == "mat") { - if (mid != ~0u) { - LogWarn("only one material tag allowed per "); - } - mid = ResolveMaterialRef(scope); - } else if (elemName == "matref") { - if (mid != ~0u) { - LogWarn("only one material tag allowed per "); - } - mid = ResolveMaterialRef(scope); - } - } - - if (mid == ~0u) { - ThrowException("missing material index"); - } - - bool nor = false; - bool uv = false; - for (unsigned int i = 0; i < vcount; ++i) { - if (!has[i]) { - ThrowException("missing face vertex data"); - } - - nor = nor || tf[i].has_normal; - uv = uv || tf[i].has_uv; - } - - if (mid >= (1 << 30)) { - LogWarn("material indices exhausted, this may cause errors in the output"); - } - unsigned int meshId = mid | ((nor ? 1 : 0) << 31) | ((uv ? 1 : 0) << 30); - - TempMaterialMesh &mesh = bymat[meshId]; - mesh.matid = mid; - - for (unsigned int i = 0; i < vcount; ++i) { - mesh.positions.push_back(tf[i].pos); - if (nor) { - mesh.normals.push_back(tf[i].normal); - } - if (uv) { - mesh.uvs.push_back(tf[i].uv); - } - - mesh.pflags |= 1 << (vcount - 1); - } - - mesh.vcounts.push_back(vcount); - } - } - - // finally extract output meshes and add them to the scope - typedef std::pair pairt; - for (const pairt &p : bymat) { - aiMesh *const m = ToOutputMesh(p.second); - scope.meshes_linear.push_back(m); - - // if this is a definition, keep it on the stack - if (mesh_id != ~0u) { - scope.meshes.insert(std::pair(mesh_id, m)); - } - } - - // no id == not a reference, insert this mesh right *here* - return mesh_id == ~0u; -} - -// ---------------------------------------------------------------------------------------------- -unsigned int XGLImporter::ResolveMaterialRef(TempScope &scope) { - const std::string &s = GetElementName(); - if (s == "mat") { - ReadMaterial(scope); - return static_cast(scope.materials_linear.size() - 1); - } ->>>>>>> master const int id = ReadIndexFromText(node); -<<<<<<< HEAD std::map::iterator it = scope.materials.find(id), end = scope.materials.end(); if (it == end) { ThrowException(" index out of range"); @@ -1156,28 +650,11 @@ unsigned int XGLImporter::ResolveMaterialRef(TempScope &scope) { } ai_assert(false); -======= - std::map::iterator it = scope.materials.find(id), end = scope.materials.end(); - if (it == end) { - ThrowException(" index out of range"); - } - - // ok, this is n^2 and should get optimized one day - aiMaterial *const m = (*it).second; - - unsigned int i = 0, mcount = static_cast(scope.materials_linear.size()); - for (; i < mcount; ++i) { - if (scope.materials_linear[i] == m) { - return i; - } - } ->>>>>>> master return 0; } // ------------------------------------------------------------------------------------------------ -<<<<<<< HEAD void XGLImporter::ReadMaterial(XmlNode &node, TempScope &scope) { const unsigned int mat_id = ReadIDAttr(node); @@ -1326,178 +803,11 @@ aiVector2D XGLImporter::ReadVec2(XmlNode &node) { } vec.x = v[0]; vec.y = v[1]; -======= -void XGLImporter::ReadMaterial(TempScope &scope) { - const unsigned int mat_id = ReadIDAttr(); - - aiMaterial *mat(new aiMaterial); - while (ReadElementUpToClosing("mat")) { - const std::string &s = GetElementName(); - if (s == "amb") { - const aiColor3D c = ReadCol3(); - mat->AddProperty(&c, 1, AI_MATKEY_COLOR_AMBIENT); - } else if (s == "diff") { - const aiColor3D c = ReadCol3(); - mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); - } else if (s == "spec") { - const aiColor3D c = ReadCol3(); - mat->AddProperty(&c, 1, AI_MATKEY_COLOR_SPECULAR); - } else if (s == "emiss") { - const aiColor3D c = ReadCol3(); - mat->AddProperty(&c, 1, AI_MATKEY_COLOR_EMISSIVE); - } else if (s == "alpha") { - const float f = ReadFloat(); - mat->AddProperty(&f, 1, AI_MATKEY_OPACITY); - } else if (s == "shine") { - const float f = ReadFloat(); - mat->AddProperty(&f, 1, AI_MATKEY_SHININESS); - } - } - - scope.materials[mat_id] = mat; - scope.materials_linear.push_back(mat); -} - -// ---------------------------------------------------------------------------------------------- -void XGLImporter::ReadFaceVertex(const TempMesh &t, TempFace &out) { - const std::string &end = GetElementName(); - - bool havep = false; - while (ReadElementUpToClosing(end.c_str())) { - const std::string &s = GetElementName(); - if (s == "pref") { - const unsigned int id = ReadIndexFromText(); - std::map::const_iterator it = t.points.find(id); - if (it == t.points.end()) { - ThrowException("point index out of range"); - } - - out.pos = (*it).second; - havep = true; - } else if (s == "nref") { - const unsigned int id = ReadIndexFromText(); - std::map::const_iterator it = t.normals.find(id); - if (it == t.normals.end()) { - ThrowException("normal index out of range"); - } - - out.normal = (*it).second; - out.has_normal = true; - } else if (s == "tcref") { - const unsigned int id = ReadIndexFromText(); - std::map::const_iterator it = t.uvs.find(id); - if (it == t.uvs.end()) { - ThrowException("uv index out of range"); - } - - out.uv = (*it).second; - out.has_uv = true; - } else if (s == "p") { - out.pos = ReadVec3(); - } else if (s == "n") { - out.normal = ReadVec3(); - } else if (s == "tc") { - out.uv = ReadVec2(); - } - } - - if (!havep) { - ThrowException("missing in element"); - } -} - -// ------------------------------------------------------------------------------------------------ -unsigned int XGLImporter::ReadIDAttr() { - for (int i = 0, e = m_reader->getAttributeCount(); i < e; ++i) { - - if (!ASSIMP_stricmp(m_reader->getAttributeName(i), "id")) { - return m_reader->getAttributeValueAsInt(i); - } - } - return ~0u; -} - -// ------------------------------------------------------------------------------------------------ -float XGLImporter::ReadFloat() { - if (!SkipToText()) { - LogError("unexpected EOF reading float element contents"); - return 0.f; - } - const char *s = m_reader->getNodeData(), *se; - - if (!SkipSpaces(&s)) { - LogError("unexpected EOL, failed to parse float"); - return 0.f; - } - - float t; - se = fast_atoreal_move(s, t); - - if (se == s) { - LogError("failed to read float text"); - return 0.f; - } - - return t; -} - -// ------------------------------------------------------------------------------------------------ -unsigned int XGLImporter::ReadIndexFromText() { - if (!SkipToText()) { - LogError("unexpected EOF reading index element contents"); - return ~0u; - } - const char *s = m_reader->getNodeData(), *se; - if (!SkipSpaces(&s)) { - LogError("unexpected EOL, failed to parse index element"); - return ~0u; - } - - const unsigned int t = strtoul10(s, &se); - - if (se == s) { - LogError("failed to read index"); - return ~0u; - } - - return t; -} - -// ------------------------------------------------------------------------------------------------ -aiVector2D XGLImporter::ReadVec2() { - aiVector2D vec; - - if (!SkipToText()) { - LogError("unexpected EOF reading vec2 contents"); - return vec; - } - const char *s = m_reader->getNodeData(); - - ai_real v[2]; - for (int i = 0; i < 2; ++i) { - if (!SkipSpaces(&s)) { - LogError("unexpected EOL, failed to parse vec2"); - return vec; - } - - v[i] = fast_atof(&s); - - SkipSpaces(&s); - if (i != 1 && *s != ',') { - LogError("expected comma, failed to parse vec2"); - return vec; - } - ++s; - } - vec.x = v[0]; - vec.y = v[1]; ->>>>>>> master return vec; } // ------------------------------------------------------------------------------------------------ -<<<<<<< HEAD aiVector3D XGLImporter::ReadVec3(XmlNode &node) { aiVector3D vec; const char *s = node.value(); @@ -1526,42 +836,6 @@ aiColor3D XGLImporter::ReadCol3(XmlNode &node) { LogWarn("color values out of range, ignoring"); } return aiColor3D(v.x, v.y, v.z); -======= -aiVector3D XGLImporter::ReadVec3() { - aiVector3D vec; - - if (!SkipToText()) { - LogError("unexpected EOF reading vec3 contents"); - return vec; - } - const char *s = m_reader->getNodeData(); - - for (int i = 0; i < 3; ++i) { - if (!SkipSpaces(&s)) { - LogError("unexpected EOL, failed to parse vec3"); - return vec; - } - vec[i] = fast_atof(&s); - - SkipSpaces(&s); - if (i != 2 && *s != ',') { - LogError("expected comma, failed to parse vec3"); - return vec; - } - ++s; - } - - return vec; -} - -// ------------------------------------------------------------------------------------------------ -aiColor3D XGLImporter::ReadCol3() { - const aiVector3D &v = ReadVec3(); - if (v.x < 0.f || v.x > 1.0f || v.y < 0.f || v.y > 1.0f || v.z < 0.f || v.z > 1.0f) { - LogWarn("color values out of range, ignoring"); - } - return aiColor3D(v.x, v.y, v.z); ->>>>>>> master } #endif diff --git a/code/AssetLib/XGL/XGLLoader.h b/code/AssetLib/XGL/XGLLoader.h index 1cd278113..d8bef4f9c 100644 --- a/code/AssetLib/XGL/XGLLoader.h +++ b/code/AssetLib/XGL/XGLLoader.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -55,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include @@ -191,7 +191,8 @@ private: unsigned int ResolveMaterialRef(TempScope &scope); private: - std::shared_ptr m_reader; + //std::shared_ptr m_reader; + XmlParser *mXmlParser; aiScene *m_scene; }; diff --git a/include/assimp/ParsingUtils.h b/include/assimp/ParsingUtils.h index 69dc95da2..28c2f0e76 100644 --- a/include/assimp/ParsingUtils.h +++ b/include/assimp/ParsingUtils.h @@ -256,6 +256,37 @@ std::string GetNextToken(const char*& in) { return std::string(cur,(size_t)(in-cur)); } +// --------------------------------------------------------------------------------- +/** @brief Will perform a simple tokenize. + * @param str String to tokenize. + * @param tokens Array with tokens, will be empty if no token was found. + * @param delimiters Delimiter for tokenize. + * @return Number of found token. + */ +template +AI_FORCE_INLINE unsigned int tokenize(const string_type &str, std::vector &tokens, + const string_type &delimiters) { + // Skip delimiters at beginning. + typename string_type::size_type lastPos = str.find_first_not_of(delimiters, 0); + + // Find first "non-delimiter". + typename string_type::size_type pos = str.find_first_of(delimiters, lastPos); + while (string_type::npos != pos || string_type::npos != lastPos) { + // Found a token, add it to the vector. + string_type tmp = str.substr(lastPos, pos - lastPos); + if (!tmp.empty() && ' ' != tmp[0]) + tokens.push_back(tmp); + + // Skip delimiters. Note the "not_of" + lastPos = str.find_first_not_of(delimiters, pos); + + // Find next "non-delimiter" + pos = str.find_first_of(delimiters, lastPos); + } + + return static_cast(tokens.size()); +} + // --------------------------------------------------------------------------------- } // ! namespace Assimp