From eda967bd02fd973060b1ad17db10aad37da9822f Mon Sep 17 00:00:00 2001 From: Jean-Louis Date: Mon, 28 Dec 2020 12:31:54 +0100 Subject: [PATCH] Parse the node + refactoring --- code/AssetLib/3MF/3MFXmlTags.h | 2 + code/AssetLib/3MF/D3MFImporter.cpp | 340 ++++++++++++++++++++--------- 2 files changed, 238 insertions(+), 104 deletions(-) diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index abad7793c..49e19c658 100644 --- a/code/AssetLib/3MF/3MFXmlTags.h +++ b/code/AssetLib/3MF/3MFXmlTags.h @@ -55,6 +55,8 @@ namespace XmlTag { static const std::string resources = "resources"; static const std::string object = "object"; static const std::string mesh = "mesh"; + static const std::string components = "components"; + static const std::string component = "component"; static const std::string vertices = "vertices"; static const std::string vertex = "vertex"; static const std::string triangles = "triangles"; diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index 84defe903..47cb5fa8d 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -66,19 +66,73 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace D3MF { +enum class ResourceType { + RT_Object, + RT_BaseMaterials, + RT_Unknown +}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...) + +class Resource +{ +public: + Resource(int id) : + mId(id) {} + + virtual ~Resource() {} + + int mId; + + virtual ResourceType getType() { + return ResourceType::RT_Unknown; + } +}; + +class BaseMaterials : public Resource { +public: + BaseMaterials(int id) : + Resource(id), + mMaterials(), + mMaterialIndex() {} + + std::vector mMaterials; + std::vector mMaterialIndex; + + virtual ResourceType getType() { + return ResourceType::RT_BaseMaterials; + } +}; + +class Object : public Resource { +public: + std::vector mMeshes; + std::vector mMeshIndex; + std::vector mComponents; + + Object(int id) : + Resource(id) {} + + virtual ResourceType getType() { + return ResourceType::RT_Object; + } +}; + + class XmlSerializer { public: XmlSerializer(XmlParser *xmlParser) : mMeshes(), - mBasematerialsDictionnary(), + mResourcesDictionnary(), mMaterialCount(0), + mMeshCount(0), mXmlParser(xmlParser) { // empty } ~XmlSerializer() { - // empty + for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); it++) { + delete it->second; + } } void ImportXml(aiScene *scene) { @@ -86,10 +140,8 @@ public: return; } - scene->mRootNode = new aiNode(); - std::vector children; + scene->mRootNode = new aiNode("3MF"); - std::string nodeName; XmlNode node = mXmlParser->getRootNode().child("model"); if (node.empty()) { return; @@ -98,9 +150,7 @@ public: for (XmlNode currentNode = resNode.first_child(); currentNode; currentNode = currentNode.next_sibling()) { const std::string ¤tNodeName = currentNode.name(); if (currentNodeName == D3MF::XmlTag::object) { - children.push_back(ReadObject(currentNode, scene)); - } else if (currentNodeName == D3MF::XmlTag::build) { - // + ReadObject(currentNode);; } else if (currentNodeName == D3MF::XmlTag::basematerials) { ReadBaseMaterials(currentNode); } else if (currentNodeName == D3MF::XmlTag::meta) { @@ -108,10 +158,75 @@ public: } } - if (scene->mRootNode->mName.length == 0) { - scene->mRootNode->mName.Set("3MF"); + XmlNode buildNode = node.child("build"); + for (XmlNode currentNode = buildNode.first_child(); currentNode; currentNode = currentNode.next_sibling()) { + const std::string ¤tNodeName = currentNode.name(); + if (currentNodeName == D3MF::XmlTag::item) { + int objectId = -1; + std::string transform; + getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId); + bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transform); + + aiNode *sceneNode = new aiNode("item"); + auto it = mResourcesDictionnary.find(objectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it->second); + std::vector meshes; + getMeshesList(obj, meshes); + sceneNode->mNumMeshes = static_cast(meshes.size()); + sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes]; + std::copy(meshes.begin(), meshes.end(), sceneNode->mMeshes); + + if (hasTransform) { + // split the string + std::vector numbers; + std::string currentNumber; + for (size_t i = 0; i < transform.size(); ++i) { + const char c = transform[i]; + if (c == ' ') { + if (currentNumber.size() > 0) { + float f = std::stof(currentNumber); + numbers.push_back(f); + currentNumber.clear(); + } + } else { + currentNumber.push_back(c); + } + } + if (currentNumber.size() > 0) { + float f = std::stof(currentNumber); + numbers.push_back(f); + } + + aiMatrix4x4 transformMatrix; + transformMatrix.a1 = numbers[0]; + transformMatrix.b1 = numbers[1]; + transformMatrix.c1 = numbers[2]; + transformMatrix.d1 = 0; + + transformMatrix.a2 = numbers[3]; + transformMatrix.b2 = numbers[4]; + transformMatrix.c2 = numbers[5]; + transformMatrix.d2 = 0; + + transformMatrix.a3 = numbers[6]; + transformMatrix.b3 = numbers[7]; + transformMatrix.c3 = numbers[8]; + transformMatrix.d3 = 0; + + transformMatrix.a4 = numbers[9]; + transformMatrix.b4 = numbers[10]; + transformMatrix.c4 = numbers[11]; + transformMatrix.d4 = 1; + sceneNode->mTransformation = transformMatrix; + } + + scene->mRootNode->addChildren(1, &sceneNode); + } + } } + // import the metadata if (!mMetaData.empty()) { const size_t numMeta(mMetaData.size()); @@ -131,17 +246,15 @@ public: scene->mNumMaterials = static_cast(mMaterialCount); if (0 != scene->mNumMaterials) { scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; - for (auto it = mBasematerialsDictionnary.begin(); it != mBasematerialsDictionnary.end(); it++) { - for (unsigned int i = 0; i < it->second.size(); ++i) { - scene->mMaterials[it->second[i].first] = it->second[i].second; + for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); it++) { + if (it->second->getType() == ResourceType::RT_BaseMaterials) { + BaseMaterials *baseMaterials = static_cast(it->second); + for (unsigned int i = 0; i < baseMaterials->mMaterials.size(); ++i) { + scene->mMaterials[baseMaterials->mMaterialIndex[i]] = baseMaterials->mMaterials[i]; + } } } } - - // create the scene-graph - scene->mRootNode->mNumChildren = static_cast(children.size()); - scene->mRootNode->mChildren = new aiNode *[scene->mRootNode->mNumChildren](); - std::copy(children.begin(), children.end(), scene->mRootNode->mChildren); } private: @@ -156,53 +269,79 @@ private: } } - aiNode *ReadObject(XmlNode &node, aiScene *scene) { - std::unique_ptr nodePtr(new aiNode()); + bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { + std::string strValue; + bool ret = getNodeAttribute(node, attribute, strValue); + if (ret) { + value = std::atoi(strValue.c_str()); + return true; + } else { + return false; + } + } - std::vector meshIds; + void getMeshesList(Object *obj, std::vector &meshes) { + for (size_t i = 0; i < obj->mMeshIndex.size(); ++i) { + meshes.push_back(obj->mMeshIndex[i]); + } + for (size_t i = 0; i < obj->mComponents.size(); ++i) { + int componentId = obj->mComponents[i]; + auto it = mResourcesDictionnary.find(componentId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) + getMeshesList(static_cast(it->second), meshes); + } + } - std::string id, type, pid, pindex; + void ReadObject(XmlNode &node) { + int id = -1, pid = -1, pindex = -1; bool hasId = getNodeAttribute(node, D3MF::XmlTag::id, id); //bool hasType = getNodeAttribute(node, D3MF::XmlTag::type, type); not used currently bool hasPid = getNodeAttribute(node, D3MF::XmlTag::pid, pid); bool hasPindex = getNodeAttribute(node, D3MF::XmlTag::pindex, pindex); + std::string idStr = to_string(id); + if (!hasId) { - return nullptr; + return; } - nodePtr->mParent = scene->mRootNode; - nodePtr->mName.Set(id); - - size_t meshIdx = mMeshes.size(); + Object *obj = new Object(id); for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { const std::string ¤tName = currentNode.name(); if (currentName == D3MF::XmlTag::mesh) { auto mesh = ReadMesh(currentNode); - mesh->mName.Set(id); - if (hasPid && hasPindex && mBasematerialsDictionnary.find(atoi(pid.c_str())) != mBasematerialsDictionnary.end()) { - int iPid = atoi(pid.c_str()); - int iPindex = atoi(pindex.c_str()); - mesh->mMaterialIndex = mBasematerialsDictionnary[iPid][iPindex].first; + mesh->mName.Set(idStr); + + if (hasPid) { + auto it = mResourcesDictionnary.find(pid); + if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) { + BaseMaterials *materials = static_cast(it->second); + mesh->mMaterialIndex = materials->mMaterialIndex[pindex]; + } } mMeshes.push_back(mesh); - meshIds.push_back(static_cast(meshIdx)); - ++meshIdx; + + obj->mMeshes.push_back(mesh); + obj->mMeshIndex.push_back(mMeshCount); + mMeshCount++; + } else if (currentName == D3MF::XmlTag::components) { + for (XmlNode currentSubNode = currentNode.first_child(); currentSubNode; currentSubNode = currentSubNode.next_sibling()) { + if (currentSubNode.name() == D3MF::XmlTag::component) { + int objectId = -1; + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) + obj->mComponents.push_back(objectId); + } + } } } - nodePtr->mNumMeshes = static_cast(meshIds.size()); - - nodePtr->mMeshes = new unsigned int[nodePtr->mNumMeshes]; - - std::copy(meshIds.begin(), meshIds.end(), nodePtr->mMeshes); - - return nodePtr.release(); + mResourcesDictionnary.insert(std::make_pair(id, obj)); } aiMesh *ReadMesh(XmlNode &node) { aiMesh *mesh = new aiMesh(); + for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { const std::string ¤tName = currentNode.name(); if (currentName == D3MF::XmlTag::vertices) { @@ -241,6 +380,7 @@ private: mesh->mNumVertices = static_cast(vertices.size()); mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; std::copy(vertices.begin(), vertices.end(), mesh->mVertices); } @@ -258,14 +398,23 @@ private: for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { const std::string ¤tName = currentNode.name(); if (currentName == D3MF::XmlTag::triangle) { - faces.push_back(ReadTriangle(currentNode)); - const char *pidToken = currentNode.attribute(D3MF::XmlTag::pid.c_str()).as_string(); - const char *p1Token = currentNode.attribute(D3MF::XmlTag::p1.c_str()).as_string(); - if (nullptr != pidToken && nullptr != p1Token && mBasematerialsDictionnary.find(std::atoi(pidToken)) != mBasematerialsDictionnary.end()) { - int pid(std::atoi(pidToken)); - int p1(std::atoi(p1Token)); - mesh->mMaterialIndex = mBasematerialsDictionnary[pid][p1].first; - // TODO: manage the separation into several meshes if the triangles of the mesh do not all refer to the same material + aiFace face = ReadTriangle(currentNode); + faces.push_back(face); + + int pid, p1; + bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); + bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); + + if (hasPid && hasP1) { + auto it = mResourcesDictionnary.find(pid); + if (it != mResourcesDictionnary.end()) + { + if (it->second->getType() == ResourceType::RT_BaseMaterials) { + BaseMaterials *baseMaterials = static_cast(it->second); + mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1]; + } + // TODO: manage the separation into several meshes if the triangles of the mesh do not all refer to the same material + } } } } @@ -290,21 +439,20 @@ private: } void ReadBaseMaterials(XmlNode &node) { - std::vector MatIdArray; - const char *baseMaterialId = node.attribute(D3MF::XmlTag::basematerials_id.c_str()).as_string(); - if (nullptr != baseMaterialId) { - unsigned int id = std::atoi(baseMaterialId); - std::vector > materials; + int id = -1; + if (getNodeAttribute(node, D3MF::XmlTag::basematerials_id, id)) { + BaseMaterials* baseMaterials = new BaseMaterials(id); for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { if (currentNode.name() == D3MF::XmlTag::basematerials_base) { - materials.push_back(std::make_pair(mMaterialCount, readMaterialDef(currentNode, id))); + baseMaterials->mMaterialIndex.push_back(mMaterialCount); + baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id)); mMaterialCount++; } } - mBasematerialsDictionnary.insert(std::make_pair(id, materials)); + mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); } } @@ -320,37 +468,24 @@ private: } const char *buf(color); - if ('#' != *buf) { + if ('#' != buf[0]) { return false; } - ++buf; - char comp[3] = { 0, 0, '\0' }; - comp[0] = *buf; - ++buf; - comp[1] = *buf; - ++buf; - diffuse.r = static_cast(strtol(comp, nullptr, 16)) / ai_real(255.0); + char r[3] = { buf[1], buf[2], '\0' }; + diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); - comp[0] = *buf; - ++buf; - comp[1] = *buf; - ++buf; - diffuse.g = static_cast(strtol(comp, nullptr, 16)) / ai_real(255.0); + char g[3] = { buf[3], buf[4], '\0' }; + diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); - comp[0] = *buf; - ++buf; - comp[1] = *buf; - ++buf; - diffuse.b = static_cast(strtol(comp, nullptr, 16)) / ai_real(255.0); + char b[3] = { buf[5], buf[6], '\0' }; + diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); if (7 == len) return true; - comp[0] = *buf; - ++buf; - comp[1] = *buf; - ++buf; - diffuse.a = static_cast(strtol(comp, nullptr, 16)) / ai_real(255.0); + + char a[3] = { buf[7], buf[8], '\0' }; + diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); return true; } @@ -364,32 +499,29 @@ private: } aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId) { - aiMaterial *mat(nullptr); - const char *name(nullptr); - const std::string nodeName = node.name(); - if (nodeName == D3MF::XmlTag::basematerials_base) { - name = node.attribute(D3MF::XmlTag::basematerials_name.c_str()).as_string(); - std::string stdMatName; - aiString matName; - std::string strId(to_string(basematerialsId)); - stdMatName += "id"; - stdMatName += strId; - stdMatName += "_"; - if (nullptr != name) { - stdMatName += std::string(name); - } else { - stdMatName += "basemat_"; - stdMatName += to_string(mMaterialCount - basematerialsId); - } - matName.Set(stdMatName); + aiMaterial *material = new aiMaterial(); + material->mNumProperties = 0; + std::string name; + bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name); - mat = new aiMaterial; - mat->AddProperty(&matName, AI_MATKEY_NAME); - - assignDiffuseColor(node, mat); + std::string stdMaterialName; + std::string strId(to_string(basematerialsId)); + stdMaterialName += "id"; + stdMaterialName += strId; + stdMaterialName += "_"; + if (hasName) { + stdMaterialName += std::string(name); + } else { + stdMaterialName += "basemat_"; + stdMaterialName += to_string(mMaterialCount - basematerialsId); } - return mat; + aiString assimpMaterialName(stdMaterialName); + material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME); + + assignDiffuseColor(node, material); + + return material; } private: @@ -399,8 +531,8 @@ private: }; std::vector mMetaData; std::vector mMeshes; - std::map>> mBasematerialsDictionnary; - unsigned int mMaterialCount; + std::map mResourcesDictionnary; + unsigned int mMaterialCount, mMeshCount; XmlParser *mXmlParser; };