瀏覽代碼

Merge branch 'master' into fix_malformed_irr_files

Steve M 2 年之前
父節點
當前提交
ca7f6de671
共有 4 個文件被更改,包括 77 次插入10 次删除
  1. 1 1
      code/AssetLib/glTF2/glTF2Exporter.cpp
  2. 30 3
      code/Common/BaseImporter.cpp
  3. 34 0
      test/unit/utImporter.cpp
  4. 12 6
      test/unit/utglTF2ImportExport.cpp

+ 1 - 1
code/AssetLib/glTF2/glTF2Exporter.cpp

@@ -730,7 +730,7 @@ bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlo
 
 
 bool glTF2Exporter::GetMatSpecular(const aiMaterial &mat, glTF2::MaterialSpecular &specular) {
 bool glTF2Exporter::GetMatSpecular(const aiMaterial &mat, glTF2::MaterialSpecular &specular) {
     // Specular requires either/or, default factors of zero disables specular, so do not export
     // Specular requires either/or, default factors of zero disables specular, so do not export
-    if (GetMatColor(mat, specular.specularColorFactor, AI_MATKEY_COLOR_SPECULAR) != AI_SUCCESS || mat.Get(AI_MATKEY_SPECULAR_FACTOR, specular.specularFactor) != AI_SUCCESS) {
+    if (GetMatColor(mat, specular.specularColorFactor, AI_MATKEY_COLOR_SPECULAR) != AI_SUCCESS && mat.Get(AI_MATKEY_SPECULAR_FACTOR, specular.specularFactor) != AI_SUCCESS) {
         return false;
         return false;
     }
     }
     // The spec states that the default is 1.0 and [1.0, 1.0, 1.0]. We if both are 0, which should disable specular. Otherwise, if one is 0, set to 1.0
     // The spec states that the default is 1.0 and [1.0, 1.0, 1.0]. We if both are 0, which should disable specular. Otherwise, if one is 0, set to 1.0

+ 30 - 3
code/Common/BaseImporter.cpp

@@ -59,6 +59,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <memory>
 #include <memory>
 #include <sstream>
 #include <sstream>
 
 
+namespace {
+// Checks whether the passed string is a gcs version.
+bool IsGcsVersion(const std::string &s) {
+    if (s.empty()) return false;
+    return std::all_of(s.cbegin(), s.cend(), [](const char c) {
+        // gcs only permits numeric characters.
+        return std::isdigit(static_cast<int>(c));
+    });
+}
+
+// Removes a possible version hash from a filename, as found for example in
+// gcs uris (e.g. `gs://bucket/model.glb#1234`), see also
+// https://github.com/GoogleCloudPlatform/gsutil/blob/c80f329bc3c4011236c78ce8910988773b2606cb/gslib/storage_url.py#L39.
+std::string StripVersionHash(const std::string &filename) {
+    const std::string::size_type pos = filename.find_last_of('#');
+    // Only strip if the hash is behind a possible file extension and the part
+    // behind the hash is a version string.
+    if (pos != std::string::npos && pos > filename.find_last_of('.') &&
+        IsGcsVersion(filename.substr(pos + 1))) {
+        return filename.substr(0, pos);
+    }
+    return filename;
+}
+}  // namespace
+
 using namespace Assimp;
 using namespace Assimp;
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -241,6 +266,7 @@ void BaseImporter::GetExtensionList(std::set<std::string> &extensions) {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Check for file extension
 // Check for file extension
 /*static*/ bool BaseImporter::HasExtension(const std::string &pFile, const std::set<std::string> &extensions) {
 /*static*/ bool BaseImporter::HasExtension(const std::string &pFile, const std::set<std::string> &extensions) {
+    const std::string file = StripVersionHash(pFile);
     // CAUTION: Do not just search for the extension!
     // CAUTION: Do not just search for the extension!
     // GetExtension() returns the part after the *last* dot, but some extensions
     // GetExtension() returns the part after the *last* dot, but some extensions
     // have dots inside them, e.g. ogre.mesh.xml. Compare the entire end of the
     // have dots inside them, e.g. ogre.mesh.xml. Compare the entire end of the
@@ -248,9 +274,9 @@ void BaseImporter::GetExtensionList(std::set<std::string> &extensions) {
     for (const std::string& ext : extensions) {
     for (const std::string& ext : extensions) {
         // Yay for C++<20 not having std::string::ends_with()
         // Yay for C++<20 not having std::string::ends_with()
         const std::string dotExt = "." + ext;
         const std::string dotExt = "." + ext;
-        if (dotExt.length() > pFile.length()) continue;
+        if (dotExt.length() > file.length()) continue;
         // Possible optimization: Fetch the lowercase filename!
         // Possible optimization: Fetch the lowercase filename!
-        if (0 == ASSIMP_stricmp(pFile.c_str() + pFile.length() - dotExt.length(), dotExt.c_str())) {
+        if (0 == ASSIMP_stricmp(file.c_str() + file.length() - dotExt.length(), dotExt.c_str())) {
             return true;
             return true;
         }
         }
     }
     }
@@ -259,7 +285,8 @@ void BaseImporter::GetExtensionList(std::set<std::string> &extensions) {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Get file extension from path
 // Get file extension from path
-std::string BaseImporter::GetExtension(const std::string &file) {
+std::string BaseImporter::GetExtension(const std::string &pFile) {
+    const std::string file = StripVersionHash(pFile);
     std::string::size_type pos = file.find_last_of('.');
     std::string::size_type pos = file.find_last_of('.');
 
 
     // no file extension at all
     // no file extension at all

+ 34 - 0
test/unit/utImporter.cpp

@@ -361,3 +361,37 @@ TEST_F(ImporterTest, unexpectedException) {
         EXPECT_TRUE(false);
         EXPECT_TRUE(false);
     }
     }
 }
 }
+
+// ------------------------------------------------------------------------------------------------
+
+struct ExtensionTestCase {
+    std::string testName;
+    std::string filename;
+    std::string getExtensionResult;
+    std::string hasExtension;
+    bool hasExtensionResult;
+};
+
+using ExtensionTest = ::testing::TestWithParam<ExtensionTestCase>;
+
+TEST_P(ExtensionTest, testGetAndHasExtension) {
+    const ExtensionTestCase& testCase = GetParam();
+    EXPECT_EQ(testCase.getExtensionResult, BaseImporter::GetExtension(testCase.filename));
+    EXPECT_EQ(testCase.hasExtensionResult, BaseImporter::HasExtension(testCase.filename, {testCase.hasExtension}));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ExtensionTests, ExtensionTest,
+    ::testing::ValuesIn<ExtensionTestCase>({
+        {"NoExtension", "name", "", "glb", false},
+        {"NoExtensionAndEmptyVersion", "name#", "", "glb", false},
+        {"WithExtensionAndEmptyVersion", "name.glb#", "glb#", "glb", false},
+        {"WithExtensionAndVersion", "name.glb#1234", "glb", "glb", true},
+        {"WithExtensionAndHashInStem", "name#1234.glb", "glb", "glb", true},
+        {"WithExtensionAndInvalidVersion", "name.glb#_", "glb#_", "glb", false},
+        {"WithExtensionAndDotAndHashInStem", "name.glb#.abc", "abc", "glb", false},
+        {"WithTwoExtensions", "name.abc.def", "def", "abc.def", true},
+    }),
+    [](const ::testing::TestParamInfo<ExtensionTest::ParamType>& info) {
+        return info.param.testName;
+    });

+ 12 - 6
test/unit/utglTF2ImportExport.cpp

@@ -60,7 +60,7 @@ using namespace Assimp;
 
 
 class utglTF2ImportExport : public AbstractImportExportBase {
 class utglTF2ImportExport : public AbstractImportExportBase {
 public:
 public:
-    virtual bool importerMatTest(const char *file, bool spec_gloss, std::array<aiTextureMapMode, 2> exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }) {
+    virtual bool importerMatTest(const char *file, bool spec, bool gloss, std::array<aiTextureMapMode, 2> exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }) {
         Assimp::Importer importer;
         Assimp::Importer importer;
         const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure);
         const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure);
         EXPECT_NE(scene, nullptr);
         EXPECT_NE(scene, nullptr);
@@ -105,16 +105,19 @@ public:
 
 
         aiColor3D spec_color = { 0, 0, 0 };
         aiColor3D spec_color = { 0, 0, 0 };
         ai_real glossiness = ai_real(0.5);
         ai_real glossiness = ai_real(0.5);
-        if (spec_gloss) {
+        if (spec) {
             EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color));
             EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color));
             constexpr ai_real spec_val(0.20000000298023225); // From the file
             constexpr ai_real spec_val(0.20000000298023225); // From the file
             EXPECT_EQ(spec_val, spec_color.r);
             EXPECT_EQ(spec_val, spec_color.r);
             EXPECT_EQ(spec_val, spec_color.g);
             EXPECT_EQ(spec_val, spec_color.g);
             EXPECT_EQ(spec_val, spec_color.b);
             EXPECT_EQ(spec_val, spec_color.b);
+        } else {
+            EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color));
+        }
+        if (gloss) {
             EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness));
             EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness));
             EXPECT_EQ(ai_real(1.0), glossiness);
             EXPECT_EQ(ai_real(1.0), glossiness);
         } else {
         } else {
-            EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color));
             EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness));
             EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness));
         }
         }
 
 
@@ -143,7 +146,7 @@ public:
 };
 };
 
 
 TEST_F(utglTF2ImportExport, importglTF2FromFileTest) {
 TEST_F(utglTF2ImportExport, importglTF2FromFileTest) {
-    EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", false, {aiTextureMapMode_Mirror, aiTextureMapMode_Clamp}));
+    EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", false, false, {aiTextureMapMode_Mirror, aiTextureMapMode_Clamp}));
 }
 }
 
 
 TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) {
 TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) {
@@ -151,7 +154,7 @@ TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) {
 }
 }
 
 
 TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) {
 TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) {
-    EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true));
+    EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true, true));
 }
 }
 
 
 void VerifyClearCoatScene(const aiScene *scene) {
 void VerifyClearCoatScene(const aiScene *scene) {
@@ -223,13 +226,16 @@ TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossi
     // Export with specular glossiness disabled
     // Export with specular glossiness disabled
     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb"));
     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb"));
     
     
+    // And re-import
+    EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true, false));
+
     // Export with specular glossiness enabled
     // Export with specular glossiness enabled
     ExportProperties props;
     ExportProperties props;
     props.SetPropertyBool(AI_CONFIG_USE_GLTF_PBR_SPECULAR_GLOSSINESS, true);
     props.SetPropertyBool(AI_CONFIG_USE_GLTF_PBR_SPECULAR_GLOSSINESS, true);
     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", 0, &props));
     EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", 0, &props));
 
 
     // And re-import
     // And re-import
-    EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true));
+    EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true, true));
 }
 }
 
 
 TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) {
 TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) {