Mesh: Improve 3MF format to support files from 'Make World'

This commit is contained in:
wmayer
2024-11-19 21:04:30 +01:00
committed by wwmayer
parent c0b9191cf5
commit c534e655f9
2 changed files with 313 additions and 148 deletions

View File

@@ -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<zipios::ZipHeader>(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<zipios::ZipFile>(filename);
if (file->isValid()) {
zip.reset(file->getInputStream("3D/3dmodel.model"));
}
}
@@ -78,30 +73,36 @@ std::vector<int> 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<XercesDOMParser> 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<XERCES_CPP_NAMESPACE::DOMDocument> 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<XercesDOMParser> Reader3MF::makeDomParser()
{
std::unique_ptr<XercesDOMParser> 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<XercesDOMParser> parser = makeDomParser();
parser->parse(inputSource);
std::unique_ptr<DOMDocument> 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<DOMElement*>(node)->getElementsByTagName(
XStr("resources").unicodeForm()));
bool build = LoadBuild(
static_cast<DOMElement*>(node)->getElementsByTagName(XStr("build").unicodeForm()));
return (resource && build);
return LoadResourcesAndBuild(static_cast<DOMElement*>(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<DOMElement*>(node)->getElementsByTagName(XStr("object").unicodeForm());
return LoadObjects(objectList);
auto elem = static_cast<DOMElement*>(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<DOMElement*>(node)->getElementsByTagName(XStr("item").unicodeForm());
auto elem = static_cast<DOMElement*>(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<char> sep(" ,");
boost::tokenizer<boost::char_separator<char>> tokens(transform, sep);
std::vector<std::string> 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<Base::Matrix4D> 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<Base::Matrix4D> Reader3MF::ReadTransform(DOMNode* transformAttr)
{
constexpr const std::size_t numEntries = 12;
using Pos2d = std::array<std::array<int, 2>, 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<char> sep(" ,");
boost::tokenizer<boost::char_separator<char>> tokens(transform, sep);
std::vector<std::string> 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<DOMElement*>(objectNode);
if (idAttr) {
int id = std::stoi(StrX(idAttr->getNodeValue()).c_str());
DOMNodeList* meshList = static_cast<DOMElement*>(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<DOMElement*>(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<Base::Matrix4D> 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<DOMElement*>(node);
MeshPointArray points;
MeshFacetArray facets;
LoadVertices(static_cast<DOMElement*>(node)->getElementsByTagName(
XStr("vertices").unicodeForm()),
points);
LoadTriangles(static_cast<DOMElement*>(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<DOMElement*>(node)->getElementsByTagName(XStr("vertex").unicodeForm());
auto elem = static_cast<DOMElement*>(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<DOMElement*>(node)->getElementsByTagName(
XStr("triangle").unicodeForm());
auto elem = static_cast<DOMElement*>(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);
}
}
}

View File

@@ -28,23 +28,24 @@
#include <Mod/Mesh/MeshGlobal.h>
#include <iosfwd>
#include <memory>
#include <optional>
#include <unordered_map>
#include <xercesc/util/XercesDefs.hpp>
#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<XERCES_CPP_NAMESPACE::XercesDOMParser> 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<Base::Matrix4D> ReadTransform(XERCES_CPP_NAMESPACE::DOMNode*);
private:
std::vector<Component> components;
using MeshKernelAndTransform = std::pair<MeshKernel, Base::Matrix4D>;
std::unordered_map<int, MeshKernelAndTransform> meshes;
std::unique_ptr<zipios::FileCollection> file;
std::unique_ptr<std::istream> zip;
};