From 6d5c388780c88b0a85009aec4246fd23bb2653b3 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 30 Aug 2020 21:10:04 +0200 Subject: [PATCH] Introduce node iterator. --- code/AssetLib/AMF/AMFImporter.cpp | 19 +-- code/AssetLib/AMF/AMFImporter_Geometry.cpp | 4 +- code/AssetLib/AMF/AMFImporter_Postprocess.cpp | 76 ++++++------ code/AssetLib/Collada/ColladaParser.cpp | 114 ++++++++++-------- include/assimp/XmlParser.h | 59 ++++++++- 5 files changed, 172 insertions(+), 100 deletions(-) diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index d3bd03412..881651e75 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -291,10 +291,13 @@ void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) { void AMFImporter::ParseNode_Root() { std::string unit, version; AMFNodeElementBase *ne(nullptr); - pugi::xml_node *root = mXmlParser->findNode("amf"); - - unit = root->attribute("unit").as_string(); - version = root->attribute("version").as_string(); + XmlNode *root = mXmlParser->findNode("amf"); + if (nullptr == root) { + throw DeadlyImportError("Root node \"amf\" not found."); + } + XmlNode node = *root; + unit = node.attribute("unit").as_string(); + version = node.attribute("version").as_string(); // Read attributes for node . // Check attributes @@ -313,7 +316,7 @@ void AMFImporter::ParseNode_Root() { ((AMFRoot *)ne)->Version = version; // Check for child nodes - for (pugi::xml_node ¤tNode : root->children()) { + for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { const std::string currentName = currentNode.name(); if (currentName == "object") { ParseNode_Object(currentNode); @@ -353,7 +356,7 @@ void AMFImporter::ParseNode_Constellation(XmlNode &node) { } // Check for child nodes - for (pugi::xml_node ¤tNode : node.children()) { + for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { std::string name = currentNode.name(); if (name == "instance") { ParseNode_Instance(currentNode); @@ -392,7 +395,7 @@ void AMFImporter::ParseNode_Instance(XmlNode &node) { if (node.empty()) { mNodeElement_Cur->Child.push_back(ne); } - for (pugi::xml_node currentNode : node.children()) { + for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { bool read_flag[6] = { false, false, false, false, false, false }; std::string currentName = currentNode.name(); if (currentName == "deltax") { @@ -447,7 +450,7 @@ void AMFImporter::ParseNode_Object(XmlNode &node) { if (node.empty()) { mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element } - for (pugi::xml_node ¤tNode : node.children()) { + for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { const std::string currentName = currentNode.name(); if (currentName == "color") { ParseNode_Color(currentNode); diff --git a/code/AssetLib/AMF/AMFImporter_Geometry.cpp b/code/AssetLib/AMF/AMFImporter_Geometry.cpp index 0a48b2f09..55986b552 100644 --- a/code/AssetLib/AMF/AMFImporter_Geometry.cpp +++ b/code/AssetLib/AMF/AMFImporter_Geometry.cpp @@ -196,7 +196,7 @@ void AMFImporter::ParseNode_Volume(XmlNode &node) { } bool col_read = false; - for (pugi::xml_node currentNode : node.children()) { + for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { const std::string currentName = currentNode.name(); if (currentName == "color") { if (col_read) Throw_MoreThanOnceDefined(currentName ,"color", "Only one color can be defined for ."); @@ -238,7 +238,7 @@ void AMFImporter::ParseNode_Triangle(XmlNode &node) { // Check for child nodes bool col_read = false, tex_read = false; bool read_flag[3] = { false, false, false }; - for (pugi::xml_node currentNode : node.children()) { + for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { const std::string currentName = currentNode.name(); if (currentName == "color") { if (col_read) Throw_MoreThanOnceDefined(currentName , "color", "Only one color can be defined for ."); diff --git a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp index 9d514b4e8..a86082cfd 100644 --- a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp +++ b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp @@ -98,9 +98,13 @@ void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &pNodeElem return; } - pVertexCoordinateArray.reserve(vn->Child.size()); // all coordinates stored as child and we need to reserve space for future push_back's. - pVertexColorArray.resize(vn->Child.size()); // colors count equal vertices count. + // all coordinates stored as child and we need to reserve space for future push_back's. + pVertexCoordinateArray.reserve(vn->Child.size()); + + // colors count equal vertices count. + pVertexColorArray.resize(vn->Child.size()); col_idx = 0; + // Inside vertices collect all data and place to arrays for (AMFNodeElementBase *vn_child : vn->Child) { // vertices, colors @@ -118,26 +122,20 @@ void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &pNodeElem pVertexColorArray[col_idx] = (AMFColor *)vtx; continue; } - } // for(CAMFImporter_NodeElement* vtx: vn_child->Child) + } col_idx++; - } // if(vn_child->Type == CAMFImporter_NodeElement::ENET_Vertex) - } // for(CAMFImporter_NodeElement* vn_child: vn->Child) + } + } } -size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &pID_R, const std::string &pID_G, const std::string &pID_B, - const std::string &pID_A) { - size_t TextureConverted_Index; - std::string TextureConverted_ID; - - // check input data - if (pID_R.empty() && pID_G.empty() && pID_B.empty() && pID_A.empty()) +size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string &r, const std::string &g, const std::string &b, const std::string &a) { + if (r.empty() && g.empty() && b.empty() && a.empty()) { throw DeadlyImportError("PostprocessHelper_GetTextureID_Or_Create. At least one texture ID must be defined."); + } - // Create ID - TextureConverted_ID = pID_R + "_" + pID_G + "_" + pID_B + "_" + pID_A; - // Check if texture specified by set of IDs is converted already. - TextureConverted_Index = 0; + std::string TextureConverted_ID = r + "_" + g + "_" + b + "_" + a; + size_t TextureConverted_Index = 0; for (const SPP_Texture &tex_convd : mTexture_Converted) { if (tex_convd.ID == TextureConverted_ID) { return TextureConverted_Index; @@ -146,10 +144,10 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string & } } - // // Converted texture not found, create it. - // - AMFTexture *src_texture[4]{ nullptr }; + AMFTexture *src_texture[4] { + nullptr + }; std::vector src_texture_4check; SPP_Texture converted_texture; @@ -157,8 +155,8 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string & AMFNodeElementBase *t_tex = nullptr; // R - if (!pID_R.empty()) { - if (!Find_NodeElement(pID_R, AMFNodeElementBase::EType::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_R); + if (!r.empty()) { + if (!Find_NodeElement(r, AMFNodeElementBase::EType::ENET_Texture, &t_tex)) Throw_ID_NotFound(r); src_texture[0] = (AMFTexture *)t_tex; src_texture_4check.push_back((AMFTexture *)t_tex); @@ -167,8 +165,8 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string & } // G - if (!pID_G.empty()) { - if (!Find_NodeElement(pID_G, AMFNodeElementBase::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_G); + if (!g.empty()) { + if (!Find_NodeElement(g, AMFNodeElementBase::ENET_Texture, &t_tex)) Throw_ID_NotFound(g); src_texture[1] = (AMFTexture *)t_tex; src_texture_4check.push_back((AMFTexture *)t_tex); @@ -177,8 +175,8 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string & } // B - if (!pID_B.empty()) { - if (!Find_NodeElement(pID_B, AMFNodeElementBase::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_B); + if (!b.empty()) { + if (!Find_NodeElement(b, AMFNodeElementBase::ENET_Texture, &t_tex)) Throw_ID_NotFound(b); src_texture[2] = (AMFTexture *)t_tex; src_texture_4check.push_back((AMFTexture *)t_tex); @@ -187,8 +185,8 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string & } // A - if (!pID_A.empty()) { - if (!Find_NodeElement(pID_A, AMFNodeElementBase::ENET_Texture, &t_tex)) Throw_ID_NotFound(pID_A); + if (!a.empty()) { + if (!Find_NodeElement(a, AMFNodeElementBase::ENET_Texture, &t_tex)) Throw_ID_NotFound(a); src_texture[3] = (AMFTexture *)t_tex; src_texture_4check.push_back((AMFTexture *)t_tex); @@ -218,10 +216,10 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string & // Create format hint. strcpy(converted_texture.FormatHint, "rgba0000"); // copy initial string. - if (!pID_R.empty()) converted_texture.FormatHint[4] = '8'; - if (!pID_G.empty()) converted_texture.FormatHint[5] = '8'; - if (!pID_B.empty()) converted_texture.FormatHint[6] = '8'; - if (!pID_A.empty()) converted_texture.FormatHint[7] = '8'; + if (!r.empty()) converted_texture.FormatHint[4] = '8'; + if (!g.empty()) converted_texture.FormatHint[5] = '8'; + if (!b.empty()) converted_texture.FormatHint[6] = '8'; + if (!a.empty()) converted_texture.FormatHint[7] = '8'; // Сopy data of textures. size_t tex_size = 0; @@ -230,19 +228,19 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string & size_t off_b = 0; // Calculate size of the target array and rule how data will be copied. - if (!pID_R.empty() && nullptr != src_texture[0]) { + if (!r.empty() && nullptr != src_texture[0]) { tex_size += src_texture[0]->Data.size(); step++, off_g++, off_b++; } - if (!pID_G.empty() && nullptr != src_texture[1]) { + if (!g.empty() && nullptr != src_texture[1]) { tex_size += src_texture[1]->Data.size(); step++, off_b++; } - if (!pID_B.empty() && nullptr != src_texture[2]) { + if (!b.empty() && nullptr != src_texture[2]) { tex_size += src_texture[2]->Data.size(); step++; } - if (!pID_A.empty() && nullptr != src_texture[3]) { + if (!a.empty() && nullptr != src_texture[3]) { tex_size += src_texture[3]->Data.size(); step++; } @@ -260,10 +258,10 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string & } }; // auto CopyTextureData = [&](const size_t pOffset, const size_t pStep, const uint8_t pSrcTexNum) -> void - CopyTextureData(pID_R, 0, step, 0); - CopyTextureData(pID_G, off_g, step, 1); - CopyTextureData(pID_B, off_b, step, 2); - CopyTextureData(pID_A, step - 1, step, 3); + CopyTextureData(r, 0, step, 0); + CopyTextureData(g, off_g, step, 1); + CopyTextureData(b, off_b, step, 2); + CopyTextureData(a, step - 1, step, 3); // Store new converted texture ID converted_texture.ID = TextureConverted_ID; diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index e02823cc8..7a8de4eaf 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -170,8 +170,9 @@ std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) { if (nullptr == root) { return std::string(); } - const char *filepath = root->value(); - aiString ai_str(filepath); + std::string v; + XmlParser::getValueAsString(*root, v); + aiString ai_str(v); UriDecodePath(ai_str); return std::string(ai_str.C_Str()); } @@ -302,10 +303,11 @@ void ColladaParser::ReadAssetInfo(XmlNode &node) { mUnitSize = static_cast(attr.as_double()); } } else if (name == "up_axis") { - const char *content = currentNode.value(); - if (strncmp(content, "X_UP", 4) == 0) { + std::string v; + XmlParser::getValueAsString(currentNode, v); + if (v == "X_UP" ) { mUpDirection = UP_X; - } else if (strncmp(content, "Z_UP", 4) == 0) { + } else if (v == "Z_UP" ) { mUpDirection = UP_Z; } else { mUpDirection = UP_Y; @@ -334,21 +336,23 @@ void ColladaParser::ReadMetaDataItem(XmlNode &node, StringMetaData &metadata) { const Collada::MetaKeyPairVector &key_renaming = GetColladaAssimpMetaKeysCamelCase(); const std::string name = node.name(); - if (!name.empty()) { - const char *value_char = node.value(); - if (nullptr != value_char) { - aiString aistr; - aistr.Set(value_char); + if (name.empty()) { + return; + } - std::string camel_key_str(name); - ToCamelCase(camel_key_str); + std::string v; + if (XmlParser::getValueAsString(node, v)) { + aiString aistr; + aistr.Set(v); - size_t found_index; - if (FindCommonKey(camel_key_str, key_renaming, found_index)) { - metadata.emplace(key_renaming[found_index].second, aistr); - } else { - metadata.emplace(camel_key_str, aistr); - } + std::string camel_key_str(name); + ToCamelCase(camel_key_str); + + size_t found_index; + if (FindCommonKey(camel_key_str, key_renaming, found_index)) { + metadata.emplace(key_renaming[found_index].second, aistr); + } else { + metadata.emplace(camel_key_str, aistr); } } } @@ -588,17 +592,9 @@ void ColladaParser::ReadControllerLibrary(XmlNode &node) { return; } - int attrId = node.attribute("id").as_int(); - std::string id = node.value(); + std::string id = node.attribute("id").as_string(); mControllerLibrary[id] = Controller(); - for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { - const std::string currentName = currentNode.name(); - if (currentName == "controller") { - attrId = currentNode.attribute("id").as_int(); - std::string controllerId = currentNode.attribute(std::to_string(attrId).c_str()).value(); - ReadController(node, mControllerLibrary[controllerId]); - } - } + ReadController(node, mControllerLibrary[id]); } // ------------------------------------------------------------------------------------------------ @@ -614,8 +610,10 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &pControll pController.mMeshId = currentNode.attribute("source").as_string(); int methodIndex = currentNode.attribute("method").as_int(); if (methodIndex > 0) { - const char *method = currentNode.attribute("method").value(); - if (strcmp(method, "RELATIVE") == 0) { + std::string method; + XmlParser::getValueAsString(currentNode, method); + + if (method == "RELATIVE" ) { pController.mMethod = Relative; } } @@ -722,9 +720,11 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC pController.mWeights.resize(numWeights); } else if (currentName == "v" && vertexCount > 0) { // read JointIndex - WeightIndex pairs - const char *text = currentNode.value(); + std::string stdText; + XmlParser::getValueAsString(currentNode, stdText); + const char *text = stdText.c_str(); for (std::vector>::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) { - if (*text == 0) { + if (text == 0) { ThrowException("Out of data while reading "); } it->first = strtoul10(text, &text); @@ -770,13 +770,12 @@ void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) { // FIX: C4D exporter writes empty tags if (!currentNode.empty()) { // element content is filename - hopefully - const char *sz = currentNode.value(); + const char *sz = currentNode.text().as_string(); if (sz) { aiString filepath(sz); UriDecodePath(filepath); pImage.mFileName = filepath.C_Str(); } - // TestClosing("init_from"); } if (!pImage.mFileName.length()) { pImage.mFileName = "unknown_texture"; @@ -800,13 +799,13 @@ void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) { // TODO: correctly jump over cube and volume maps? } } else if (mFormat == FV_1_5_n) { + std::string value; XmlNode refChild = currentNode.child("ref"); XmlNode hexChild = currentNode.child("hex"); if (refChild) { // element content is filename - hopefully - const char *sz = refChild.value(); - if (sz) { - aiString filepath(sz); + if (XmlParser::getValueAsString(refChild, value)) { + aiString filepath(value); UriDecodePath(filepath); pImage.mFileName = filepath.C_Str(); } @@ -817,8 +816,8 @@ void ColladaParser::ReadImage(XmlNode &node, Collada::Image &pImage) { ASSIMP_LOG_WARN("Collada: Unknown image file format"); } - const char *data = hexChild.value(); - + XmlParser::getValueAsString(hexChild, value); + const char *data = value.c_str(); // hexadecimal-encoded binary octets. First of all, find the // required buffer size to reserve enough storage. const char *cur = data; @@ -924,7 +923,10 @@ void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) { // ------------------------------------------------------------------------------------------------ // Reads a light entry into the given light void ColladaParser::ReadLight(XmlNode &node, Collada::Light &pLight) { - for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { + XmlNodeIterator xmlIt; + xmlIt.collectChildrenPreOrder(node); + XmlNode currentNode; + while (xmlIt.getNext(currentNode)) { const std::string ¤tName = currentNode.name(); if (currentName == "spot") { pLight.mType = aiLightSource_SPOT; @@ -936,7 +938,9 @@ void ColladaParser::ReadLight(XmlNode &node, Collada::Light &pLight) { pLight.mType = aiLightSource_POINT; } else if (currentName == "color") { // text content contains 3 floats - const char *content = currentNode.value(); + std::string v; + XmlParser::getValueAsString(currentNode, v); + const char *content = v.c_str(); content = fast_atoreal_move(content, (ai_real &)pLight.mColor.r); SkipSpacesAndLineEnd(&content); @@ -1156,8 +1160,9 @@ void ColladaParser::ReadSamplerProperties(XmlNode &node, Sampler &out) { } else if (currentName == "rotateUV") { XmlParser::getFloatAttribute(currentNode, currentName.c_str(), out.mTransform.mRotation); } else if (currentName == "blend_mode") { - - const char *sz = currentNode.value(); + std::string v; + XmlParser::getValueAsString(currentNode, v); + const char *sz = v.c_str(); // http://www.feelingsoftware.com/content/view/55/72/lang,en/ // NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE if (0 == ASSIMP_strincmp(sz, "ADD", 3)) @@ -1196,7 +1201,9 @@ void ColladaParser::ReadEffectColor(XmlNode &node, aiColor4D &pColor, Sampler &p const std::string ¤tName = currentNode.name(); if (currentName == "color") { // text content contains 4 floats - const char *content = currentNode.value(); + std::string v; + XmlParser::getValueAsString(currentNode, v); + const char *content = v.c_str(); content = fast_atoreal_move(content, (ai_real &)pColor.r); SkipSpacesAndLineEnd(&content); @@ -1353,9 +1360,10 @@ void ColladaParser::ReadSource(XmlNode &node) { if (currentName == "float_array" || currentName == "IDREF_array" || currentName == "Name_array") { ReadDataArray(currentNode); } else if (currentName == "technique_common") { - // I don't care for your profiles - } else if (currentName == "accessor") { - ReadAccessor(currentNode, sourceID); + XmlNode technique = currentNode.child("accessor"); + if (!technique.empty()) { + ReadAccessor(technique, sourceID); + } } } } @@ -1371,7 +1379,9 @@ void ColladaParser::ReadDataArray(XmlNode &node) { XmlParser::getStdStrAttribute(node, "id", id); unsigned int count; XmlParser::getUIntAttribute(node, "count", count); - const char *content = node.value(); + std::string v; + XmlParser::getValueAsString(node, v); + const char *content = v.c_str(); // read values and store inside an array in the data library mDataLibrary[id] = Data(); @@ -1575,7 +1585,9 @@ void ColladaParser::ReadIndexData(XmlNode &node, Mesh &pMesh) { if (numPrimitives) // It is possible to define a mesh without any primitives { // case - specifies the number of indices for each polygon - const char *content = currentNode.value(); + std::string v; + XmlParser::getValueAsString(currentNode, v); + const char *content = v.c_str(); vcount.reserve(numPrimitives); for (unsigned int a = 0; a < numPrimitives; a++) { if (*content == 0) @@ -1694,7 +1706,9 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector 0) // It is possible to not contain any indices { - const char *content = node.value(); + std::string v; + XmlParser::getValueAsString(node, v); + const char *content = v.c_str(); while (*content != 0) { // read a value. // Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways. diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 6f9780e53..2d745aa68 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -209,7 +209,18 @@ public: } -private: + static inline bool getValueAsString( XmlNode &node, std::string &text ) { + text = ""; + if (node.empty()) { + return false; + } + + text = node.text().as_string(); + + return true; + } + + private: pugi::xml_document *mDoc; TNodeType *mRoot; TNodeType mCurrent; @@ -218,6 +229,52 @@ private: using XmlParser = TXmlParser; +class XmlNodeIterator { +public: + XmlNodeIterator() : + mNodes(), + mIndex(9999999) { + // empty + } + + void collectChildrenPreOrder( XmlNode &node ) { + mNodes.push_back(&node); + for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { + collectChildrenPreOrder(currentNode); + } + } + + void collectChildrenPostOrder(XmlNode &node) { + for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { + collectChildrenPostOrder(currentNode); + } + mNodes.push_back(&node); + } + + bool getNext(XmlNode &next) { + if (mIndex == mNodes.size()) { + return false; + } + + mNodes[mIndex]; + ++mIndex; + + return true; + } + + void clear() { + if (mNodes.empty()) { + return; + } + + mNodes.clear(); + } + +private: + std::vector mNodes; + size_t mIndex; + TraverseOrder mTraverseOrder; +}; } // namespace Assimp #endif // !! INCLUDED_AI_IRRXML_WRAPPER