From 4b1ff645e3d21c370ffd7447063263dfe1711cd1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 12 Aug 2021 21:13:07 +0200 Subject: [PATCH] closes https://github.com/assimp/assimp/issues/3398: Add support for embedded textures --- code/AssetLib/3DS/3DSConverter.cpp | 11 +- code/AssetLib/3DS/3DSLoader.h | 9 + code/AssetLib/3MF/3MFTypes.h | 125 +++++ code/AssetLib/3MF/3MFXmlTags.h | 10 +- code/AssetLib/3MF/D3MFImporter.cpp | 524 +-------------------- code/AssetLib/3MF/D3MFOpcPackage.cpp | 60 ++- code/AssetLib/3MF/D3MFOpcPackage.h | 12 +- code/AssetLib/3MF/XmlSerializer.cpp | 590 ++++++++++++++++++++++++ code/AssetLib/3MF/XmlSerializer.h | 104 +++++ code/AssetLib/Collada/ColladaLoader.cpp | 23 +- code/AssetLib/Collada/ColladaParser.cpp | 6 +- code/AssetLib/Obj/ObjFileImporter.cpp | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 5 +- code/CMakeLists.txt | 5 +- include/assimp/scene.h | 2 +- include/assimp/vector3.h | 33 +- tools/assimp_view/MaterialManager.h | 73 ++- tools/assimp_view/MeshRenderer.cpp | 9 +- 18 files changed, 1006 insertions(+), 597 deletions(-) create mode 100644 code/AssetLib/3MF/3MFTypes.h create mode 100644 code/AssetLib/3MF/XmlSerializer.cpp create mode 100644 code/AssetLib/3MF/XmlSerializer.h diff --git a/code/AssetLib/3DS/3DSConverter.cpp b/code/AssetLib/3DS/3DSConverter.cpp index aca16b0d6..add1553bc 100644 --- a/code/AssetLib/3DS/3DSConverter.cpp +++ b/code/AssetLib/3DS/3DSConverter.cpp @@ -68,8 +68,8 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() { unsigned int idx(NotSet); for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) { std::string s = mScene->mMaterials[i].mName; - for (std::string::iterator it = s.begin(); it != s.end(); ++it) { - *it = static_cast(::tolower(static_cast(*it))); + for (char & it : s) { + it = static_cast(::tolower(static_cast(it))); } if (std::string::npos == s.find("default")) continue; @@ -79,12 +79,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() { mScene->mMaterials[i].mDiffuse.r != mScene->mMaterials[i].mDiffuse.b) continue; - if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || - mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || - mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || - mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || - mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || - mScene->mMaterials[i].sTexShininess.mMapName.length() != 0) { + if (ContainsTextures(i)) { continue; } idx = i; diff --git a/code/AssetLib/3DS/3DSLoader.h b/code/AssetLib/3DS/3DSLoader.h index 2091fbeb7..dba20eede 100644 --- a/code/AssetLib/3DS/3DSLoader.h +++ b/code/AssetLib/3DS/3DSLoader.h @@ -208,6 +208,15 @@ protected: */ void ReplaceDefaultMaterial(); + bool ContainsTextures(unsigned int i) const { + return mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || + mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || + mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || + mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || + mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || + mScene->mMaterials[i].sTexShininess.mMapName.length() != 0; + } + // ------------------------------------------------------------------- /** Convert the whole scene */ diff --git a/code/AssetLib/3MF/3MFTypes.h b/code/AssetLib/3MF/3MFTypes.h new file mode 100644 index 000000000..e404bf012 --- /dev/null +++ b/code/AssetLib/3MF/3MFTypes.h @@ -0,0 +1,125 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct aiMaterial; +struct aiMesh; + +namespace Assimp { +namespace D3MF { + +enum class ResourceType { + RT_Object, + RT_BaseMaterials, + RT_EmbeddedTexture2D, + RT_Texture2DGroup, + RT_Unknown +}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...) + +class Resource { +public: + int mId; + + Resource(int id) : + mId(id) { + // empty + } + + virtual ~Resource() { + // empty + } + + virtual ResourceType getType() const { + return ResourceType::RT_Unknown; + } +}; + +class EmbeddedTexture : public Resource { +public: + std::string mPath; + std::string mContentType; + std::string mTilestyleU; + std::string mTilestyleV; + std::vector mBuffer; + + EmbeddedTexture(int id) : + Resource(id), + mPath(), + mContentType(), + mTilestyleU(), + mTilestyleV() { + // empty + } + + ~EmbeddedTexture() = default; + + ResourceType getType() const override { + return ResourceType::RT_EmbeddedTexture2D; + } +}; + +class Texture2DGroup : public Resource { +public: + std::vector mTex2dCoords; + int mTexId; + Texture2DGroup(int id) : + Resource(id), + mTexId(-1) { + // empty + } + + ~Texture2DGroup() = default; + + ResourceType getType() const override { + return ResourceType::RT_Texture2DGroup; + } +}; + +class BaseMaterials : public Resource { +public: + std::vector mMaterialIndex; + + BaseMaterials(int id) : + Resource(id), + mMaterialIndex() { + // empty + } + + ~BaseMaterials() = default; + + ResourceType getType() const override { + 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_") + ai_to_string(id)) { + // empty + } + + ~Object() = default; + + ResourceType getType() const override { + return ResourceType::RT_Object; + } +}; + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index d447556d6..28b198f5d 100644 --- a/code/AssetLib/3MF/3MFXmlTags.h +++ b/code/AssetLib/3MF/3MFXmlTags.h @@ -80,13 +80,21 @@ namespace XmlTag { const char* const item = "item"; const char* const objectid = "objectid"; const char* const transform = "transform"; + const char *const path = "path"; // Material definitions const char* const basematerials = "basematerials"; - const char* const basematerials_id = "id"; const char* const basematerials_base = "base"; const char* const basematerials_name = "name"; const char* const basematerials_displaycolor = "displaycolor"; + const char* const texture_2d = "m:texture2d"; + const char *const texture_group = "m:texture2dgroup"; + const char *const texture_content_type = "contenttype"; + const char *const texture_tilestyleu = "tilestyleu"; + const char *const texture_tilestylev = "tilestylev"; + const char *const texture_2d_coord = "m:tex2coord"; + const char *const texture_cuurd_u = "u"; + const char *const texture_cuurd_v = "v"; // Meta info tags const char* const CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index 747af7cfc..fe3c07744 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "D3MFImporter.h" #include "3MFXmlTags.h" #include "D3MFOpcPackage.h" +#include "XmlSerializer.h" #include #include @@ -61,513 +62,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include +#include 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: - int mId; - - Resource(int id) : - mId(id) { - // empty - } - - virtual ~Resource() { - // empty - } - - virtual ResourceType getType() const { - return ResourceType::RT_Unknown; - } -}; - -class BaseMaterials : public Resource { -public: - std::vector mMaterials; - std::vector mMaterialIndex; - - BaseMaterials(int id) : - Resource(id), - mMaterials(), - mMaterialIndex() { - // empty - } - - ~BaseMaterials() = default; - - ResourceType getType() const override { - 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_") + ai_to_string(id)) { - // empty - } - - ~Object() = default; - - ResourceType getType() const override { - return ResourceType::RT_Object; - } -}; - -class XmlSerializer { -public: - XmlSerializer(XmlParser *xmlParser) : - mResourcesDictionnary(), - mMaterialCount(0), - mMeshCount(0), - mXmlParser(xmlParser) { - // empty - } - - ~XmlSerializer() { - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it ) { - delete it->second; - } - } - - void ImportXml(aiScene *scene) { - if (nullptr == scene) { - return; - } - - scene->mRootNode = new aiNode(XmlTag::RootTag); - - XmlNode node = mXmlParser->getRootNode().child(XmlTag::model); - if (node.empty()) { - return; - } - XmlNode resNode = node.child(XmlTag::resources); - for (auto ¤tNode : resNode.children()) { - const std::string currentNodeName = currentNode.name(); - if (currentNodeName == XmlTag::object) { - ReadObject(currentNode); - } else if (currentNodeName == XmlTag::basematerials) { - ReadBaseMaterials(currentNode); - } else if (currentNodeName == XmlTag::meta) { - ReadMetadata(currentNode); - } - } - - XmlNode buildNode = node.child(XmlTag::build); - for (auto ¤tNode : buildNode.children()) { - const std::string currentNodeName = currentNode.name(); - if (currentNodeName == 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(); - scene->mMetaData = aiMetadata::Alloc(static_cast(numMeta)); - for (size_t i = 0; i < numMeta; ++i) { - aiString val(mMetaData[i].value); - scene->mMetaData->Set(static_cast(i), mMetaData[i].name, val); - } - } - - // import the meshes - 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); - ai_assert(nullptr != obj); - for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { - scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; - } - } - } - } - - // import the materials - scene->mNumMaterials = mMaterialCount; - if (scene->mNumMaterials != 0) { - scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; - 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]; - } - } - } - } - } - -private: - void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) { - ai_assert(nullptr != obj); - - 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); - - sceneNode->mTransformation = nodeTransform; - if (nullptr != parent) { - 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); - } - } - } - - 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; - } - - 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; - } - - 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) { - const 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; - - return transformMatrix; - } - - void ReadObject(XmlNode &node) { - int id = -1, pid = -1, pindex = -1; - bool hasId = getNodeAttribute(node, XmlTag::id, id); - bool hasPid = getNodeAttribute(node, XmlTag::pid, pid); - bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex); - if (!hasId) { - return; - } - - Object *obj = new Object(id); - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == D3MF::XmlTag::mesh) { - auto mesh = ReadMesh(currentNode); - mesh->mName.Set(ai_to_string(id)); - - 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 ¤tSubNode : currentNode.children()) { - const std::string subNodeName = currentSubNode.name(); - if (subNodeName == 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 }); - } - } - } - } - } - - mResourcesDictionnary.insert(std::make_pair(id, obj)); - } - - aiMesh *ReadMesh(XmlNode &node) { - aiMesh *mesh = new aiMesh(); - - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::vertices) { - ImportVertices(currentNode, mesh); - } else if (currentName == XmlTag::triangles) { - ImportTriangles(currentNode, mesh); - } - } - - return mesh; - } - - void ReadMetadata(XmlNode &node) { - pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name); - const std::string name = attribute.as_string(); - const std::string value = node.value(); - if (name.empty()) { - return; - } - - MetaEntry entry; - entry.name = name; - entry.value = value; - mMetaData.push_back(entry); - } - - void ImportVertices(XmlNode &node, aiMesh *mesh) { - std::vector vertices; - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::vertex) { - vertices.push_back(ReadVertex(currentNode)); - } - } - - mesh->mNumVertices = static_cast(vertices.size()); - mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - std::copy(vertices.begin(), vertices.end(), mesh->mVertices); - } - - aiVector3D ReadVertex(XmlNode &node) { - aiVector3D vertex; - vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); - vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); - vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); - - return vertex; - } - - void ImportTriangles(XmlNode &node, aiMesh *mesh) { - std::vector faces; - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::triangle) { - aiFace face = ReadTriangle(currentNode); - faces.push_back(face); - - int pid = 0, p1 = 0; - 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 - } - } - } - } - - mesh->mNumFaces = static_cast(faces.size()); - mesh->mFaces = new aiFace[mesh->mNumFaces]; - mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - std::copy(faces.begin(), faces.end(), mesh->mFaces); - } - - aiFace ReadTriangle(XmlNode &node) { - aiFace face; - - face.mNumIndices = 3; - face.mIndices = new unsigned int[face.mNumIndices]; - face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); - face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); - face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); - - return face; - } - - void ReadBaseMaterials(XmlNode &node) { - int id = -1; - if (getNodeAttribute(node, D3MF::XmlTag::basematerials_id, id)) { - BaseMaterials *baseMaterials = new BaseMaterials(id); - - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::basematerials_base) { - baseMaterials->mMaterialIndex.push_back(mMaterialCount); - baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id)); - ++mMaterialCount; - } - } - - mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); - } - } - - bool parseColor(const char *color, aiColor4D &diffuse) { - if (nullptr == color) { - return false; - } - - //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) - const size_t len = strlen(color); - if (9 != len && 7 != len) { - return false; - } - - const char *buf(color); - if ('#' != buf[0]) { - return false; - } - - char r[3] = { buf[1], buf[2], '\0' }; - diffuse.r = static_cast(strtol(r, 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); - - 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; - - char a[3] = { buf[7], buf[8], '\0' }; - diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); - - return true; - } - - void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { - const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); - aiColor4D diffuse; - if (parseColor(color, diffuse)) { - mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - } - } - - 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); - - std::string stdMaterialName; - const std::string strId(ai_to_string(basematerialsId)); - stdMaterialName += "id"; - stdMaterialName += strId; - stdMaterialName += "_"; - if (hasName) { - stdMaterialName += std::string(name); - } else { - stdMaterialName += "basemat_"; - stdMaterialName += ai_to_string(mMaterialCount - basematerialsId); - } - - aiString assimpMaterialName(stdMaterialName); - material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME); - - assignDiffuseColor(node, material); - - return material; - } - -private: - struct MetaEntry { - std::string name; - std::string value; - }; - std::vector mMetaData; - std::map mResourcesDictionnary; - unsigned int mMaterialCount, mMeshCount; - XmlParser *mXmlParser; -}; - -} //namespace D3MF using namespace D3MF; @@ -597,7 +94,9 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo const std::string extension(GetExtension(filename)); if (extension == desc.mFileExtensions) { return true; - } else if (!extension.length() || checkSig) { + } + + if (!extension.length() || checkSig) { if (nullptr == pIOHandler) { return false; } @@ -611,7 +110,7 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo return false; } -void D3MFImporter::SetupProperties(const Importer * /*pImp*/) { +void D3MFImporter::SetupProperties(const Importer*) { // empty } @@ -624,8 +123,17 @@ void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, XmlParser xmlParser; if (xmlParser.parse(opcPackage.RootStream())) { - XmlSerializer xmlSerializer(&xmlParser); + XmlSerializer xmlSerializer(&xmlParser, &opcPackage); xmlSerializer.ImportXml(pScene); + + const std::vector &tex = opcPackage.GetEmbeddedTextures(); + if (!tex.empty()) { + pScene->mNumTextures = static_cast(tex.size()); + pScene->mTextures = new aiTexture *[pScene->mNumTextures]; + for (unsigned int i = 0; i < pScene->mNumTextures; ++i) { + pScene->mTextures[i] = tex[i]; + } + } } } diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index dbf4f2e10..d3b667583 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -43,14 +43,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "D3MFOpcPackage.h" #include - #include #include #include #include #include #include - +#include #include "3MFXmlTags.h" #include #include @@ -64,11 +63,12 @@ namespace Assimp { namespace D3MF { // ------------------------------------------------------------------------------------------------ -typedef std::shared_ptr OpcPackageRelationshipPtr; +using OpcPackageRelationshipPtr = std::shared_ptr; class OpcPackageRelationshipReader { public: - OpcPackageRelationshipReader(XmlParser &parser) { + OpcPackageRelationshipReader(XmlParser &parser) : + m_relationShips() { XmlNode root = parser.getRootNode(); ParseRootNode(root); } @@ -91,6 +91,7 @@ public: if (relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty()) { return false; } + return true; } @@ -100,7 +101,7 @@ public: } for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { - std::string name = currentNode.name(); + const std::string name = currentNode.name(); if (name == "Relationship") { OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship()); relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID).as_string(); @@ -116,11 +117,20 @@ public: std::vector m_relationShips; }; +static bool IsEmbeddedTexture( const std::string &filename ) { + const std::string extension = BaseImporter::GetExtension(filename); + + if (extension == "jpg" || extension == "png") { + return true; + } + + return false; +} // ------------------------------------------------------------------------------------------------ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : mRootStream(nullptr), mZipArchive() { - mZipArchive.reset(new ZipArchiveIOSystem(pIOHandler, rFile)); + mZipArchive = std::make_unique(pIOHandler, rFile); if (!mZipArchive->isOpen()) { throw DeadlyImportError("Failed to open file ", rFile, "."); } @@ -141,13 +151,13 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : } std::string rootFile = ReadPackageRootRelationship(fileStream); - if (rootFile.size() > 0 && rootFile[0] == '/') { + if (!rootFile.empty() && rootFile[0] == '/') { rootFile = rootFile.substr(1); if (rootFile[0] == '/') { // deal with zip-bug rootFile = rootFile.substr(1); } - } + } ASSIMP_LOG_VERBOSE_DEBUG(rootFile); @@ -158,9 +168,12 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : if (nullptr == mRootStream) { throw DeadlyImportError("Cannot open root-file in archive : " + rootFile); } - } else if (file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) { ASSIMP_LOG_WARN("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES", file); + } else if (IsEmbeddedTexture(file)) { + IOStream *fileStream = mZipArchive->Open(file.c_str()); + LoadEmbeddedTextures(fileStream, file); + mZipArchive->Close(fileStream); } else { ASSIMP_LOG_WARN("Ignored file of unknown type: ", file); } @@ -169,20 +182,25 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : D3MFOpcPackage::~D3MFOpcPackage() { mZipArchive->Close(mRootStream); + mZipArchive = nullptr; } IOStream *D3MFOpcPackage::RootStream() const { return mRootStream; } -static const std::string ModelRef = "3D/3dmodel.model"; +const std::vector &D3MFOpcPackage::GetEmbeddedTextures() const { + return mEmbeddedTextures; +} + +static const char *const ModelRef = "3D/3dmodel.model"; bool D3MFOpcPackage::validate() { if (nullptr == mRootStream || nullptr == mZipArchive) { return false; } - return mZipArchive->Exists(ModelRef.c_str()); + return mZipArchive->Exists(ModelRef); } std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { @@ -204,6 +222,26 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { return (*itr)->target; } +void D3MFOpcPackage::LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename) { + if (nullptr == fileStream) { + return; + } + + const size_t size = fileStream->FileSize(); + if (0 == size) { + return; + } + + char *data = new char[size]; + fileStream->Read(data, 1, size); + aiTexture *texture = new aiTexture; + texture->mFilename.Set(filename.c_str()); + texture->mWidth = static_cast(size); + texture->mHeight = 0; + texture->pcData = (aiTexel*) data; + mEmbeddedTextures.emplace_back(texture); +} + } // Namespace D3MF } // Namespace Assimp diff --git a/code/AssetLib/3MF/D3MFOpcPackage.h b/code/AssetLib/3MF/D3MFOpcPackage.h index 22b4510d0..93928b9a7 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.h +++ b/code/AssetLib/3MF/D3MFOpcPackage.h @@ -46,8 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +struct aiTexture; + namespace Assimp { - class ZipArchiveIOSystem; + +class ZipArchiveIOSystem; namespace D3MF { @@ -63,16 +66,19 @@ public: ~D3MFOpcPackage(); IOStream* RootStream() const; bool validate(); + const std::vector &GetEmbeddedTextures() const; protected: std::string ReadPackageRootRelationship(IOStream* stream); + void LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename); private: IOStream* mRootStream; std::unique_ptr mZipArchive; + std::vector mEmbeddedTextures; }; -} // Namespace D3MF -} // Namespace Assimp +} // namespace D3MF +} // namespace Assimp #endif // D3MFOPCPACKAGE_H diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp new file mode 100644 index 000000000..d93c7b121 --- /dev/null +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -0,0 +1,590 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2021, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#include "XmlSerializer.h" +#include "D3MFOpcPackage.h" +#include "3MFXmlTags.h" +#include "3MFTypes.h" +#include + +namespace Assimp { +namespace D3MF { + +static const int IdNotSet = -1; + +XmlSerializer::XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive) : + mResourcesDictionnary(), + mMeshCount(0), + mXmlParser(xmlParser), + mD3MFOpcPackage(archive) { + ai_assert(nullptr != xmlParser); + ai_assert(nullptr != archive); +} + +XmlSerializer::~XmlSerializer() { + for (auto &it : mResourcesDictionnary) { + delete it.second; + } +} + +void XmlSerializer::ImportXml(aiScene *scene) { + if (nullptr == scene) { + return; + } + + scene->mRootNode = new aiNode(XmlTag::RootTag); + XmlNode node = mXmlParser->getRootNode().child(XmlTag::model); + if (node.empty()) { + return; + } + + XmlNode resNode = node.child(XmlTag::resources); + for (auto ¤tNode : resNode.children()) { + const std::string currentNodeName = currentNode.name(); + if (currentNodeName == XmlTag::texture_2d) { + ReadEmbeddecTexture(currentNode); + } else if (currentNodeName == XmlTag::texture_group) { + ReadTextureGroup(currentNode); + } else if (currentNodeName == XmlTag::object) { + ReadObject(currentNode); + } else if (currentNodeName == XmlTag::basematerials) { + ReadBaseMaterials(currentNode); + } else if (currentNodeName == XmlTag::meta) { + ReadMetadata(currentNode); + } + } + StoreMaterialsInScene(scene); + XmlNode buildNode = node.child(XmlTag::build); + if (buildNode.empty()) { + return; + } + + for (auto ¤tNode : buildNode.children()) { + const std::string currentNodeName = currentNode.name(); + if (currentNodeName == XmlTag::item) { + int objectId = IdNotSet; + 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(); + scene->mMetaData = aiMetadata::Alloc(static_cast(numMeta)); + for (size_t i = 0; i < numMeta; ++i) { + aiString val(mMetaData[i].value); + scene->mMetaData->Set(static_cast(i), mMetaData[i].name, val); + } + } + + // import the meshes, materials are already stored + scene->mNumMeshes = static_cast(mMeshCount); + if (scene->mNumMeshes != 0) { + scene->mMeshes = new aiMesh *[scene->mNumMeshes](); + for (auto &it : mResourcesDictionnary) { + if (it.second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it.second); + ai_assert(nullptr != obj); + for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { + scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; + } + } + } + } +} + +void XmlSerializer::addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) { + ai_assert(nullptr != obj); + + 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); + + sceneNode->mTransformation = nodeTransform; + if (nullptr != parent) { + parent->addChildren(1, &sceneNode); + } + + for (Assimp::D3MF::Component c : obj->mComponents) { + auto it = mResourcesDictionnary.find(c.mObjectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); + } + } +} + +bool XmlSerializer::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; + } + + return false; +} + +bool XmlSerializer::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; + } + + return false; +} + +aiMatrix4x4 XmlSerializer::parseTransformMatrix(std::string matrixStr) { + // split the string + std::vector numbers; + std::string currentNumber; + for (char c : matrixStr) { + if (c == ' ') { + if (!currentNumber.empty()) { + float f = std::stof(currentNumber); + numbers.push_back(f); + currentNumber.clear(); + } + } else { + currentNumber.push_back(c); + } + } + if (!currentNumber.empty()) { + const 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; + + return transformMatrix; +} + +void XmlSerializer::ReadObject(XmlNode &node) { + int id = IdNotSet, pid = IdNotSet, pindex = IdNotSet; + bool hasId = getNodeAttribute(node, XmlTag::id, id); + if (!hasId) { + return; + } + + bool hasPid = getNodeAttribute(node, XmlTag::pid, pid); + bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex); + + Object *obj = new Object(id); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == D3MF::XmlTag::mesh) { + auto mesh = ReadMesh(currentNode); + mesh->mName.Set(ai_to_string(id)); + + 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 ¤tSubNode : currentNode.children()) { + const std::string subNodeName = currentSubNode.name(); + if (subNodeName == D3MF::XmlTag::component) { + int objectId = IdNotSet; + 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 }); + } + } + } + } + } + + mResourcesDictionnary.insert(std::make_pair(id, obj)); +} + +aiMesh *XmlSerializer::ReadMesh(XmlNode &node) { + if (node.empty()) { + return nullptr; + } + + aiMesh *mesh = new aiMesh(); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::vertices) { + ImportVertices(currentNode, mesh); + } else if (currentName == XmlTag::triangles) { + ImportTriangles(currentNode, mesh); + } + } + + return mesh; +} + +void XmlSerializer::ReadMetadata(XmlNode &node) { + pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name); + const std::string name = attribute.as_string(); + const std::string value = node.value(); + if (name.empty()) { + return; + } + + MetaEntry entry; + entry.name = name; + entry.value = value; + mMetaData.push_back(entry); +} + +void XmlSerializer::ImportVertices(XmlNode &node, aiMesh *mesh) { + ai_assert(nullptr != mesh); + + std::vector vertices; + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::vertex) { + vertices.push_back(ReadVertex(currentNode)); + } + } + + mesh->mNumVertices = static_cast(vertices.size()); + mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + std::copy(vertices.begin(), vertices.end(), mesh->mVertices); +} + +aiVector3D XmlSerializer::ReadVertex(XmlNode &node) { + aiVector3D vertex; + vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); + vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); + vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); + + return vertex; +} + +void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { + std::vector faces; + + const size_t numTriangles = std::distance(node.children(XmlTag::triangle).begin(), node.children(XmlTag::triangle).end()); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::triangle) { + int pid = IdNotSet, p1 = IdNotSet; + 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]; + } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) { + if (mesh->mTextureCoords[0] == nullptr) { + Texture2DGroup *group = static_cast(it->second); + const std::string name = ai_to_string(group->mTexId); + for (size_t i = 0; i < mMaterials.size(); ++i) { + if (name == mMaterials[i]->GetName().C_Str()) { + mesh->mMaterialIndex = static_cast(i); + } + } + mesh->mTextureCoords[0] = new aiVector3D[group->mTex2dCoords.size()]; + for (unsigned int i = 0; i < group->mTex2dCoords.size(); ++i) { + mesh->mTextureCoords[0][i] = aiVector3D(group->mTex2dCoords[i].x, group->mTex2dCoords[i].y, 0); + } + } + } + } + } + + aiFace face = ReadTriangle(currentNode); + faces.push_back(face); + } + } + + mesh->mNumFaces = static_cast(faces.size()); + mesh->mFaces = new aiFace[mesh->mNumFaces]; + mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + std::copy(faces.begin(), faces.end(), mesh->mFaces); +} + +aiFace XmlSerializer::ReadTriangle(XmlNode &node) { + aiFace face; + + face.mNumIndices = 3; + face.mIndices = new unsigned int[face.mNumIndices]; + face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); + face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); + face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); + + return face; +} + +void XmlSerializer::ReadBaseMaterials(XmlNode &node) { + int id = IdNotSet; + if (getNodeAttribute(node, D3MF::XmlTag::id, id)) { + BaseMaterials *baseMaterials = new BaseMaterials(id); + + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::basematerials_base) { + baseMaterials->mMaterialIndex.push_back(static_cast(mMaterials.size())); + mMaterials.push_back(readMaterialDef(currentNode, id)); + } + } + + mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); + } +} + +static const size_t ColRGBA_Len = 9; +static const size_t ColRGB_Len = 7; + +// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) +static bool validateColorString(const char *color) { + const size_t len = strlen(color); + if (ColRGBA_Len != len && ColRGB_Len != len) { + return false; + } + + return true; +} + +bool XmlSerializer::parseColor(const char *color, aiColor4D &diffuse) { + if (nullptr == color) { + return false; + } + + if (!validateColorString(color)) { + return false; + } + + //const char *buf(color); + if ('#' != color[0]) { + return false; + } + + char r[3] = { color[1], color[2], '\0' }; + diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); + + char g[3] = { color[3], color[4], '\0' }; + diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); + + char b[3] = { color[5], color[6], '\0' }; + diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); + const size_t len = strlen(color); + if (ColRGB_Len == len) { + return true; + } + + char a[3] = { color[7], color[8], '\0' }; + diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); + + return true; +} + +void XmlSerializer::ReadEmbeddecTexture(XmlNode &node) { + if (node.empty()) { + return; + } + + std::string value; + EmbeddedTexture *tex2D = nullptr; + if (XmlParser::getStdStrAttribute(node, XmlTag::id, value)) { + tex2D = new EmbeddedTexture(atoi(value.c_str())); + } + if (nullptr == tex2D) { + return; + } + + if (XmlParser::getStdStrAttribute(node, XmlTag::path, value)) { + tex2D->mPath = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_content_type, value)) { + tex2D->mContentType = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestyleu, value)) { + tex2D->mTilestyleU = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestylev, value)) { + tex2D->mTilestyleV = value; + } + mEmbeddedTextures.emplace_back(tex2D); + StoreEmbeddedTexture(tex2D); +} + +void XmlSerializer::StoreEmbeddedTexture(EmbeddedTexture *tex) { + aiMaterial *mat = new aiMaterial; + aiString s; + s.Set(ai_to_string(tex->mId).c_str()); + mat->AddProperty(&s, AI_MATKEY_NAME); + s.Set(tex->mPath); + mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0)); + + aiColor3D col; + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_DIFFUSE); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_AMBIENT); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_EMISSIVE); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_SPECULAR); + mMaterials.emplace_back(mat); +} + +void XmlSerializer::ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup) { + if (node.empty() || nullptr == tex2DGroup) { + return; + } + int id = IdNotSet; + if (XmlParser::getIntAttribute(node, "texid", id)) { + tex2DGroup->mTexId = id; + } + double value; + for (XmlNode currentNode : node.children()) { + const std::string currentName = currentNode.name(); + aiVector2D texCoord; + if (currentName == XmlTag::texture_2d_coord) { + XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_u, value); + texCoord.x = (ai_real)value; + XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_v, value); + texCoord.y = (ai_real)value; + tex2DGroup->mTex2dCoords.push_back(texCoord); + } + } +} + +void XmlSerializer::ReadTextureGroup(XmlNode &node) { + if (node.empty()) { + return; + } + + int id = IdNotSet; + if (!XmlParser::getIntAttribute(node, XmlTag::id, id)) { + return; + } + + Texture2DGroup *group = new Texture2DGroup(id); + ReadTextureCoords2D(node, group); + mResourcesDictionnary.insert(std::make_pair(id, group)); +} + +void XmlSerializer::assignDiffuseColor(XmlNode &node, aiMaterial *mat) { + const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); + aiColor4D diffuse; + if (parseColor(color, diffuse)) { + mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + } +} + +aiMaterial *XmlSerializer::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); + + std::string stdMaterialName; + const std::string strId(ai_to_string(basematerialsId)); + stdMaterialName += "id"; + stdMaterialName += strId; + stdMaterialName += "_"; + if (hasName) { + stdMaterialName += std::string(name); + } else { + stdMaterialName += "basemat_"; + stdMaterialName += ai_to_string(mMaterials.size()); + } + + aiString assimpMaterialName(stdMaterialName); + material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME); + + assignDiffuseColor(node, material); + + return material; +} + +void XmlSerializer::StoreMaterialsInScene( aiScene *scene ) { + if (nullptr == scene || mMaterials.empty()) { + return; + } + + scene->mNumMaterials = static_cast(mMaterials.size()); + scene->mMaterials = new aiMaterial*[scene->mNumMaterials]; + for (size_t i = 0; i < mMaterials.size(); ++i) { + scene->mMaterials[i] = mMaterials[i]; + } +} +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/3MF/XmlSerializer.h b/code/AssetLib/3MF/XmlSerializer.h new file mode 100644 index 000000000..40300d70d --- /dev/null +++ b/code/AssetLib/3MF/XmlSerializer.h @@ -0,0 +1,104 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2021, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include +#include + +struct aiNode; +struct aiMesh; +struct aiMaterial; + +namespace Assimp { +namespace D3MF { + +class Resource; +class D3MFOpcPackage; +class Object; +class Texture2DGroup; +class EmbeddedTexture; + +class XmlSerializer { +public: + XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive); + ~XmlSerializer(); + void ImportXml(aiScene *scene); + +private: + void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform); + bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value); + bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value); + aiMatrix4x4 parseTransformMatrix(std::string matrixStr); + void ReadObject(XmlNode &node); + aiMesh *ReadMesh(XmlNode &node); + void ReadMetadata(XmlNode &node); + void ImportVertices(XmlNode &node, aiMesh *mesh); + aiVector3D ReadVertex(XmlNode &node); + void ImportTriangles(XmlNode &node, aiMesh *mesh); + aiFace ReadTriangle(XmlNode &node); + void ReadBaseMaterials(XmlNode &node); + bool parseColor(const char *color, aiColor4D &diffuse); + void ReadEmbeddecTexture(XmlNode &node); + void StoreEmbeddedTexture(EmbeddedTexture *tex); + void ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup); + void ReadTextureGroup(XmlNode &node); + void assignDiffuseColor(XmlNode &node, aiMaterial *mat); + aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId); + void StoreMaterialsInScene(aiScene *scene); + +private: + struct MetaEntry { + std::string name; + std::string value; + }; + std::vector mMetaData; + std::vector mEmbeddedTextures; + std::vector mMaterials; + std::map mResourcesDictionnary; + unsigned int mMeshCount; + XmlParser *mXmlParser; + D3MFOpcPackage *mD3MFOpcPackage; +}; + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 492e6971f..876a6fd1d 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -135,14 +135,15 @@ bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool // XML - too generic, we need to open the file and search for typical keywords if (extension == "xml" || !extension.length() || checkSig) { - /* If CanRead() is called in order to check whether we - * support a specific file extension in general pIOHandler - * might be nullptr and it's our duty to return true here. - */ - if (!pIOHandler) { + // If CanRead() is called in order to check whether we + // support a specific file extension in general pIOHandler + // might be nullptr and it's our duty to return true here. + if (nullptr == pIOHandler) { return true; } - static const char *tokens[] = { "mNumMeshes = static_cast(newMeshRefs.size()); - if (newMeshRefs.size()) { + if (!newMeshRefs.empty()) { struct UIntTypeConverter { unsigned int operator()(const size_t &v) const { return static_cast(v); @@ -1538,9 +1539,9 @@ void ColladaLoader::AddTexture(aiMaterial &mat, map = sampler.mUVId; } else { map = -1; - for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) { - if (IsNumeric(*it)) { - map = strtoul10(&(*it)); + for (char it : sampler.mUVChannel) { + if (IsNumeric(it)) { + map = strtoul10(&it); break; } } @@ -1682,7 +1683,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) // store the material mMaterialIndexByName[matIt->first] = newMats.size(); - newMats.push_back(std::pair(&effect, mat)); + newMats.emplace_back(&effect, mat); } // ScenePreprocessor generates a default material automatically if none is there. // All further code here in this loader works well without a valid material so diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 37c7274f4..bc5f951ab 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -918,7 +918,7 @@ void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) { if (currentName == "instance_effect") { std::string url; readUrlAttribute(currentNode, url); - pMaterial.mEffect = url.c_str(); + pMaterial.mEffect = url; } } } @@ -2208,8 +2208,8 @@ void ColladaParser::ReadMaterialVertexInputBinding(XmlNode &node, Collada::Seman void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive) { // Attempt to load any undefined Collada::Image in ImageLibrary - for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) { - Collada::Image &image = (*it).second; + for (auto & it : mImageLibrary) { + Collada::Image &image = it.second; if (image.mImageData.empty()) { std::unique_ptr image_file(zip_archive.Open(image.mFileName.c_str())); diff --git a/code/AssetLib/Obj/ObjFileImporter.cpp b/code/AssetLib/Obj/ObjFileImporter.cpp index d6232be81..e45533c30 100644 --- a/code/AssetLib/Obj/ObjFileImporter.cpp +++ b/code/AssetLib/Obj/ObjFileImporter.cpp @@ -162,7 +162,7 @@ void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, I // ------------------------------------------------------------------------------------------------ // Create the data from parsed obj-file void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene *pScene) { - if (0L == pModel) { + if (nullptr == pModel) { return; } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index d80df97d2..08a1ea0ed 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1470,10 +1470,11 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { } } - if (numEmbeddedTexs == 0) + if (numEmbeddedTexs == 0) { return; + } - ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); + ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); mScene->mTextures = new aiTexture *[numEmbeddedTexs]; std::fill(mScene->mTextures, mScene->mTextures + numEmbeddedTexs, nullptr); diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index fd92d41e3..130ab8870 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -820,7 +820,10 @@ ADD_ASSIMP_IMPORTER( GLTF AssetLib/glTF2/glTF2Importer.h ) -ADD_ASSIMP_IMPORTER( 3MF +ADD_ASSIMP_IMPORTER(3MF + AssetLib/3MF/3MFTypes.h + AssetLib/3MF/XmlSerializer.h + AssetLib/3MF/XmlSerializer.cpp AssetLib/3MF/D3MFImporter.h AssetLib/3MF/D3MFImporter.cpp AssetLib/3MF/D3MFOpcPackage.h diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 522ddc6dc..b54c29dfa 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -400,7 +400,7 @@ struct aiScene //! Returns an embedded texture and its index std::pair GetEmbeddedTextureAndIndex(const char* filename) const { - if(nullptr==filename) { + if (nullptr==filename) { return std::make_pair(nullptr, -1); } // lookup using texture ID (if referenced like: "*1", "*2", etc.) diff --git a/include/assimp/vector3.h b/include/assimp/vector3.h index b084cf9c3..e3ad0b680 100644 --- a/include/assimp/vector3.h +++ b/include/assimp/vector3.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2021, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -65,27 +63,49 @@ template class aiMatrix3x3t; template class aiMatrix4x4t; // --------------------------------------------------------------------------- -/** Represents a three-dimensional vector. */ +/// @brief Represents a three-dimensional vector. +// --------------------------------------------------------------------------- template class aiVector3t { public: + /// @brief The default class constructor. aiVector3t() AI_NO_EXCEPT : x(), y(), z() {} + + /// @brief The class constructor with the components. + /// @param _x The x-component for the vector. + /// @param _y The y-component for the vector. + /// @param _z The z-component for the vector. aiVector3t(TReal _x, TReal _y, TReal _z) : x(_x), y(_y), z(_z) {} + + /// @brief The class constructor with a default value. + /// @param _xyz The value for x, y and z. explicit aiVector3t (TReal _xyz ) : x(_xyz), y(_xyz), z(_xyz) {} + + /// @brief The copy constructor. + /// @param o The instance to copy from. aiVector3t( const aiVector3t& o ) = default; - // combined operators + /// @brief combined operators + /// @brief The copy constructor. const aiVector3t& operator += (const aiVector3t& o); + + /// @brief The copy constructor. const aiVector3t& operator -= (const aiVector3t& o); + + /// @brief The copy constructor. const aiVector3t& operator *= (TReal f); + + /// @brief The copy constructor. const aiVector3t& operator /= (TReal f); - // transform vector by matrix + /// @brief Transform vector by matrix aiVector3t& operator *= (const aiMatrix3x3t& mat); aiVector3t& operator *= (const aiMatrix4x4t& mat); - // access a single element + /// @brief access a single element, const. TReal operator[](unsigned int i) const; + + /// @brief access a single element, non-const. TReal& operator[](unsigned int i); // comparison @@ -93,6 +113,7 @@ public: bool operator!= (const aiVector3t& other) const; bool operator < (const aiVector3t& other) const; + /// @brief bool Equal(const aiVector3t& other, TReal epsilon = 1e-6) const; template diff --git a/tools/assimp_view/MaterialManager.h b/tools/assimp_view/MaterialManager.h index 77ca4224f..9aeb5e992 100644 --- a/tools/assimp_view/MaterialManager.h +++ b/tools/assimp_view/MaterialManager.h @@ -52,24 +52,11 @@ namespace AssimpView { */ //------------------------------------------------------------------------------- class CMaterialManager { -private: friend class CDisplay; - // default constructor - CMaterialManager() : - m_iShaderCount(0), sDefaultTexture() {} - - ~CMaterialManager() { - if (sDefaultTexture) { - sDefaultTexture->Release(); - } - Reset(); - } - public: //------------------------------------------------------------------ // Singleton accessors - static CMaterialManager s_cInstance; inline static CMaterialManager &Instance() { return s_cInstance; } @@ -80,24 +67,20 @@ public: // Must be called before CreateMaterial() to prevent memory leaking void DeleteMaterial(AssetHelper::MeshHelper *pcIn); - //------------------------------------------------------------------ - // Create the material for a mesh. - // - // The function checks whether an identical shader is already in use. - // A shader is considered to be identical if it has the same input - // signature and takes the same number of texture channels. - int CreateMaterial(AssetHelper::MeshHelper *pcMesh, - const aiMesh *pcSource); - - //------------------------------------------------------------------ - // Setup the material for a given mesh - // pcMesh Mesh to be rendered - // pcProj Projection matrix - // aiMe Current world matrix - // pcCam Camera matrix - // vPos Position of the camera - // TODO: Extract camera position from matrix ... - // + /// @brief Create the material for a mesh. + /// + /// The function checks whether an identical shader is already in use. + /// A shader is considered to be identical if it has the same input + /// signature and takes the same number of texture channels. + int CreateMaterial(AssetHelper::MeshHelper *pcMesh, const aiMesh *pcSource); + + /// @brief Setup the material for a given mesh. + /// @param pcMesh Mesh to be rendered + /// @param pcProj Projection matrix + /// @param aiMe Current world matrix + /// @param pcCam Camera matrix + /// @param vPos Position of the camera + /// @return 0 if successful. int SetupMaterial(AssetHelper::MeshHelper *pcMesh, const aiMatrix4x4 &pcProj, const aiMatrix4x4 &aiMe, @@ -143,14 +126,29 @@ public: // Reset the state of the class // Called whenever a new asset is loaded inline void Reset() { - this->m_iShaderCount = 0; - for (TextureCache::iterator it = sCachedTextures.begin(); it != sCachedTextures.end(); ++it) { - (*it).second->Release(); + m_iShaderCount = 0; + for (auto & sCachedTexture : sCachedTextures) { + sCachedTexture.second->Release(); } sCachedTextures.clear(); } private: + // The default constructor + CMaterialManager() : + m_iShaderCount(0), + sDefaultTexture() { + // empty + } + + // Destructor, private. + ~CMaterialManager() { + if (sDefaultTexture) { + sDefaultTexture->Release(); + } + Reset(); + } + //------------------------------------------------------------------ // find a valid path to a texture file // @@ -183,15 +181,14 @@ private: bool HasAlphaPixels(IDirect3DTexture9 *piTexture); private: - // + static CMaterialManager s_cInstance; + // Specifies the number of different shaders generated for // the current asset. This number is incremented by CreateMaterial() // each time a shader isn't found in cache and needs to be created - // unsigned int m_iShaderCount; IDirect3DTexture9 *sDefaultTexture; - - typedef std::map TextureCache; + using TextureCache = std::map; TextureCache sCachedTextures; }; diff --git a/tools/assimp_view/MeshRenderer.cpp b/tools/assimp_view/MeshRenderer.cpp index 0ec12e5b1..bc1a5236f 100644 --- a/tools/assimp_view/MeshRenderer.cpp +++ b/tools/assimp_view/MeshRenderer.cpp @@ -61,11 +61,14 @@ int CMeshRenderer::DrawUnsorted(unsigned int iIndex) { D3DPRIMITIVETYPE type = D3DPT_POINTLIST; switch (g_pcAsset->pcScene->mMeshes[iIndex]->mPrimitiveTypes) { case aiPrimitiveType_POINT: - type = D3DPT_POINTLIST;break; + type = D3DPT_POINTLIST; + break; case aiPrimitiveType_LINE: - type = D3DPT_LINELIST;break; + type = D3DPT_LINELIST; + break; case aiPrimitiveType_TRIANGLE: - type = D3DPT_TRIANGLELIST;break; + type = D3DPT_TRIANGLELIST; + break; } // and draw the mesh g_piDevice->DrawIndexedPrimitive(type,