浏览代码

Merge pull request #2741 from muxanickms/fbx_orphant_embedded_textures

FBX orphan embedded textures fix
Kim Kulling 5 年之前
父节点
当前提交
ef06cabe6c

+ 8 - 0
code/FBX/FBXCompileConfig.h

@@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define INCLUDED_AI_FBX_COMPILECONFIG_H
 #define INCLUDED_AI_FBX_COMPILECONFIG_H
 
 
 #include <map>
 #include <map>
+#include <set>
 
 
 //
 //
 #if _MSC_VER > 1500 || (defined __GNUC___)
 #if _MSC_VER > 1500 || (defined __GNUC___)
@@ -54,16 +55,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #   else
 #   else
 #   define fbx_unordered_map map
 #   define fbx_unordered_map map
 #   define fbx_unordered_multimap multimap
 #   define fbx_unordered_multimap multimap
+#   define fbx_unordered_set set
+#   define fbx_unordered_multiset multiset
 #endif
 #endif
 
 
 #ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP
 #ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP
 #   include <unordered_map>
 #   include <unordered_map>
+#   include <unordered_set>
 #   if _MSC_VER > 1600
 #   if _MSC_VER > 1600
 #       define fbx_unordered_map unordered_map
 #       define fbx_unordered_map unordered_map
 #       define fbx_unordered_multimap unordered_multimap
 #       define fbx_unordered_multimap unordered_multimap
+#       define fbx_unordered_set unordered_set
+#       define fbx_unordered_multiset unordered_multiset
 #   else
 #   else
 #       define fbx_unordered_map tr1::unordered_map
 #       define fbx_unordered_map tr1::unordered_map
 #       define fbx_unordered_multimap tr1::unordered_multimap
 #       define fbx_unordered_multimap tr1::unordered_multimap
+#       define fbx_unordered_set tr1::unordered_set
+#       define fbx_unordered_multiset tr1::unordered_multiset
 #   endif
 #   endif
 #endif
 #endif
 
 

+ 53 - 4
code/FBX/FBXConverter.cpp

@@ -97,6 +97,14 @@ namespace Assimp {
             // populate the node_anim_chain_bits map, which is needed
             // populate the node_anim_chain_bits map, which is needed
             // to determine which nodes need to be generated.
             // to determine which nodes need to be generated.
             ConvertAnimations();
             ConvertAnimations();
+            // Embedded textures in FBX could be connected to nothing but to itself,
+            // for instance Texture -> Video connection only but not to the main graph,
+            // The idea here is to traverse all objects to find these Textures and convert them,
+            // so later during material conversion it will find converted texture in the textures_converted array.
+            if (doc.Settings().readTextures)
+            {
+                ConvertOrphantEmbeddedTextures();
+            }
             ConvertRootNode();
             ConvertRootNode();
 
 
             if (doc.Settings().readAllMaterials) {
             if (doc.Settings().readAllMaterials) {
@@ -1774,7 +1782,7 @@ namespace Assimp {
                 bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found)
                 bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found)
                 unsigned int index;
                 unsigned int index;
 
 
-                VideoMap::const_iterator it = textures_converted.find(media);
+                VideoMap::const_iterator it = textures_converted.find(*media);
                 if (it != textures_converted.end()) {
                 if (it != textures_converted.end()) {
                     index = (*it).second;
                     index = (*it).second;
                     textureReady = true;
                     textureReady = true;
@@ -1782,7 +1790,7 @@ namespace Assimp {
                 else {
                 else {
                     if (media->ContentLength() > 0) {
                     if (media->ContentLength() > 0) {
                         index = ConvertVideo(*media);
                         index = ConvertVideo(*media);
-                        textures_converted[media] = index;
+                        textures_converted[*media] = index;
                         textureReady = true;
                         textureReady = true;
                     }
                     }
                 }
                 }
@@ -2306,13 +2314,13 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
             if (media != nullptr && media->ContentLength() > 0) {
             if (media != nullptr && media->ContentLength() > 0) {
                 unsigned int index;
                 unsigned int index;
 
 
-                VideoMap::const_iterator it = textures_converted.find(media);
+                VideoMap::const_iterator it = textures_converted.find(*media);
                 if (it != textures_converted.end()) {
                 if (it != textures_converted.end()) {
                     index = (*it).second;
                     index = (*it).second;
                 }
                 }
                 else {
                 else {
                     index = ConvertVideo(*media);
                     index = ConvertVideo(*media);
-                    textures_converted[media] = index;
+                    textures_converted[*media] = index;
                 }
                 }
 
 
                 // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
                 // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
@@ -3666,6 +3674,47 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
             }
             }
         }
         }
 
 
+        void FBXConverter::ConvertOrphantEmbeddedTextures()
+        {
+            // in C++14 it could be:
+            // for (auto&& [id, object] : objects)
+            for (auto&& id_and_object : doc.Objects())
+            {
+                auto&& id = std::get<0>(id_and_object);
+                auto&& object = std::get<1>(id_and_object);
+                // If an object doesn't have parent
+                if (doc.ConnectionsBySource().count(id) == 0)
+                {
+                    const Texture* realTexture = nullptr;
+                    try
+                    {
+                        const auto& element = object->GetElement();
+                        const Token& key = element.KeyToken();
+                        const char* obtype = key.begin();
+                        const size_t length = static_cast<size_t>(key.end() - key.begin());
+                        if (strncmp(obtype, "Texture", length) == 0)
+                        {
+                            const Texture* texture = static_cast<const Texture*>(object->Get());
+                            if (texture->Media() && texture->Media()->ContentLength() > 0)
+                            {
+                                realTexture = texture;
+                            }
+                        }
+                    }
+                    catch (...)
+                    {
+                        // do nothing
+                    }
+                    if (realTexture)
+                    {
+                        const Video* media = realTexture->Media();
+                        unsigned int index = ConvertVideo(*media);
+                        textures_converted[*media] = index;
+                    }
+                }
+            }
+        }
+
         // ------------------------------------------------------------------------------------------------
         // ------------------------------------------------------------------------------------------------
         void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones)
         void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones)
         {
         {

+ 9 - 5
code/FBX/FBXConverter.h

@@ -427,6 +427,10 @@ private:
     // copy generated meshes, animations, lights, cameras and textures to the output scene
     // copy generated meshes, animations, lights, cameras and textures to the output scene
     void TransferDataToScene();
     void TransferDataToScene();
 
 
+    // ------------------------------------------------------------------------------------------------
+    // FBX file could have embedded textures not connected to anything
+    void ConvertOrphantEmbeddedTextures();
+
 private:
 private:
     // 0: not assigned yet, others: index is value - 1
     // 0: not assigned yet, others: index is value - 1
     unsigned int defaultMaterialIndex;
     unsigned int defaultMaterialIndex;
@@ -438,21 +442,21 @@ private:
     std::vector<aiCamera*> cameras;
     std::vector<aiCamera*> cameras;
     std::vector<aiTexture*> textures;
     std::vector<aiTexture*> textures;
 
 
-    using MaterialMap = std::map<const Material*, unsigned int>;
+    using MaterialMap = std::fbx_unordered_map<const Material*, unsigned int>;
     MaterialMap materials_converted;
     MaterialMap materials_converted;
 
 
-    using VideoMap = std::map<const Video*, unsigned int>;
+    using VideoMap = std::fbx_unordered_map<const Video, unsigned int>;
     VideoMap textures_converted;
     VideoMap textures_converted;
 
 
-    using MeshMap = std::map<const Geometry*, std::vector<unsigned int> >;
+    using MeshMap = std::fbx_unordered_map<const Geometry*, std::vector<unsigned int> >;
     MeshMap meshes_converted;
     MeshMap meshes_converted;
 
 
     // fixed node name -> which trafo chain components have animations?
     // fixed node name -> which trafo chain components have animations?
-    using NodeAnimBitMap = std::map<std::string, unsigned int> ;
+    using NodeAnimBitMap = std::fbx_unordered_map<std::string, unsigned int> ;
     NodeAnimBitMap node_anim_chain_bits;
     NodeAnimBitMap node_anim_chain_bits;
 
 
     // number of nodes with the same name
     // number of nodes with the same name
-    using NodeNameCache = std::unordered_map<std::string, unsigned int>;
+    using NodeNameCache = std::fbx_unordered_map<std::string, unsigned int>;
     NodeNameCache mNodeNames;
     NodeNameCache mNodeNames;
 
 
     // Deformer name is not the same as a bone name - it does contain the bone name though :)
     // Deformer name is not the same as a bone name - it does contain the bone name though :)

+ 37 - 2
code/FBX/FBXDocument.h

@@ -637,6 +637,20 @@ public:
         return ptr;
         return ptr;
     }
     }
 
 
+    bool operator==(const Video& other) const
+    {
+        return (
+               type == other.type
+            && relativeFileName == other.relativeFileName
+            && fileName == other.fileName
+        );
+    }
+
+    bool operator<(const Video& other) const
+    {
+        return std::tie(type, relativeFileName, fileName) < std::tie(other.type, other.relativeFileName, other.fileName);
+    }
+
 private:
 private:
     std::string type;
     std::string type;
     std::string relativeFileName;
     std::string relativeFileName;
@@ -1005,10 +1019,10 @@ public:
 // during their entire lifetime (Document). FBX files have
 // during their entire lifetime (Document). FBX files have
 // up to many thousands of objects (most of which we never use),
 // up to many thousands of objects (most of which we never use),
 // so the memory overhead for them should be kept at a minimum.
 // so the memory overhead for them should be kept at a minimum.
-typedef std::map<uint64_t, LazyObject*> ObjectMap;
+typedef std::fbx_unordered_map<uint64_t, LazyObject*> ObjectMap;
 typedef std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > PropertyTemplateMap;
 typedef std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > PropertyTemplateMap;
 
 
-typedef std::multimap<uint64_t, const Connection*> ConnectionMap;
+typedef std::fbx_unordered_multimap<uint64_t, const Connection*> ConnectionMap;
 
 
 /** DOM class for global document settings, a single instance per document can
 /** DOM class for global document settings, a single instance per document can
  *  be accessed via Document.Globals(). */
  *  be accessed via Document.Globals(). */
@@ -1177,4 +1191,25 @@ private:
 } // Namespace FBX
 } // Namespace FBX
 } // Namespace Assimp
 } // Namespace Assimp
 
 
+namespace std
+{
+    template <>
+    struct hash<const Assimp::FBX::Video>
+    {
+        std::size_t operator()(const Assimp::FBX::Video& video) const
+        {
+            using std::size_t;
+            using std::hash;
+            using std::string;
+
+            size_t res = 17;
+            res = res * 31 + hash<string>()(video.Name());
+            res = res * 31 + hash<string>()(video.RelativeFilename());
+            res = res * 31 + hash<string>()(video.Type());
+
+            return res;
+        }
+    };
+}
+
 #endif // INCLUDED_AI_FBX_DOCUMENT_H
 #endif // INCLUDED_AI_FBX_DOCUMENT_H

文件差异内容过多而无法显示
+ 544 - 0
test/models/FBX/box_orphant_embedded_texture.fbx


+ 20 - 0
test/unit/utFBXImporterExporter.cpp

@@ -263,3 +263,23 @@ TEST_F(utFBXImporterExporter, fbxTokenizeTestTest) {
     //const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/transparentTest2.fbx", aiProcess_ValidateDataStructure);
     //const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/transparentTest2.fbx", aiProcess_ValidateDataStructure);
     //EXPECT_NE(nullptr, scene);
     //EXPECT_NE(nullptr, scene);
 }
 }
+
+TEST_F(utFBXImporterExporter, importOrphantEmbeddedTextureTest) {
+    // see https://github.com/assimp/assimp/issues/1957
+    Assimp::Importer importer;
+    const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/box_orphant_embedded_texture.fbx", aiProcess_ValidateDataStructure);
+    EXPECT_NE(nullptr, scene);
+
+    EXPECT_EQ(1u, scene->mNumMaterials);
+    aiMaterial *mat = scene->mMaterials[0];
+    ASSERT_NE(nullptr, mat);
+
+    aiString path;
+    aiTextureMapMode modes[2];
+    ASSERT_EQ(aiReturn_SUCCESS, mat->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, nullptr, nullptr, modes));
+    ASSERT_STREQ(path.C_Str(), "..\\Primitives\\GridGrey.tga");
+
+    ASSERT_EQ(1u, scene->mNumTextures);
+    ASSERT_TRUE(scene->mTextures[0]->pcData);
+    ASSERT_EQ(9026u, scene->mTextures[0]->mWidth) << "FBX ASCII base64 compression used for a texture.";
+}

部分文件因为文件数量过多而无法显示