Browse Source

Merge branch 'master' into sfjohnston_mods

Kim Kulling 2 years ago
parent
commit
64e0ba2625

+ 1 - 1
.github/workflows/sanitizer.yml

@@ -46,7 +46,7 @@ jobs:
         CC: clang
     
     - name: configure and build
-      uses: lukka/run-cmake@v2
+      uses: lukka/run-cmake@v3
       with:
         cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
         cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'

+ 2 - 2
CMakeLists.txt

@@ -286,9 +286,9 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT MINGW)
 ELSEIF(MSVC)
   # enable multi-core compilation with MSVC
   IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) # clang-cl
-    ADD_COMPILE_OPTIONS(/bigobj /W4 /WX )
+    ADD_COMPILE_OPTIONS(/bigobj)
   ELSE() # msvc
-    ADD_COMPILE_OPTIONS(/MP /bigobj /W4 /WX)
+    ADD_COMPILE_OPTIONS(/MP /bigobj)
   ENDIF()
   
   # disable "elements of array '' will be default initialized" warning on MSVC2013

+ 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) {
     // 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;
     }
     // 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

+ 49 - 19
code/Common/BaseImporter.cpp

@@ -59,6 +59,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <memory>
 #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;
 
 // ------------------------------------------------------------------------------------------------
@@ -230,33 +255,38 @@ void BaseImporter::GetExtensionList(std::set<std::string> &extensions) {
         const char *ext0,
         const char *ext1,
         const char *ext2) {
-    std::string::size_type pos = pFile.find_last_of('.');
-
-    // no file extension - can't read
-    if (pos == std::string::npos) {
-        return false;
-    }
-
-    const char *ext_real = &pFile[pos + 1];
-    if (!ASSIMP_stricmp(ext_real, ext0)) {
-        return true;
-    }
-
-    // check for other, optional, file extensions
-    if (ext1 && !ASSIMP_stricmp(ext_real, ext1)) {
-        return true;
+    std::set<std::string> extensions;
+    for (const char* ext : {ext0, ext1, ext2}) {
+        if (ext == nullptr) continue;
+        extensions.emplace(ext);
     }
+    return HasExtension(pFile, extensions);
+}
 
-    if (ext2 && !ASSIMP_stricmp(ext_real, ext2)) {
-        return true;
+// ------------------------------------------------------------------------------------------------
+// Check for file extension
+/*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!
+    // 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
+    // string.
+    for (const std::string& ext : extensions) {
+        // Yay for C++<20 not having std::string::ends_with()
+        const std::string dotExt = "." + ext;
+        if (dotExt.length() > file.length()) continue;
+        // Possible optimization: Fetch the lowercase filename!
+        if (0 == ASSIMP_stricmp(file.c_str() + file.length() - dotExt.length(), dotExt.c_str())) {
+            return true;
+        }
     }
-
     return false;
 }
 
 // ------------------------------------------------------------------------------------------------
 // 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('.');
 
     // no file extension at all

+ 3 - 17
code/Common/Importer.cpp

@@ -637,24 +637,10 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) {
             std::set<std::string> extensions;
             pimpl->mImporter[a]->GetExtensionList(extensions);
 
-            // CAUTION: Do not just search for the extension!
-            // 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 string.
-            for (std::set<std::string>::const_iterator it = extensions.cbegin(); it != extensions.cend(); ++it) {
-
-                // Yay for C++<20 not having std::string::ends_with()
-                std::string extension = "." + *it;
-                if (extension.length() <= pFile.length()) {
-                    // Possible optimization: Fetch the lowercase filename!
-                    if (0 == ASSIMP_stricmp(pFile.c_str() + pFile.length() - extension.length(), extension.c_str())) {
-                        ImporterAndIndex candidate = { pimpl->mImporter[a], a };
-                        possibleImporters.push_back(candidate);
-                        break;
-                    }
-                }
-
+            if (BaseImporter::HasExtension(pFile, extensions)) {
+                ImporterAndIndex candidate = { pimpl->mImporter[a], a };
+                possibleImporters.push_back(candidate);
             }
-
         }
 
         // If just one importer supports this extension, pick it and close the case.

+ 10 - 0
include/assimp/BaseImporter.h

@@ -275,6 +275,16 @@ public: // static utilities
             const char *ext1 = nullptr,
             const char *ext2 = nullptr);
 
+    // -------------------------------------------------------------------
+    /** @brief Check whether a file has one of the passed file extensions
+     *  @param pFile Input file
+     *  @param extensions Extensions to check for. Lowercase characters only, no dot!
+     *  @note Case-insensitive
+     */
+    static bool HasExtension(
+            const std::string &pFile,
+            const std::set<std::string> &extensions);
+
     // -------------------------------------------------------------------
     /** @brief Extract file extension from a string
      *  @param pFile Input file

+ 1 - 1
test/models/IRR/scenegraphAnim.irr

@@ -126,7 +126,7 @@
 					<float name="Shininess" value="0.000000" />
 					<float name="Param1" value="0.000000" />
 					<float name="Param2" value="0.000000" />
-					<texture name="Texture1" value="="UVTransformTestImg.png" />
+					<texture name="Texture1" value="UVTransformTestImg.png" />
 					<texture name="Texture2" value="" />
 					<texture name="Texture3" value="" />
 					<texture name="Texture4" value="" />

+ 554 - 0
test/models/IRR/scenegraphAnimMod.irr

@@ -0,0 +1,554 @@
+<?xml version="1.0"?>
+<irr_scene>
+
+	<attributes>
+		<string name="Name" value="root" />
+		<int name="Id" value="-1" />
+		<colorf name="AmbientLight" value="0.000000, 0.000000, 0.000000, 0.000000" />
+	</attributes>
+
+	<node type="camera">
+
+		<attributes>
+			<string name="Name" value="" />
+			<int name="Id" value="-1" />
+			<vector3d name="Position" value="3.333332, 1.666666, -3.333332" />
+			<vector3d name="Rotation" value="14426.697266, 0.000000, 0.000000" />
+			<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
+			<bool name="Visible" value="true" />
+			<enum name="AutomaticCulling" value="box" />
+			<int name="DebugDataVisible" value="-1" />
+			<bool name="IsDebugObject" value="false" />
+			<vector3d name="Target" value="0.000000, 0.000000, 100.000000" />
+			<vector3d name="UpVector" value="0.000000, 1.000000, 0.000000" />
+			<float name="Fovy" value="1.256637" />
+			<float name="Aspect" value="1.333333" />
+			<float name="ZNear" value="1.000000" />
+			<float name="ZFar" value="3000.000000" />
+		</attributes>
+
+<!--		<animators>-->
+<!--			<attributes>-->
+<!--				<string name="Type" value="rotation" />-->
+<!--				<vector3d name="Rotation" value="0.200000, 0.000000, 0.000000" />-->
+<!--			</attributes>-->
+<!--		</animators>-->
+	</node>
+
+	<node type="light">
+
+		<attributes>
+			<string name="Name" value="SpotLightAnim" />
+			<int name="Id" value="-1" />
+			<vector3d name="Position" value="3.333332, 1.666666, -3.333332" />
+			<vector3d name="Rotation" value="15721.575195, 37111.304688, 29682.550781" />
+			<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
+			<bool name="Visible" value="true" />
+			<enum name="AutomaticCulling" value="box" />
+			<int name="DebugDataVisible" value="-1" />
+			<bool name="IsDebugObject" value="false" />
+			<colorf name="AmbientColor" value="0.000000, 0.000000, 0.000000, 1.000000" />
+			<colorf name="DiffuseColor" value="1.000000, 1.000000, 1.000000, 1.000000" />
+			<colorf name="SpecularColor" value="1.000000, 1.000000, 1.000000, 1.000000" />
+			<float name="Radius" value="100.000000" />
+			<bool name="CastShadows" value="true" />
+			<enum name="LightType" value="Spot" />
+		</attributes>
+
+<!--		<animators>-->
+<!--			<attributes>-->
+<!--				<string name="Type" value="rotation" />-->
+<!--				<vector3d name="Rotation" value="0.200000, 0.500000, 0.400000" />-->
+<!--			</attributes>-->
+<!--		</animators>-->
+	</node>
+
+	<node type="light">
+
+		<attributes>
+			<string name="Name" value="PointLight" />
+			<int name="Id" value="-1" />
+			<vector3d name="Position" value="3.333332, 1.666666, -3.333332" />
+			<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
+			<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
+			<bool name="Visible" value="true" />
+			<enum name="AutomaticCulling" value="box" />
+			<int name="DebugDataVisible" value="-1" />
+			<bool name="IsDebugObject" value="false" />
+			<colorf name="AmbientColor" value="0.000000, 0.000000, 0.000000, 1.000000" />
+			<colorf name="DiffuseColor" value="1.000000, 1.000000, 1.000000, 1.000000" />
+			<colorf name="SpecularColor" value="1.000000, 1.000000, 1.000000, 1.000000" />
+			<float name="Radius" value="100.000000" />
+			<bool name="CastShadows" value="true" />
+			<enum name="LightType" value="Point" />
+		</attributes>
+
+	</node>
+
+	<node type="empty">
+
+		<attributes>
+			<string name="Name" value="ChildOfRoot" />
+			<int name="Id" value="-1" />
+			<vector3d name="Position" value="18.573780, 14.000406, -10.178408" />
+			<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
+			<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
+			<bool name="Visible" value="true" />
+			<enum name="AutomaticCulling" value="false" />
+			<int name="DebugDataVisible" value="0" />
+			<bool name="IsDebugObject" value="false" />
+		</attributes>
+
+		<node type="sphere">
+
+			<attributes>
+				<string name="Name" value="Sphere_ChildOfDummy" />
+				<int name="Id" value="-1" />
+				<vector3d name="Position" value="22.607138, 0.000000, -19.720987" />
+				<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
+				<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
+				<bool name="Visible" value="true" />
+				<enum name="AutomaticCulling" value="box" />
+				<int name="DebugDataVisible" value="0" />
+				<bool name="IsDebugObject" value="false" />
+				<float name="Radius" value="5.000000" />
+				<int name="PolyCountX" value="16" />
+				<int name="PolyCountY" value="16" />
+			</attributes>
+
+			<materials>
+				<attributes>
+					<enum name="Type" value="solid" />
+					<color name="Ambient" value="ffffffff" />
+					<color name="Diffuse" value="ffffffff" />
+					<color name="Emissive" value="00000000" />
+					<color name="Specular" value="ffffffff" />
+					<float name="Shininess" value="0.000000" />
+					<float name="Param1" value="0.000000" />
+					<float name="Param2" value="0.000000" />
+					<texture name="Texture1" value="UVTransformTestImg.png" />
+					<texture name="Texture2" value="" />
+					<texture name="Texture3" value="" />
+					<texture name="Texture4" value="" />
+					<bool name="Wireframe" value="false" />
+					<bool name="GouraudShading" value="true" />
+					<bool name="Lighting" value="false" />
+					<bool name="ZWriteEnable" value="true" />
+					<int name="ZBuffer" value="1" />
+					<bool name="BackfaceCulling" value="true" />
+					<bool name="FogEnable" value="false" />
+					<bool name="NormalizeNormals" value="false" />
+					<bool name="BilinearFilter1" value="true" />
+					<bool name="BilinearFilter2" value="true" />
+					<bool name="BilinearFilter3" value="true" />
+					<bool name="BilinearFilter4" value="true" />
+					<bool name="TrilinearFilter1" value="false" />
+					<bool name="TrilinearFilter2" value="false" />
+					<bool name="TrilinearFilter3" value="false" />
+					<bool name="TrilinearFilter4" value="false" />
+					<bool name="AnisotropicFilter1" value="false" />
+					<bool name="AnisotropicFilter2" value="false" />
+					<bool name="AnisotropicFilter3" value="false" />
+					<bool name="AnisotropicFilter4" value="false" />
+					<enum name="TextureWrap1" value="texture_clamp_repeat" />
+					<enum name="TextureWrap2" value="texture_clamp_repeat" />
+					<enum name="TextureWrap3" value="texture_clamp_repeat" />
+					<enum name="TextureWrap4" value="texture_clamp_repeat" />
+				</attributes>
+			</materials>
+			<animators>
+				<attributes>
+					<string name="Type" value="flyCircle" />
+					<vector3d name="Center" value="0.000000, 0.000000, 0.000000" />
+					<float name="Radius" value="30.000000" />
+					<float name="Speed" value="0.001000" />
+					<vector3d name="Direction" value="0.000000, 1.000000, 0.000000" />
+				</attributes>
+			</animators>
+			<node type="empty">
+
+				<attributes>
+					<string name="Name" value="ChildOfSphere" />
+					<int name="Id" value="-1" />
+					<vector3d name="Position" value="18.573780, 14.000406, -10.178408" />
+					<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
+					<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
+					<bool name="Visible" value="true" />
+					<enum name="AutomaticCulling" value="false" />
+					<int name="DebugDataVisible" value="0" />
+					<bool name="IsDebugObject" value="false" />
+				</attributes>
+
+			</node>
+
+		</node>
+
+	</node>
+
+	<node type="animatedMesh">
+
+		<attributes>
+			<string name="Name" value="" />
+			<int name="Id" value="-1" />
+			<vector3d name="Position" value="18.573780, 14.000406, -10.178408" />
+			<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
+			<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
+			<bool name="Visible" value="true" />
+			<enum name="AutomaticCulling" value="box" />
+			<int name="DebugDataVisible" value="0" />
+			<bool name="IsDebugObject" value="false" />
+			<string name="Mesh" value="dwarf.x" />
+			<bool name="Looping" value="true" />
+			<bool name="ReadOnlyMaterials" value="false" />
+			<float name="FramesPerSecond" value="0.025000" />
+		</attributes>
+
+		<materials>
+			<attributes>
+				<enum name="Type" value="solid" />
+				<color name="Ambient" value="ffffffff" />
+				<color name="Diffuse" value="ffcccccc" />
+				<color name="Emissive" value="ff5d5d5d" />
+				<color name="Specular" value="ff000000" />
+				<float name="Shininess" value="0.000000" />
+				<float name="Param1" value="0.000000" />
+				<float name="Param2" value="0.000000" />
+				<texture name="Texture1" value="axe.jpg" />
+				<texture name="Texture2" value="" />
+				<texture name="Texture3" value="" />
+				<texture name="Texture4" value="" />
+				<bool name="Wireframe" value="false" />
+				<bool name="GouraudShading" value="true" />
+				<bool name="Lighting" value="false" />
+				<bool name="ZWriteEnable" value="true" />
+				<int name="ZBuffer" value="1" />
+				<bool name="BackfaceCulling" value="true" />
+				<bool name="FogEnable" value="false" />
+				<bool name="NormalizeNormals" value="false" />
+				<bool name="BilinearFilter1" value="true" />
+				<bool name="BilinearFilter2" value="true" />
+				<bool name="BilinearFilter3" value="true" />
+				<bool name="BilinearFilter4" value="true" />
+				<bool name="TrilinearFilter1" value="false" />
+				<bool name="TrilinearFilter2" value="false" />
+				<bool name="TrilinearFilter3" value="false" />
+				<bool name="TrilinearFilter4" value="false" />
+				<bool name="AnisotropicFilter1" value="false" />
+				<bool name="AnisotropicFilter2" value="false" />
+				<bool name="AnisotropicFilter3" value="false" />
+				<bool name="AnisotropicFilter4" value="false" />
+				<enum name="TextureWrap1" value="texture_clamp_repeat" />
+				<enum name="TextureWrap2" value="texture_clamp_repeat" />
+				<enum name="TextureWrap3" value="texture_clamp_repeat" />
+				<enum name="TextureWrap4" value="texture_clamp_repeat" />
+			</attributes>
+			<attributes>
+				<enum name="Type" value="solid" />
+				<color name="Ambient" value="ffffffff" />
+				<color name="Diffuse" value="ffcccccc" />
+				<color name="Emissive" value="ff4d4d4d" />
+				<color name="Specular" value="ff000000" />
+				<float name="Shininess" value="0.000000" />
+				<float name="Param1" value="0.000000" />
+				<float name="Param2" value="0.000000" />
+				<texture name="Texture1" value="dwarf.jpg" />
+				<texture name="Texture2" value="" />
+				<texture name="Texture3" value="" />
+				<texture name="Texture4" value="" />
+				<bool name="Wireframe" value="false" />
+				<bool name="GouraudShading" value="true" />
+				<bool name="Lighting" value="false" />
+				<bool name="ZWriteEnable" value="true" />
+				<int name="ZBuffer" value="1" />
+				<bool name="BackfaceCulling" value="true" />
+				<bool name="FogEnable" value="false" />
+				<bool name="NormalizeNormals" value="false" />
+				<bool name="BilinearFilter1" value="true" />
+				<bool name="BilinearFilter2" value="true" />
+				<bool name="BilinearFilter3" value="true" />
+				<bool name="BilinearFilter4" value="true" />
+				<bool name="TrilinearFilter1" value="false" />
+				<bool name="TrilinearFilter2" value="false" />
+				<bool name="TrilinearFilter3" value="false" />
+				<bool name="TrilinearFilter4" value="false" />
+				<bool name="AnisotropicFilter1" value="false" />
+				<bool name="AnisotropicFilter2" value="false" />
+				<bool name="AnisotropicFilter3" value="false" />
+				<bool name="AnisotropicFilter4" value="false" />
+				<enum name="TextureWrap1" value="texture_clamp_repeat" />
+				<enum name="TextureWrap2" value="texture_clamp_repeat" />
+				<enum name="TextureWrap3" value="texture_clamp_repeat" />
+				<enum name="TextureWrap4" value="texture_clamp_repeat" />
+			</attributes>
+			<attributes>
+				<enum name="Type" value="solid" />
+				<color name="Ambient" value="ffffffff" />
+				<color name="Diffuse" value="ffcccccc" />
+				<color name="Emissive" value="ff505050" />
+				<color name="Specular" value="ff000000" />
+				<float name="Shininess" value="0.000000" />
+				<float name="Param1" value="0.000000" />
+				<float name="Param2" value="0.000000" />
+				<texture name="Texture1" value="" />
+				<texture name="Texture2" value="" />
+				<texture name="Texture3" value="" />
+				<texture name="Texture4" value="" />
+				<bool name="Wireframe" value="false" />
+				<bool name="GouraudShading" value="true" />
+				<bool name="Lighting" value="false" />
+				<bool name="ZWriteEnable" value="true" />
+				<int name="ZBuffer" value="1" />
+				<bool name="BackfaceCulling" value="true" />
+				<bool name="FogEnable" value="false" />
+				<bool name="NormalizeNormals" value="false" />
+				<bool name="BilinearFilter1" value="true" />
+				<bool name="BilinearFilter2" value="true" />
+				<bool name="BilinearFilter3" value="true" />
+				<bool name="BilinearFilter4" value="true" />
+				<bool name="TrilinearFilter1" value="false" />
+				<bool name="TrilinearFilter2" value="false" />
+				<bool name="TrilinearFilter3" value="false" />
+				<bool name="TrilinearFilter4" value="false" />
+				<bool name="AnisotropicFilter1" value="false" />
+				<bool name="AnisotropicFilter2" value="false" />
+				<bool name="AnisotropicFilter3" value="false" />
+				<bool name="AnisotropicFilter4" value="false" />
+				<enum name="TextureWrap1" value="texture_clamp_repeat" />
+				<enum name="TextureWrap2" value="texture_clamp_repeat" />
+				<enum name="TextureWrap3" value="texture_clamp_repeat" />
+				<enum name="TextureWrap4" value="texture_clamp_repeat" />
+			</attributes>
+		</materials>
+	</node>
+
+	<node type="mesh">
+
+		<attributes>
+			<string name="Name" value="" />
+			<int name="Id" value="-1" />
+			<vector3d name="Position" value="42.392685, 54.034889, -80.052681" />
+			<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
+			<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
+			<bool name="Visible" value="true" />
+			<enum name="AutomaticCulling" value="box" />
+			<int name="DebugDataVisible" value="0" />
+			<bool name="IsDebugObject" value="false" />
+			<string name="Mesh" value="cellar.irrmesh" />
+			<bool name="ReadOnlyMaterials" value="false" />
+		</attributes>
+
+		<materials>
+			<attributes>
+				<enum name="Type" value="lightmap_m4" />
+				<color name="Ambient" value="ffffffff" />
+				<color name="Diffuse" value="ffffffff" />
+				<color name="Emissive" value="00000000" />
+				<color name="Specular" value="ffffffff" />
+				<float name="Shininess" value="0.750000" />
+				<float name="Param1" value="0.000000" />
+				<float name="Param2" value="0.000000" />
+				<texture name="Texture1" value="brownground_1-1.jpg" />
+				<texture name="Texture2" value="1.png" />
+				<texture name="Texture3" value="" />
+				<texture name="Texture4" value="" />
+				<bool name="Wireframe" value="false" />
+				<bool name="GouraudShading" value="true" />
+				<bool name="Lighting" value="false" />
+				<bool name="ZWriteEnable" value="true" />
+				<int name="ZBuffer" value="1" />
+				<bool name="BackfaceCulling" value="true" />
+				<bool name="FogEnable" value="false" />
+				<bool name="NormalizeNormals" value="false" />
+				<bool name="BilinearFilter1" value="true" />
+				<bool name="BilinearFilter2" value="true" />
+				<bool name="BilinearFilter3" value="true" />
+				<bool name="BilinearFilter4" value="true" />
+				<bool name="TrilinearFilter1" value="false" />
+				<bool name="TrilinearFilter2" value="false" />
+				<bool name="TrilinearFilter3" value="false" />
+				<bool name="TrilinearFilter4" value="false" />
+				<bool name="AnisotropicFilter1" value="false" />
+				<bool name="AnisotropicFilter2" value="false" />
+				<bool name="AnisotropicFilter3" value="false" />
+				<bool name="AnisotropicFilter4" value="false" />
+				<enum name="TextureWrap1" value="texture_clamp_repeat" />
+				<enum name="TextureWrap2" value="texture_clamp_repeat" />
+				<enum name="TextureWrap3" value="texture_clamp_repeat" />
+				<enum name="TextureWrap4" value="texture_clamp_repeat" />
+			</attributes>
+			<attributes>
+				<enum name="Type" value="lightmap_m4" />
+				<color name="Ambient" value="ffdfdfdf" />
+				<color name="Diffuse" value="ffdfdfdf" />
+				<color name="Emissive" value="00000000" />
+				<color name="Specular" value="ffffffff" />
+				<float name="Shininess" value="0.750000" />
+				<float name="Param1" value="0.000000" />
+				<float name="Param2" value="0.000000" />
+				<texture name="Texture1" value="crackedground_1-6.jpg" />
+				<texture name="Texture2" value="1.png" />
+				<texture name="Texture3" value="" />
+				<texture name="Texture4" value="" />
+				<bool name="Wireframe" value="false" />
+				<bool name="GouraudShading" value="true" />
+				<bool name="Lighting" value="false" />
+				<bool name="ZWriteEnable" value="true" />
+				<int name="ZBuffer" value="1" />
+				<bool name="BackfaceCulling" value="true" />
+				<bool name="FogEnable" value="false" />
+				<bool name="NormalizeNormals" value="false" />
+				<bool name="BilinearFilter1" value="true" />
+				<bool name="BilinearFilter2" value="true" />
+				<bool name="BilinearFilter3" value="true" />
+				<bool name="BilinearFilter4" value="true" />
+				<bool name="TrilinearFilter1" value="false" />
+				<bool name="TrilinearFilter2" value="false" />
+				<bool name="TrilinearFilter3" value="false" />
+				<bool name="TrilinearFilter4" value="false" />
+				<bool name="AnisotropicFilter1" value="false" />
+				<bool name="AnisotropicFilter2" value="false" />
+				<bool name="AnisotropicFilter3" value="false" />
+				<bool name="AnisotropicFilter4" value="false" />
+				<enum name="TextureWrap1" value="texture_clamp_repeat" />
+				<enum name="TextureWrap2" value="texture_clamp_repeat" />
+				<enum name="TextureWrap3" value="texture_clamp_repeat" />
+				<enum name="TextureWrap4" value="texture_clamp_repeat" />
+			</attributes>
+		</materials>
+	</node>
+
+	<node type="cube">
+
+		<attributes>
+			<string name="Name" value="JumpingCube" />
+			<int name="Id" value="-1" />
+			<vector3d name="Position" value="16.565477, 21.458202, 15.451395" />
+			<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
+			<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
+			<bool name="Visible" value="true" />
+			<enum name="AutomaticCulling" value="box" />
+			<int name="DebugDataVisible" value="0" />
+			<bool name="IsDebugObject" value="false" />
+			<float name="Size" value="10.000000" />
+		</attributes>
+
+		<materials>
+			<attributes>
+				<enum name="Type" value="solid" />
+				<color name="Ambient" value="ffffffff" />
+				<color name="Diffuse" value="ffffffff" />
+				<color name="Emissive" value="00000000" />
+				<color name="Specular" value="ffffffff" />
+				<float name="Shininess" value="0.000000" />
+				<float name="Param1" value="0.000000" />
+				<float name="Param2" value="0.000000" />
+				<texture name="Texture1" value="UVTransformTestImg.png" />
+				<texture name="Texture2" value="" />
+				<texture name="Texture3" value="" />
+				<texture name="Texture4" value="" />
+				<bool name="Wireframe" value="false" />
+				<bool name="GouraudShading" value="true" />
+				<bool name="Lighting" value="false" />
+				<bool name="ZWriteEnable" value="true" />
+				<int name="ZBuffer" value="1" />
+				<bool name="BackfaceCulling" value="true" />
+				<bool name="FogEnable" value="false" />
+				<bool name="NormalizeNormals" value="false" />
+				<bool name="BilinearFilter1" value="true" />
+				<bool name="BilinearFilter2" value="true" />
+				<bool name="BilinearFilter3" value="true" />
+				<bool name="BilinearFilter4" value="true" />
+				<bool name="TrilinearFilter1" value="false" />
+				<bool name="TrilinearFilter2" value="false" />
+				<bool name="TrilinearFilter3" value="false" />
+				<bool name="TrilinearFilter4" value="false" />
+				<bool name="AnisotropicFilter1" value="false" />
+				<bool name="AnisotropicFilter2" value="false" />
+				<bool name="AnisotropicFilter3" value="false" />
+				<bool name="AnisotropicFilter4" value="false" />
+				<enum name="TextureWrap1" value="texture_clamp_repeat" />
+				<enum name="TextureWrap2" value="texture_clamp_repeat" />
+				<enum name="TextureWrap3" value="texture_clamp_repeat" />
+				<enum name="TextureWrap4" value="texture_clamp_repeat" />
+			</attributes>
+		</materials>
+		<animators>
+			<attributes>
+				<string name="Type" value="followSpline" />
+				<float name="Speed" value="1.000000" />
+				<float name="Tightness" value="0.500000" />
+				<vector3d name="Point1" value="0.000000, 0.000000, 0.000000" />
+				<vector3d name="Point2" value="20.000000, 20.000000, 20.000000" />
+				<vector3d name="Point3" value="0.000000, 40.000000, 0.000000" />
+				<vector3d name="Point4" value="32.000000, 0.000000, 30.000000" />
+			</attributes>
+		</animators>
+	</node>
+
+	<node type="sphere">
+
+		<attributes>
+			<string name="Name" value="SillySphere" />
+			<int name="Id" value="-1" />
+			<vector3d name="Position" value="-52.105640, 35.426533, -57.016262" />
+			<vector3d name="Rotation" value="994.501831, 732.416443, 732.416443" />
+			<vector3d name="Scale" value="3.048931, 7.026268, 6.031934" />
+			<bool name="Visible" value="true" />
+			<enum name="AutomaticCulling" value="box" />
+			<int name="DebugDataVisible" value="0" />
+			<bool name="IsDebugObject" value="false" />
+			<float name="Radius" value="5.000000" />
+			<int name="PolyCountX" value="16" />
+			<int name="PolyCountY" value="16" />
+		</attributes>
+
+		<materials>
+			<attributes>
+				<enum name="Type" value="solid" />
+				<color name="Ambient" value="ffffffff" />
+				<color name="Diffuse" value="ffffffff" />
+				<color name="Emissive" value="00000000" />
+				<color name="Specular" value="ffffffff" />
+				<float name="Shininess" value="0.000000" />
+				<float name="Param1" value="0.000000" />
+				<float name="Param2" value="0.000000" />
+				<texture name="Texture1" value="UVTransformTestImg.png" />
+				<texture name="Texture2" value="" />
+				<texture name="Texture3" value="" />
+				<texture name="Texture4" value="" />
+				<bool name="Wireframe" value="false" />
+				<bool name="GouraudShading" value="true" />
+				<bool name="Lighting" value="false" />
+				<bool name="ZWriteEnable" value="true" />
+				<int name="ZBuffer" value="1" />
+				<bool name="BackfaceCulling" value="true" />
+				<bool name="FogEnable" value="false" />
+				<bool name="NormalizeNormals" value="false" />
+				<bool name="BilinearFilter1" value="true" />
+				<bool name="BilinearFilter2" value="true" />
+				<bool name="BilinearFilter3" value="true" />
+				<bool name="BilinearFilter4" value="true" />
+				<bool name="TrilinearFilter1" value="false" />
+				<bool name="TrilinearFilter2" value="false" />
+				<bool name="TrilinearFilter3" value="false" />
+				<bool name="TrilinearFilter4" value="false" />
+				<bool name="AnisotropicFilter1" value="false" />
+				<bool name="AnisotropicFilter2" value="false" />
+				<bool name="AnisotropicFilter3" value="false" />
+				<bool name="AnisotropicFilter4" value="false" />
+				<enum name="TextureWrap1" value="texture_clamp_repeat" />
+				<enum name="TextureWrap2" value="texture_clamp_repeat" />
+				<enum name="TextureWrap3" value="texture_clamp_repeat" />
+				<enum name="TextureWrap4" value="texture_clamp_repeat" />
+			</attributes>
+		</materials>
+<!--		<animators>-->
+<!--			<attributes>-->
+<!--				<string name="Type" value="rotation" />-->
+<!--				<vector3d name="Rotation" value="0.300000, 0.300000, 0.300000" />-->
+<!--			</attributes>-->
+<!--		</animators>-->
+	</node>
+
+</irr_scene>
+

BIN
test/models/IRR/scenegraphAnimMod_UTF16LE.irr


BIN
test/models/IRR/scenegraphAnim_UTF16LE.irr


+ 34 - 0
test/unit/utImporter.cpp

@@ -361,3 +361,37 @@ TEST_F(ImporterTest, unexpectedException) {
         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 {
 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;
         const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure);
         EXPECT_NE(scene, nullptr);
@@ -105,16 +105,19 @@ public:
 
         aiColor3D spec_color = { 0, 0, 0 };
         ai_real glossiness = ai_real(0.5);
-        if (spec_gloss) {
+        if (spec) {
             EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color));
             constexpr ai_real spec_val(0.20000000298023225); // From the file
             EXPECT_EQ(spec_val, spec_color.r);
             EXPECT_EQ(spec_val, spec_color.g);
             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(ai_real(1.0), glossiness);
         } else {
-            EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color));
             EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness));
         }
 
@@ -143,7 +146,7 @@ public:
 };
 
 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) {
@@ -151,7 +154,7 @@ TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) {
 }
 
 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) {
@@ -223,13 +226,16 @@ TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossi
     // Export with specular glossiness disabled
     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
     ExportProperties props;
     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));
 
     // 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) {