From c534e655f98948c7f428abb28ed5fd6df009a610 Mon Sep 17 00:00:00 2001 From: wmayer Date: Tue, 19 Nov 2024 21:04:30 +0100 Subject: [PATCH] Mesh: Improve 3MF format to support files from 'Make World' --- src/Mod/Mesh/App/Core/IO/Reader3MF.cpp | 404 +++++++++++++++++-------- src/Mod/Mesh/App/Core/IO/Reader3MF.h | 57 ++-- 2 files changed, 313 insertions(+), 148 deletions(-) diff --git a/src/Mod/Mesh/App/Core/IO/Reader3MF.cpp b/src/Mod/Mesh/App/Core/IO/Reader3MF.cpp index 63d4d0cf7b..6f7610173e 100644 --- a/src/Mod/Mesh/App/Core/IO/Reader3MF.cpp +++ b/src/Mod/Mesh/App/Core/IO/Reader3MF.cpp @@ -41,26 +41,21 @@ using namespace MeshCore; -#ifndef XERCES_CPP_NAMESPACE_BEGIN -#define XERCES_CPP_NAMESPACE_QUALIFIER using namespace XERCES_CPP_NAMESPACE; -#else -XERCES_CPP_NAMESPACE_USE -#endif Reader3MF::Reader3MF(std::istream& str) { - zipios::ZipHeader zipHeader(str); - if (zipHeader.isValid()) { - zip.reset(zipHeader.getInputStream("3D/3dmodel.model")); + file = std::make_unique(str); + if (file->isValid()) { + zip.reset(file->getInputStream("3D/3dmodel.model")); } } Reader3MF::Reader3MF(const std::string& filename) { - zipios::ZipFile zipFile(filename); - if (zipFile.isValid()) { - zip.reset(zipFile.getInputStream("3D/3dmodel.model")); + file = std::make_unique(filename); + if (file->isValid()) { + zip.reset(file->getInputStream("3D/3dmodel.model")); } } @@ -78,30 +73,36 @@ std::vector Reader3MF::GetMeshIds() const bool Reader3MF::Load() { try { - if (!zip) { - return false; - } - return LoadModel(*zip); + return TryLoad(); } catch (const std::exception&) { return false; } } +bool Reader3MF::TryLoad() +{ + if (!zip) { + return false; + } + if (LoadModel(*zip)) { + return true; + } + + return LoadMeshFromComponents(); +} + bool Reader3MF::LoadModel(std::istream& str) { - try { - std::unique_ptr parser(new XercesDOMParser); - parser->setValidationScheme(XercesDOMParser::Val_Auto); - parser->setDoNamespaces(false); - parser->setDoSchema(false); - parser->setValidationSchemaFullChecking(false); - parser->setCreateEntityReferenceNodes(false); + Component comp; + comp.path = "3dmodel.model"; + return LoadModel(str, comp); +} - Base::StdInputSource inputSource(str, "3dmodel.model"); - parser->parse(inputSource); - std::unique_ptr xmlDocument(parser->adoptDocument()); - return LoadModel(*xmlDocument); +bool Reader3MF::LoadModel(std::istream& str, const Component& comp) +{ + try { + return TryLoadModel(str, comp); } catch (const XMLException&) { return false; @@ -111,24 +112,52 @@ bool Reader3MF::LoadModel(std::istream& str) } } -bool Reader3MF::LoadModel(XERCES_CPP_NAMESPACE::DOMDocument& xmlDocument) +std::unique_ptr Reader3MF::makeDomParser() +{ + std::unique_ptr parser(new XercesDOMParser); + parser->setValidationScheme(XercesDOMParser::Val_Auto); + parser->setDoNamespaces(false); + parser->setDoSchema(false); + parser->setValidationSchemaFullChecking(false); + parser->setCreateEntityReferenceNodes(false); + return parser; +} + +bool Reader3MF::TryLoadModel(std::istream& str, const Component& comp) +{ + if (!str) { + return false; + } + + Base::StdInputSource inputSource(str, comp.path.c_str()); + std::unique_ptr parser = makeDomParser(); + parser->parse(inputSource); + std::unique_ptr xmlDocument(parser->adoptDocument()); + return LoadModel(*xmlDocument, comp); +} + +bool Reader3MF::LoadModel(DOMDocument& xmlDocument, const Component& comp) { DOMNodeList* nodes = xmlDocument.getElementsByTagName(XStr("model").unicodeForm()); for (XMLSize_t i = 0; i < nodes->getLength(); i++) { DOMNode* node = nodes->item(i); if (node->getNodeType() == DOMNode::ELEMENT_NODE) { - bool resource = LoadResources(static_cast(node)->getElementsByTagName( - XStr("resources").unicodeForm())); - bool build = LoadBuild( - static_cast(node)->getElementsByTagName(XStr("build").unicodeForm())); - return (resource && build); + return LoadResourcesAndBuild(static_cast(node), comp); } } return false; } -bool Reader3MF::LoadResources(DOMNodeList* nodes) +bool Reader3MF::LoadResourcesAndBuild(DOMElement* node, const Component& comp) +{ + bool resource = + LoadResources(node->getElementsByTagName(XStr("resources").unicodeForm()), comp); + bool build = LoadBuild(node->getElementsByTagName(XStr("build").unicodeForm())); + return (resource && build); +} + +bool Reader3MF::LoadResources(DOMNodeList* nodes, const Component& comp) { if (!nodes) { return false; @@ -137,16 +166,16 @@ bool Reader3MF::LoadResources(DOMNodeList* nodes) for (XMLSize_t i = 0; i < nodes->getLength(); i++) { DOMNode* node = nodes->item(i); if (node->getNodeType() == DOMNode::ELEMENT_NODE) { - DOMNodeList* objectList = - static_cast(node)->getElementsByTagName(XStr("object").unicodeForm()); - return LoadObjects(objectList); + auto elem = static_cast(node); + DOMNodeList* objectList = elem->getElementsByTagName(XStr("object").unicodeForm()); + return LoadObject(objectList, comp); } } return false; } -bool Reader3MF::LoadBuild(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes) +bool Reader3MF::LoadBuild(DOMNodeList* nodes) { if (!nodes) { return false; @@ -155,8 +184,8 @@ bool Reader3MF::LoadBuild(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes) for (XMLSize_t i = 0; i < nodes->getLength(); i++) { DOMNode* node = nodes->item(i); if (node->getNodeType() == DOMNode::ELEMENT_NODE) { - DOMNodeList* objectList = - static_cast(node)->getElementsByTagName(XStr("item").unicodeForm()); + auto elem = static_cast(node); + DOMNodeList* objectList = elem->getElementsByTagName(XStr("item").unicodeForm()); return LoadItems(objectList); } } @@ -164,57 +193,85 @@ bool Reader3MF::LoadBuild(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes) return false; } -bool Reader3MF::LoadItems(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes) +bool Reader3MF::LoadItems(DOMNodeList* nodes) { - const std::size_t numEntries = 12; if (!nodes) { return false; } for (XMLSize_t i = 0; i < nodes->getLength(); i++) { DOMNode* itemNode = nodes->item(i); - DOMNode* idAttr = itemNode->getAttributes()->getNamedItem(XStr("objectid").unicodeForm()); - if (idAttr) { - std::string id = StrX(idAttr->getNodeValue()).c_str(); - int idValue = std::stoi(id); - Base::Matrix4D mat; - - DOMNode* transformAttr = - itemNode->getAttributes()->getNamedItem(XStr("transform").unicodeForm()); - if (transformAttr) { - std::string transform = StrX(transformAttr->getNodeValue()).c_str(); - boost::char_separator sep(" ,"); - boost::tokenizer> tokens(transform, sep); - std::vector token_results; - token_results.assign(tokens.begin(), tokens.end()); - if (token_results.size() == numEntries) { - mat[0][0] = std::stod(token_results[0]); - mat[1][0] = std::stod(token_results[1]); - mat[2][0] = std::stod(token_results[2]); - mat[0][1] = std::stod(token_results[3]); - mat[1][1] = std::stod(token_results[4]); - mat[2][1] = std::stod(token_results[5]); - mat[0][2] = std::stod(token_results[6]); - mat[1][2] = std::stod(token_results[7]); - mat[2][2] = std::stod(token_results[8]); - mat[0][3] = std::stod(token_results[9]); - mat[1][3] = std::stod(token_results[10]); - mat[2][3] = std::stod(token_results[11]); - - try { - meshes.at(idValue).second = mat; - } - catch (const std::exception&) { - } - } - } - } + DOMNamedNodeMap* nodeMap = itemNode->getAttributes(); + LoadItem(nodeMap); } return true; } -bool Reader3MF::LoadObjects(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes) +void Reader3MF::LoadItem(DOMNamedNodeMap* nodeMap) +{ + DOMNode* idAttr = nodeMap->getNamedItem(XStr("objectid").unicodeForm()); + if (idAttr) { + std::string id = StrX(idAttr->getNodeValue()).c_str(); + int idValue = std::stoi(id); + + DOMNode* transformAttr = nodeMap->getNamedItem(XStr("transform").unicodeForm()); + if (transformAttr) { + std::optional mat = ReadTransform(transformAttr); + if (mat) { + auto it = meshes.find(idValue); + if (it != meshes.end()) { + it->second.second = mat.value(); + } + + auto jt = std::find_if(components.begin(), + components.end(), + [idValue](const Component& comp) { + return comp.id == idValue; + }); + if (jt != components.end()) { + jt->transform = mat.value(); + } + } + } + } +} + +std::optional Reader3MF::ReadTransform(DOMNode* transformAttr) +{ + constexpr const std::size_t numEntries = 12; + using Pos2d = std::array, numEntries>; + // clang-format off + static Pos2d pos = {{ + {0, 0}, {1, 0}, {2, 0}, + {0, 1}, {1, 1}, {2, 1}, + {0, 2}, {1, 2}, {2, 2}, + {0, 3}, {1, 3}, {2, 3} + }}; + // clang-format on + + if (transformAttr) { + std::string transform = StrX(transformAttr->getNodeValue()).c_str(); + boost::char_separator sep(" ,"); + boost::tokenizer> tokens(transform, sep); + std::vector token_results; + token_results.assign(tokens.begin(), tokens.end()); + if (token_results.size() == numEntries) { + Base::Matrix4D mat; + // NOLINTBEGIN + int index = 0; + for (const auto& it : pos) { + auto [r, c] = it; + mat[r][c] = std::stod(token_results[index++]); + } + // NOLINTEND + return mat; + } + } + return {}; +} + +bool Reader3MF::LoadObject(DOMNodeList* nodes, const Component& comp) { if (!nodes) { return false; @@ -224,11 +281,18 @@ bool Reader3MF::LoadObjects(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes) DOMNode* objectNode = nodes->item(i); if (objectNode->getNodeType() == DOMNode::ELEMENT_NODE) { DOMNode* idAttr = objectNode->getAttributes()->getNamedItem(XStr("id").unicodeForm()); + auto elem = static_cast(objectNode); if (idAttr) { int id = std::stoi(StrX(idAttr->getNodeValue()).c_str()); - DOMNodeList* meshList = static_cast(objectNode) - ->getElementsByTagName(XStr("mesh").unicodeForm()); - LoadMesh(meshList, id); + DOMNodeList* meshNode = elem->getElementsByTagName(XStr("mesh").unicodeForm()); + if (meshNode->getLength() > 0) { + LoadMesh(meshNode, id, comp); + } + else { + DOMNodeList* compNode = + elem->getElementsByTagName(XStr("components").unicodeForm()); + LoadComponents(compNode, id); + } } } } @@ -236,7 +300,77 @@ bool Reader3MF::LoadObjects(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes) return (!meshes.empty()); } -void Reader3MF::LoadMesh(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes, int id) +bool Reader3MF::LoadMeshFromComponents() +{ + for (const auto& it : components) { + std::string path = it.path.substr(1); + zip.reset(file->getInputStream(path)); + LoadModel(*zip, it); + } + + return (!meshes.empty()); +} + +void Reader3MF::LoadComponents(DOMNodeList* nodes, int id) +{ + if (!nodes) { + return; + } + + for (XMLSize_t i = 0; i < nodes->getLength(); i++) { + DOMNode* objectNode = nodes->item(i); + if (objectNode->getNodeType() == DOMNode::ELEMENT_NODE) { + auto elem = static_cast(objectNode); + DOMNodeList* compNode = elem->getElementsByTagName(XStr("component").unicodeForm()); + if (compNode->getLength() > 0) { + LoadComponent(compNode, id); + } + } + } +} + +void Reader3MF::LoadComponent(DOMNodeList* nodes, int id) +{ + if (!nodes) { + return; + } + + for (XMLSize_t i = 0; i < nodes->getLength(); i++) { + DOMNode* compNode = nodes->item(i); + if (compNode->getNodeType() == DOMNode::ELEMENT_NODE) { + if (DOMNamedNodeMap* attr = compNode->getAttributes()) { + LoadComponent(attr, id); + } + } + } +} + +void Reader3MF::LoadComponent(DOMNamedNodeMap* attr, int id) +{ + auto validComponent = [](const Component& comp) { + return (comp.id > 0 && comp.objectId >= 0 && !comp.path.empty()); + }; + + Component component; + component.id = id; + if (DOMNode* pathAttr = attr->getNamedItem(XStr("p:path").unicodeForm())) { + component.path = StrX(pathAttr->getNodeValue()).c_str(); + } + if (DOMNode* idAttr = attr->getNamedItem(XStr("objectid").unicodeForm())) { + component.objectId = std::stoi(StrX(idAttr->getNodeValue()).c_str()); + } + if (DOMNode* transformAttr = attr->getNamedItem(XStr("transform").unicodeForm())) { + std::optional mat = ReadTransform(transformAttr); + if (mat) { + component.transform = mat.value(); + } + } + if (validComponent(component)) { + components.push_back(component); + } +} + +void Reader3MF::LoadMesh(DOMNodeList* nodes, int id, const Component& comp) { if (!nodes) { return; @@ -245,29 +379,26 @@ void Reader3MF::LoadMesh(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes, int for (XMLSize_t i = 0; i < nodes->getLength(); i++) { DOMNode* node = nodes->item(i); if (node->getNodeType() == DOMNode::ELEMENT_NODE) { + auto elem = static_cast(node); MeshPointArray points; MeshFacetArray facets; - LoadVertices(static_cast(node)->getElementsByTagName( - XStr("vertices").unicodeForm()), - points); - LoadTriangles(static_cast(node)->getElementsByTagName( - XStr("triangles").unicodeForm()), - facets); + LoadVertices(elem->getElementsByTagName(XStr("vertices").unicodeForm()), points); + LoadTriangles(elem->getElementsByTagName(XStr("triangles").unicodeForm()), facets); MeshCleanup meshCleanup(points, facets); meshCleanup.RemoveInvalids(); MeshPointFacetAdjacency meshAdj(points.size(), facets); meshAdj.SetFacetNeighbourhood(); + Base::Matrix4D mat = comp.transform; MeshKernel kernel; kernel.Adopt(points, facets); - meshes.emplace(id, std::make_pair(kernel, Base::Matrix4D())); + meshes.emplace(id, std::make_pair(kernel, mat)); } } } -void Reader3MF::LoadVertices(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes, - MeshPointArray& points) +void Reader3MF::LoadVertices(DOMNodeList* nodes, MeshPointArray& points) { if (!nodes) { return; @@ -276,33 +407,39 @@ void Reader3MF::LoadVertices(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes, for (XMLSize_t i = 0; i < nodes->getLength(); i++) { DOMNode* node = nodes->item(i); if (node->getNodeType() == DOMNode::ELEMENT_NODE) { - DOMNodeList* vertexList = - static_cast(node)->getElementsByTagName(XStr("vertex").unicodeForm()); + auto elem = static_cast(node); + DOMNodeList* vertexList = elem->getElementsByTagName(XStr("vertex").unicodeForm()); if (vertexList) { - XMLSize_t numVertices = vertexList->getLength(); - points.reserve(numVertices); - for (XMLSize_t j = 0; j < numVertices; j++) { - DOMNode* vertexNode = vertexList->item(j); - DOMNamedNodeMap* attr = vertexNode->getAttributes(); - if (attr) { - DOMNode* xAttr = attr->getNamedItem(XStr("x").unicodeForm()); - DOMNode* yAttr = attr->getNamedItem(XStr("y").unicodeForm()); - DOMNode* zAttr = attr->getNamedItem(XStr("z").unicodeForm()); - if (xAttr && yAttr && zAttr) { - float x = std::stof(StrX(xAttr->getNodeValue()).c_str()); - float y = std::stof(StrX(yAttr->getNodeValue()).c_str()); - float z = std::stof(StrX(zAttr->getNodeValue()).c_str()); - points.emplace_back(x, y, z); - } - } - } + ReadVertices(vertexList, points); } } } } -void Reader3MF::LoadTriangles(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes, - MeshFacetArray& facets) +void Reader3MF::ReadVertices(DOMNodeList* vertexList, MeshPointArray& points) +{ + XMLSize_t numVertices = vertexList->getLength(); + points.reserve(numVertices); + for (XMLSize_t j = 0; j < numVertices; j++) { + DOMNode* vertexNode = vertexList->item(j); + DOMNamedNodeMap* attr = vertexNode->getAttributes(); + if (attr) { + DOMNode* xAttr = attr->getNamedItem(XStr("x").unicodeForm()); + DOMNode* yAttr = attr->getNamedItem(XStr("y").unicodeForm()); + DOMNode* zAttr = attr->getNamedItem(XStr("z").unicodeForm()); + if (xAttr && yAttr && zAttr) { + // NOLINTBEGIN + float x = std::stof(StrX(xAttr->getNodeValue()).c_str()); + float y = std::stof(StrX(yAttr->getNodeValue()).c_str()); + float z = std::stof(StrX(zAttr->getNodeValue()).c_str()); + points.emplace_back(x, y, z); + // NOLINTEND + } + } + } +} + +void Reader3MF::LoadTriangles(DOMNodeList* nodes, MeshFacetArray& facets) { if (!nodes) { return; @@ -311,26 +448,31 @@ void Reader3MF::LoadTriangles(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList* nodes, for (XMLSize_t i = 0; i < nodes->getLength(); i++) { DOMNode* node = nodes->item(i); if (node->getNodeType() == DOMNode::ELEMENT_NODE) { - DOMNodeList* triangleList = static_cast(node)->getElementsByTagName( - XStr("triangle").unicodeForm()); + auto elem = static_cast(node); + DOMNodeList* triangleList = elem->getElementsByTagName(XStr("triangle").unicodeForm()); if (triangleList) { - XMLSize_t numTriangles = triangleList->getLength(); - facets.reserve(numTriangles); - for (XMLSize_t j = 0; j < numTriangles; j++) { - DOMNode* triangleNode = triangleList->item(j); - DOMNamedNodeMap* attr = triangleNode->getAttributes(); - if (attr) { - DOMNode* v1Attr = attr->getNamedItem(XStr("v1").unicodeForm()); - DOMNode* v2Attr = attr->getNamedItem(XStr("v2").unicodeForm()); - DOMNode* v3Attr = attr->getNamedItem(XStr("v3").unicodeForm()); - if (v1Attr && v2Attr && v3Attr) { - PointIndex v1 = std::stoul(StrX(v1Attr->getNodeValue()).c_str()); - PointIndex v2 = std::stoul(StrX(v2Attr->getNodeValue()).c_str()); - PointIndex v3 = std::stoul(StrX(v3Attr->getNodeValue()).c_str()); - facets.emplace_back(v1, v2, v3); - } - } - } + ReadTriangles(triangleList, facets); + } + } + } +} + +void Reader3MF::ReadTriangles(DOMNodeList* triangleList, MeshFacetArray& facets) +{ + XMLSize_t numTriangles = triangleList->getLength(); + facets.reserve(numTriangles); + for (XMLSize_t j = 0; j < numTriangles; j++) { + DOMNode* triangleNode = triangleList->item(j); + DOMNamedNodeMap* attr = triangleNode->getAttributes(); + if (attr) { + DOMNode* v1Attr = attr->getNamedItem(XStr("v1").unicodeForm()); + DOMNode* v2Attr = attr->getNamedItem(XStr("v2").unicodeForm()); + DOMNode* v3Attr = attr->getNamedItem(XStr("v3").unicodeForm()); + if (v1Attr && v2Attr && v3Attr) { + PointIndex v1 = std::stoul(StrX(v1Attr->getNodeValue()).c_str()); + PointIndex v2 = std::stoul(StrX(v2Attr->getNodeValue()).c_str()); + PointIndex v3 = std::stoul(StrX(v3Attr->getNodeValue()).c_str()); + facets.emplace_back(v1, v2, v3); } } } diff --git a/src/Mod/Mesh/App/Core/IO/Reader3MF.h b/src/Mod/Mesh/App/Core/IO/Reader3MF.h index 13c837d2fe..bc261061f2 100644 --- a/src/Mod/Mesh/App/Core/IO/Reader3MF.h +++ b/src/Mod/Mesh/App/Core/IO/Reader3MF.h @@ -28,23 +28,24 @@ #include #include #include +#include #include #include -#ifndef XERCES_CPP_NAMESPACE_BEGIN -#define XERCES_CPP_NAMESPACE_QUALIFIER -using namespace XERCES_CPP_NAMESPACE; namespace XERCES_CPP_NAMESPACE { class DOMDocument; +class DOMElement; +class DOMNode; class DOMNodeList; +class DOMNamedNodeMap; +class XercesDOMParser; } // namespace XERCES_CPP_NAMESPACE -#else -XERCES_CPP_NAMESPACE_BEGIN -class DOMDocument; -class DOMNodeList; -XERCES_CPP_NAMESPACE_END -#endif + +namespace zipios +{ +class FileCollection; +} namespace MeshCore { @@ -84,19 +85,41 @@ public: } private: + struct Component + { + int id = -1; + int objectId = -1; + std::string path; + Base::Matrix4D transform; + }; + static std::unique_ptr makeDomParser(); + bool TryLoad(); bool LoadModel(std::istream&); - bool LoadModel(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument&); - bool LoadResources(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList*); - bool LoadBuild(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList*); - bool LoadItems(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList*); - bool LoadObjects(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList*); - void LoadMesh(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList*, int id); - void LoadVertices(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList*, MeshPointArray&); - void LoadTriangles(XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList*, MeshFacetArray&); + bool LoadModel(std::istream&, const Component&); + bool TryLoadModel(std::istream&, const Component&); + bool LoadModel(XERCES_CPP_NAMESPACE::DOMDocument&, const Component&); + bool LoadResourcesAndBuild(XERCES_CPP_NAMESPACE::DOMElement*, const Component&); + bool LoadResources(XERCES_CPP_NAMESPACE::DOMNodeList*, const Component&); + bool LoadBuild(XERCES_CPP_NAMESPACE::DOMNodeList*); + bool LoadItems(XERCES_CPP_NAMESPACE::DOMNodeList*); + void LoadItem(XERCES_CPP_NAMESPACE::DOMNamedNodeMap*); + bool LoadObject(XERCES_CPP_NAMESPACE::DOMNodeList*, const Component&); + void LoadComponents(XERCES_CPP_NAMESPACE::DOMNodeList*, int id); + void LoadComponent(XERCES_CPP_NAMESPACE::DOMNodeList*, int id); + void LoadComponent(XERCES_CPP_NAMESPACE::DOMNamedNodeMap*, int id); + void LoadMesh(XERCES_CPP_NAMESPACE::DOMNodeList*, int id, const Component&); + void LoadVertices(XERCES_CPP_NAMESPACE::DOMNodeList*, MeshPointArray&); + void ReadVertices(XERCES_CPP_NAMESPACE::DOMNodeList*, MeshPointArray&); + void LoadTriangles(XERCES_CPP_NAMESPACE::DOMNodeList*, MeshFacetArray&); + void ReadTriangles(XERCES_CPP_NAMESPACE::DOMNodeList*, MeshFacetArray&); + bool LoadMeshFromComponents(); + std::optional ReadTransform(XERCES_CPP_NAMESPACE::DOMNode*); private: + std::vector components; using MeshKernelAndTransform = std::pair; std::unordered_map meshes; + std::unique_ptr file; std::unique_ptr zip; };