diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index 0c6b5d1eb..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"; @@ -67,6 +69,7 @@ namespace XmlTag { static const std::string v3 = "v3"; static const std::string id = "id"; static const std::string pid = "pid"; + static const std::string pindex = "pindex"; static const std::string p1 = "p1"; static const std::string name = "name"; static const std::string type = "type"; diff --git a/code/AssetLib/3MF/D3MFExporter.cpp b/code/AssetLib/3MF/D3MFExporter.cpp index 8fb9009d1..b4e5d515c 100644 --- a/code/AssetLib/3MF/D3MFExporter.cpp +++ b/code/AssetLib/3MF/D3MFExporter.cpp @@ -137,7 +137,7 @@ bool D3MFExporter::exportContentTypes() { mContentOutput << std::endl; mContentOutput << ""; mContentOutput << std::endl; - exportContentTyp(XmlTag::CONTENT_TYPES_ARCHIVE); + zipContentType(XmlTag::CONTENT_TYPES_ARCHIVE); return true; } @@ -162,7 +162,7 @@ bool D3MFExporter::exportRelations() { mRelOutput << ""; mRelOutput << std::endl; - writeRelInfoToFile("_rels", ".rels"); + zipRelInfo("_rels", ".rels"); mRelOutput.flush(); return true; @@ -196,7 +196,7 @@ bool D3MFExporter::export3DModel() { info->type = XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE; mRelations.push_back(info); - writeModelToArchive("3D", "3DModel.model"); + zipModel("3D", "3DModel.model"); mModelOutput.flush(); return true; @@ -357,42 +357,27 @@ void D3MFExporter::writeBuild() { mModelOutput << std::endl; } -void D3MFExporter::exportContentTyp(const std::string &filename) { - if (nullptr == m_zipArchive) { - throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr."); - } - const std::string entry = filename; - zip_entry_open(m_zipArchive, entry.c_str()); - - const std::string &exportTxt(mContentOutput.str()); - zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size()); - - zip_entry_close(m_zipArchive); +void D3MFExporter::zipContentType(const std::string &filename) { + addFileInZip(filename, mContentOutput.str()); } -void D3MFExporter::writeModelToArchive(const std::string &folder, const std::string &modelName) { - if (nullptr == m_zipArchive) { - throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr."); - } +void D3MFExporter::zipModel(const std::string &folder, const std::string &modelName) { const std::string entry = folder + "/" + modelName; - zip_entry_open(m_zipArchive, entry.c_str()); - - const std::string &exportTxt(mModelOutput.str()); - zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size()); - - zip_entry_close(m_zipArchive); + addFileInZip(entry, mModelOutput.str()); } -void D3MFExporter::writeRelInfoToFile(const std::string &folder, const std::string &relName) { +void D3MFExporter::zipRelInfo(const std::string &folder, const std::string &relName) { + const std::string entry = folder + "/" + relName; + addFileInZip(entry, mRelOutput.str()); +} + +void D3MFExporter::addFileInZip(const std::string& entry, const std::string& content) { if (nullptr == m_zipArchive) { throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr."); } - const std::string entry = folder + "/" + relName; + zip_entry_open(m_zipArchive, entry.c_str()); - - const std::string &exportTxt(mRelOutput.str()); - zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size()); - + zip_entry_write(m_zipArchive, content.c_str(), content.size()); zip_entry_close(m_zipArchive); } diff --git a/code/AssetLib/3MF/D3MFExporter.h b/code/AssetLib/3MF/D3MFExporter.h index 24ddc9bba..abde3cea3 100644 --- a/code/AssetLib/3MF/D3MFExporter.h +++ b/code/AssetLib/3MF/D3MFExporter.h @@ -82,9 +82,12 @@ protected: void writeVertex( const aiVector3D &pos ); void writeFaces( aiMesh *mesh, unsigned int matIdx ); void writeBuild(); - void exportContentTyp( const std::string &filename ); - void writeModelToArchive( const std::string &folder, const std::string &modelName ); - void writeRelInfoToFile( const std::string &folder, const std::string &relName ); + + // Zip the data + void zipContentType( const std::string &filename ); + void zipModel( const std::string &folder, const std::string &modelName ); + void zipRelInfo( const std::string &folder, const std::string &relName ); + void addFileInZip( const std::string &entry, const std::string &content ); private: std::string mArchiveName; diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index 089a680cf..66b2c965b 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -66,22 +66,79 @@ 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; + } +}; + +struct Component { + int mObjectId; + aiMatrix4x4 mTransformation; +}; + +class Object : public Resource { +public: + std::vector mMeshes; + std::vector mMeshIndex; + std::vector mComponents; + std::string mName; + + Object(int id) : + Resource(id), + mName (std::string("Object_") + to_string(id)){} + + virtual ResourceType getType() { + return ResourceType::RT_Object; + } +}; + + class XmlSerializer { public: - using MatArray = std::vector; - using MatId2MatArray = std::map>; XmlSerializer(XmlParser *xmlParser) : - mMeshes(), - mMatArray(), - mActiveMatGroup(99999999), - mMatId2MatArray(), + 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) { @@ -89,10 +146,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; @@ -101,9 +156,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) { @@ -111,10 +164,29 @@ 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 transformationMatrixStr; + aiMatrix4x4 transformationMatrix; + getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId); + bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr); + + auto it = mResourcesDictionnary.find(objectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it->second); + if (hasTransform) { + transformationMatrix = parseTransformMatrix(transformationMatrixStr); + } + + addObjectToNode(scene->mRootNode, obj, transformationMatrix); + } + } } + // import the metadata if (!mMetaData.empty()) { const size_t numMeta(mMetaData.size()); @@ -126,66 +198,177 @@ public: } // import the meshes - scene->mNumMeshes = static_cast(mMeshes.size()); - scene->mMeshes = new aiMesh *[scene->mNumMeshes](); - std::copy(mMeshes.begin(), mMeshes.end(), scene->mMeshes); + scene->mNumMeshes = static_cast(mMeshCount); + if (scene->mNumMeshes != 0) { + scene->mMeshes = new aiMesh *[scene->mNumMeshes](); + for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); it++) { + if (it->second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it->second); + for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { + scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; + } + } + } + } + // import the materials - scene->mNumMaterials = static_cast(mMatArray.size()); - if (0 != scene->mNumMaterials) { + scene->mNumMaterials = static_cast(mMaterialCount); + if (scene->mNumMaterials != 0) { scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; - std::copy(mMatArray.begin(), mMatArray.end(), scene->mMaterials); + 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: - aiNode *ReadObject(XmlNode &node, aiScene *scene) { - std::unique_ptr nodePtr(new aiNode()); - std::vector meshIds; + void addObjectToNode(aiNode* parent, Object* obj, aiMatrix4x4 nodeTransform) { + aiNode *sceneNode = new aiNode(obj->mName); + sceneNode->mNumMeshes = static_cast(obj->mMeshes.size()); + sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes]; + std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes); - std::string name, type; - pugi::xml_attribute attr = node.attribute(D3MF::XmlTag::id.c_str()); - if (!attr.empty()) { - name = attr.as_string(); + sceneNode->mTransformation = nodeTransform; + + parent->addChildren(1, &sceneNode); + + for (size_t i = 0; i < obj->mComponents.size(); ++i) { + Component c = obj->mComponents[i]; + auto it = mResourcesDictionnary.find(c.mObjectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); + } + } - attr = node.attribute(D3MF::XmlTag::type.c_str()); - if (!attr.empty()) { - type = attr.as_string(); + } + + bool getNodeAttribute(const XmlNode& node, const std::string& attribute, std::string& value) { + pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); + if (!objectAttribute.empty()) { + value = objectAttribute.as_string(); + return true; + } else { + return false; + } + } + + 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; + } + } + + aiMatrix4x4 parseTransformMatrix(std::string matrixStr) { + // split the string + std::vector numbers; + std::string currentNumber; + for (size_t i = 0; i < matrixStr.size(); ++i) { + const char c = matrixStr[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); } - nodePtr->mParent = scene->mRootNode; - nodePtr->mName.Set(name); + aiMatrix4x4 transformMatrix; + transformMatrix.a1 = numbers[0]; + transformMatrix.b1 = numbers[1]; + transformMatrix.c1 = numbers[2]; + transformMatrix.d1 = 0; - size_t meshIdx = mMeshes.size(); + 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; + return transformMatrix; + } + + 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; + } + + 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(name); - mMeshes.push_back(mesh); - meshIds.push_back(static_cast(meshIdx)); - ++meshIdx; + 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]; + } + } + + 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; + std::string componentTransformStr; + aiMatrix4x4 componentTransform; + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) { + componentTransform = parseTransformMatrix(componentTransformStr); + } + + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) + obj->mComponents.push_back({ objectId, componentTransform }); + } + } } } - 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,11 +424,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::p1.c_str()).as_string(); - if (nullptr != pidToken) { - int matIdx(std::atoi(pidToken)); - mesh->mMaterialIndex = matIdx; + 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 + } } } } @@ -270,26 +465,21 @@ 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); - const size_t newMatIdx(mMatArray.size()); - if (id != mActiveMatGroup) { - mActiveMatGroup = id; - MatId2MatArray::const_iterator it(mMatId2MatArray.find(id)); - if (mMatId2MatArray.end() == it) { - MatIdArray.clear(); - mMatId2MatArray[id] = MatIdArray; - } else { - MatIdArray = it->second; + 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) { + baseMaterials->mMaterialIndex.push_back(mMaterialCount); + baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id)); + mMaterialCount++; } } - MatIdArray.push_back(static_cast(newMatIdx)); - mMatId2MatArray[mActiveMatGroup] = MatIdArray; - } - mMatArray.push_back(readMaterialDef(node)); + mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); + } } bool parseColor(const char *color, aiColor4D &diffuse) { @@ -304,37 +494,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; } @@ -347,32 +524,30 @@ private: } } - aiMaterial *readMaterialDef(XmlNode &node) { - 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(mActiveMatGroup)); - stdMatName += "id"; - stdMatName += strId; - stdMatName += "_"; - if (nullptr != name) { - stdMatName += std::string(name); - } else { - stdMatName += "basemat"; - } - matName.Set(stdMatName); + aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId) { + 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: @@ -381,10 +556,8 @@ private: std::string value; }; std::vector mMetaData; - std::vector mMeshes; - MatArray mMatArray; - unsigned int mActiveMatGroup; - MatId2MatArray mMatId2MatArray; + std::map mResourcesDictionnary; + unsigned int mMaterialCount, mMeshCount; XmlParser *mXmlParser; }; diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 3d430a278..46bb57295 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -121,7 +121,6 @@ public: return false; } - bool result = false; const size_t len = stream->FileSize(); mData.resize(len + 1); memset(&mData[0], '\0', len + 1); @@ -130,11 +129,11 @@ public: mDoc = new pugi::xml_document(); pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full); if (parse_result.status == pugi::status_ok) { + return true; + } else { ASSIMP_LOG_DEBUG("Error while parse xml."); - result = true; + return false; } - - return result; } pugi::xml_document *getDocument() const {