Browse Source

Merge branch 'master' into migenius-fix-doublefree

Kim Kulling 5 years ago
parent
commit
571d64e3c1

+ 6 - 1
CMakeLists.txt

@@ -253,7 +253,7 @@ ELSEIF(MSVC)
   IF(MSVC12)
     ADD_COMPILE_OPTIONS(/wd4351)
   ENDIF()
-  SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob2 /DEBUG:FULL /Zi")
+  SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob2 /Zi")
 ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
   IF(NOT HUNTER_ENABLED)
     SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}")
@@ -391,6 +391,11 @@ IF(HUNTER_ENABLED)
   )
 ELSE(HUNTER_ENABLED)
   # cmake configuration files
+  if(${BUILD_SHARED_LIBS})
+    set(BUILD_LIB_TYPE SHARED)
+  else()
+    set(BUILD_LIB_TYPE STATIC)
+  endif()
   CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE)
   CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake" @ONLY IMMEDIATE)
   IF (is_multi_config)

+ 4 - 1
appveyor.yml

@@ -14,6 +14,7 @@ matrix:
   fast_finish: true
     
 image:
+  - Visual Studio 2013
   - Visual Studio 2015
   - Visual Studio 2017
   - Visual Studio 2019
@@ -29,11 +30,13 @@ install:
   - set PATH=C:\Ruby24-x64\bin;%PATH%
   - set CMAKE_DEFINES -DASSIMP_WERROR=ON
   - if [%COMPILER%]==[MinGW] set PATH=C:\MinGW\bin;%PATH%
+  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set CMAKE_GENERATOR_NAME=Visual Studio 12 2013
   - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
   - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017
   - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" set CMAKE_GENERATOR_NAME=Visual Studio 16 2019
   - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" -A %platform% .
-  # Rename sh.exe as sh.exe in PATH interferes with MinGW
+  # Rename sh.exe as sh.exe in PATH interferes with MinGW  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
+
   - rename "C:\Program Files\Git\usr\bin\sh.exe" "sh2.exe"
   - set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5"
   - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe

+ 7 - 4
assimpTargets-debug.cmake.in

@@ -63,10 +63,13 @@ if(MSVC)
 else()
   set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" )
   if(ASSIMP_BUILD_SHARED_LIBS)
-    if(APPLE)
-        set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@")
-    else(APPLE)
-        set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
+    if(WIN32)
+      # Handle MinGW compiler.
+      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
+    elseif(APPLE)
+      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@")
+    else()
+      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
     endif()
     set_target_properties(assimp::assimp PROPERTIES
       IMPORTED_SONAME_DEBUG "${sharedLibraryName}"

+ 7 - 4
assimpTargets-release.cmake.in

@@ -63,10 +63,13 @@ if(MSVC)
 else()
   set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" )
   if(ASSIMP_BUILD_SHARED_LIBS)
-    if(APPLE)
-        set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@")
-    else(APPLE)
-        set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
+    if(WIN32)
+      # Handle MinGW compiler.
+      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
+    elseif(APPLE)
+      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@")
+    else()
+      set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
     endif()
     set_target_properties(assimp::assimp PROPERTIES
       IMPORTED_SONAME_RELEASE "${sharedLibraryName}"

+ 4 - 5
assimpTargets.cmake.in

@@ -5,6 +5,9 @@ if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.5)
 endif()
 cmake_policy(PUSH)
 cmake_policy(VERSION 2.6)
+# Required for the evaluation of "if(@BUILD_SHARED_LIBS@)" below to function
+cmake_policy(SET CMP0012 NEW)
+
 #----------------------------------------------------------------
 # Generated CMake target import file.
 #----------------------------------------------------------------
@@ -51,11 +54,7 @@ if(_IMPORT_PREFIX STREQUAL "/")
 endif()
 
 # Create imported target assimp::assimp
-if(@BUILD_SHARED_LIBS@)
-  add_library(assimp::assimp SHARED IMPORTED)
-else()
-  add_library(assimp::assimp STATIC IMPORTED)
-endif()
+add_library(assimp::assimp @BUILD_LIB_TYPE@ IMPORTED)
 
 set_target_properties(assimp::assimp PROPERTIES
   COMPATIBLE_INTERFACE_STRING "assimp_MAJOR_VERSION"

+ 94 - 64
code/Collada/ColladaExporter.cpp

@@ -92,6 +92,36 @@ void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* p
 
 } // end of namespace Assimp
 
+// ------------------------------------------------------------------------------------------------
+// Encodes a string into a valid XML ID using the xsd:ID schema qualifications.
+static const std::string XMLIDEncode(const std::string& name) {
+    const char XML_ID_CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.";
+    const unsigned int XML_ID_CHARS_COUNT = sizeof(XML_ID_CHARS) / sizeof(char);
+
+    if (name.length() == 0) {
+        return name;
+    }
+
+    std::stringstream idEncoded;
+
+    // xsd:ID must start with letter or underscore
+    if (!((name[0] >= 'A' && name[0] <= 'z') || name[0] == '_')) {
+        idEncoded << '_';
+    }
+
+    for (std::string::const_iterator it = name.begin(); it != name.end(); ++it) {
+        // xsd:ID can only contain letters, digits, underscores, hyphens and periods
+        if (strchr(XML_ID_CHARS, *it) != nullptr) {
+            idEncoded << *it;
+        } else {
+            // Select placeholder character based on invalid character to prevent name collisions 
+            idEncoded << XML_ID_CHARS[(*it) % XML_ID_CHARS_COUNT];
+        }
+    }
+
+    return idEncoded.str();
+}
+
 // ------------------------------------------------------------------------------------------------
 // Constructor for a specific scene to export
 ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) 
@@ -146,7 +176,7 @@ void ColladaExporter::WriteFile() {
     // useless Collada fu at the end, just in case we haven't had enough indirections, yet.
     mOutput << startstr << "<scene>" << endstr;
     PushTag();
-    mOutput << startstr << "<instance_visual_scene url=\"#" + XMLEscape(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr;
+    mOutput << startstr << "<instance_visual_scene url=\"#" + XMLIDEncode(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr;
     PopTag();
     mOutput << startstr << "</scene>" << endstr;
     PopTag();
@@ -356,9 +386,10 @@ void ColladaExporter::WriteCamerasLibrary() {
 void ColladaExporter::WriteCamera(size_t pIndex){
 
     const aiCamera *cam = mScene->mCameras[pIndex];
-    const std::string idstrEscaped = XMLEscape(cam->mName.C_Str());
+    const std::string cameraName = XMLEscape(cam->mName.C_Str());
+    const std::string cameraId = XMLIDEncode(cam->mName.C_Str());
 
-    mOutput << startstr << "<camera id=\"" << idstrEscaped << "-camera\" name=\"" << idstrEscaped << "_name\" >" << endstr;
+    mOutput << startstr << "<camera id=\"" << cameraId << "-camera\" name=\"" << cameraName << "\" >" << endstr;
     PushTag();
     mOutput << startstr << "<optics>" << endstr;
     PushTag();
@@ -412,10 +443,11 @@ void ColladaExporter::WriteLightsLibrary() {
 void ColladaExporter::WriteLight(size_t pIndex){
 
     const aiLight *light = mScene->mLights[pIndex];
-    const std::string idstrEscaped = XMLEscape(light->mName.C_Str());
+    const std::string lightName = XMLEscape(light->mName.C_Str());
+    const std::string lightId = XMLIDEncode(light->mName.C_Str());
 
-    mOutput << startstr << "<light id=\"" << idstrEscaped << "-light\" name=\""
-            << idstrEscaped << "_name\" >" << endstr;
+    mOutput << startstr << "<light id=\"" << lightId << "-light\" name=\""
+            << lightName << "\" >" << endstr;
     PushTag();
     mOutput << startstr << "<technique_common>" << endstr;
     PushTag();
@@ -586,7 +618,7 @@ static bool isalnum_C(char c) {
 void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd) {
   if( !pSurface.texture.empty() )
   {
-    mOutput << startstr << "<image id=\"" << XMLEscape(pNameAdd) << "\">" << endstr;
+    mOutput << startstr << "<image id=\"" << XMLIDEncode(pNameAdd) << "\">" << endstr;
     PushTag();
     mOutput << startstr << "<init_from>";
 
@@ -619,7 +651,7 @@ void ColladaExporter::WriteTextureColorEntry( const Surface& pSurface, const std
     }
     else
     {
-      mOutput << startstr << "<texture texture=\"" << XMLEscape(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
+      mOutput << startstr << "<texture texture=\"" << XMLIDEncode(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
     }
     PopTag();
     mOutput << startstr << "</" << pTypeName << ">" << endstr;
@@ -633,21 +665,21 @@ void ColladaExporter::WriteTextureParamEntry( const Surface& pSurface, const std
   // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture
   if( !pSurface.texture.empty() )
   {
-    mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-surface\">" << endstr;
+    mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface\">" << endstr;
     PushTag();
     mOutput << startstr << "<surface type=\"2D\">" << endstr;
     PushTag();
-    mOutput << startstr << "<init_from>" << XMLEscape(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr;
+    mOutput << startstr << "<init_from>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr;
     PopTag();
     mOutput << startstr << "</surface>" << endstr;
     PopTag();
     mOutput << startstr << "</newparam>" << endstr;
 
-    mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-sampler\">" << endstr;
+    mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-sampler\">" << endstr;
     PushTag();
     mOutput << startstr << "<sampler2D>" << endstr;
     PushTag();
-    mOutput << startstr << "<source>" << XMLEscape(pMatName) << "-" << pTypeName << "-surface</source>" << endstr;
+    mOutput << startstr << "<source>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface</source>" << endstr;
     PopTag();
     mOutput << startstr << "</sampler2D>" << endstr;
     PopTag();
@@ -699,11 +731,6 @@ void ColladaExporter::WriteMaterials()
         materials[a].name = std::string(name.C_Str()) + to_string(materialCountWithThisName);
       }
     }
-    for( std::string::iterator it = materials[a].name.begin(); it != materials[a].name.end(); ++it ) {
-      if( !isalnum_C( *it ) ) {
-        *it = '_';
-      }
-    }
 
     aiShadingMode shading = aiShadingMode_Flat;
     materials[a].shading_model = "phong";
@@ -768,7 +795,7 @@ void ColladaExporter::WriteMaterials()
     {
       const Material& mat = *it;
       // this is so ridiculous it must be right
-      mOutput << startstr << "<effect id=\"" << XMLEscape(mat.name) << "-fx\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
+      mOutput << startstr << "<effect id=\"" << XMLIDEncode(mat.name) << "-fx\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
       PushTag();
       mOutput << startstr << "<profile_COMMON>" << endstr;
       PushTag();
@@ -819,9 +846,9 @@ void ColladaExporter::WriteMaterials()
     for( std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it )
     {
       const Material& mat = *it;
-      mOutput << startstr << "<material id=\"" << XMLEscape(mat.name) << "\" name=\"" << mat.name << "\">" << endstr;
+      mOutput << startstr << "<material id=\"" << XMLIDEncode(mat.name) << "\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
       PushTag();
-      mOutput << startstr << "<instance_effect url=\"#" << XMLEscape(mat.name) << "-fx\"/>" << endstr;
+      mOutput << startstr << "<instance_effect url=\"#" << XMLIDEncode(mat.name) << "-fx\"/>" << endstr;
       PopTag();
       mOutput << startstr << "</material>" << endstr;
     }
@@ -850,8 +877,8 @@ void ColladaExporter::WriteControllerLibrary()
 void ColladaExporter::WriteController( size_t pIndex)
 {
     const aiMesh* mesh = mScene->mMeshes[pIndex];
-    const std::string idstr = GetMeshId( pIndex);
-    const std::string idstrEscaped = XMLEscape(idstr);
+    const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
+    const std::string idstrEscaped = XMLIDEncode(idstr);
 
     if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
         return;
@@ -886,7 +913,7 @@ void ColladaExporter::WriteController( size_t pIndex)
     mOutput << startstr << "<Name_array id=\"" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
 
     for( size_t i = 0; i < mesh->mNumBones; ++i )
-        mOutput << XMLEscape(mesh->mBones[i]->mName.C_Str()) << " ";
+        mOutput << XMLIDEncode(mesh->mBones[i]->mName.C_Str()) << " ";
 
     mOutput << "</Name_array>" << endstr;
 
@@ -1021,14 +1048,15 @@ void ColladaExporter::WriteGeometryLibrary()
 void ColladaExporter::WriteGeometry( size_t pIndex)
 {
     const aiMesh* mesh = mScene->mMeshes[pIndex];
-    const std::string idstr = GetMeshId( pIndex);
-    const std::string idstrEscaped = XMLEscape(idstr);
+    const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
+    const std::string geometryName = XMLEscape(idstr);
+    const std::string geometryId = XMLIDEncode(idstr);
 
     if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
         return;
 
     // opening tag
-    mOutput << startstr << "<geometry id=\"" << idstrEscaped << "\" name=\"" << idstrEscaped << "_name\" >" << endstr;
+    mOutput << startstr << "<geometry id=\"" << geometryId << "\" name=\"" << geometryName << "\" >" << endstr;
     PushTag();
 
     mOutput << startstr << "<mesh>" << endstr;
@@ -1059,9 +1087,9 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
 
     // assemble vertex structure
     // Only write input for POSITION since we will write other as shared inputs in polygon definition
-    mOutput << startstr << "<vertices id=\"" << idstrEscaped << "-vertices" << "\">" << endstr;
+    mOutput << startstr << "<vertices id=\"" << geometryId << "-vertices" << "\">" << endstr;
     PushTag();
-    mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << idstrEscaped << "-positions\" />" << endstr;
+    mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << geometryId << "-positions\" />" << endstr;
     PopTag();
     mOutput << startstr << "</vertices>" << endstr;
 
@@ -1079,18 +1107,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
     {
         mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr;
         PushTag();
-        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstrEscaped << "-vertices\" />" << endstr;
+        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
         if( mesh->HasNormals() )
-            mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << idstrEscaped << "-normals\" />" << endstr;
+            mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
         {
             if( mesh->HasTextureCoords(static_cast<unsigned int>(a)) )
-                mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << idstrEscaped << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
         {
             if( mesh->HasVertexColors(static_cast<unsigned int>(a) ) )
-                mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << idstrEscaped << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
 
         mOutput << startstr << "<p>";
@@ -1113,18 +1141,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
     {
         mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr;
         PushTag();
-        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstrEscaped << "-vertices\" />" << endstr;
+        mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
         if( mesh->HasNormals() )
-            mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << idstrEscaped << "-normals\" />" << endstr;
+            mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
         {
             if( mesh->HasTextureCoords(static_cast<unsigned int>(a)) )
-                mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << idstrEscaped << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
         {
             if( mesh->HasVertexColors(static_cast<unsigned int>(a) ) )
-                mOutput << startstr << "<input offset=\"0\" semantic=\"COLOR\" source=\"#" << idstrEscaped << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
+                mOutput << startstr << "<input offset=\"0\" semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" " << "set=\"" << a << "\""  << " />" << endstr;
         }
 
         mOutput << startstr << "<vcount>";
@@ -1173,13 +1201,13 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
             return;
     }
 
-    std::string arrayId = pIdString + "-array";
+    std::string arrayId = XMLIDEncode(pIdString) + "-array";
 
-    mOutput << startstr << "<source id=\"" << XMLEscape(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr;
+    mOutput << startstr << "<source id=\"" << XMLIDEncode(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr;
     PushTag();
 
     // source array
-    mOutput << startstr << "<float_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
+    mOutput << startstr << "<float_array id=\"" << arrayId << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
     PushTag();
 
     if( pType == FloatType_TexCoord2 )
@@ -1265,11 +1293,12 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
 // Writes the scene library
 void ColladaExporter::WriteSceneLibrary()
 {
-    const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str());
+    const std::string sceneName = XMLEscape(mScene->mRootNode->mName.C_Str());
+    const std::string sceneId = XMLIDEncode(mScene->mRootNode->mName.C_Str());
 
     mOutput << startstr << "<library_visual_scenes>" << endstr;
     PushTag();
-    mOutput << startstr << "<visual_scene id=\"" + scene_name_escaped + "\" name=\"" + scene_name_escaped + "\">" << endstr;
+    mOutput << startstr << "<visual_scene id=\"" + sceneId + "\" name=\"" + sceneName + "\">" << endstr;
     PushTag();
 
     // start recursive write at the root node
@@ -1300,7 +1329,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 		idstr = idstr + ending;
 	}
 
-	const std::string idstrEscaped = XMLEscape(idstr);
+	const std::string idstrEscaped = XMLIDEncode(idstr);
 	
 	mOutput << startstr << "<animation id=\"" + idstrEscaped + "\" name=\"" + animation_name_escaped + "\">" << endstr;
 	PushTag();
@@ -1372,13 +1401,13 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 			}
 			
 			const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-interpolation");
-			std::string arrayId = node_idstr + "-array";
+            std::string arrayId = XMLIDEncode(node_idstr) + "-array";
 			
-			mOutput << startstr << "<source id=\"" << XMLEscape(node_idstr) << "\">" << endstr;
+			mOutput << startstr << "<source id=\"" << XMLIDEncode(node_idstr) << "\">" << endstr;
 			PushTag();
 			
 			// source array
-			mOutput << startstr << "<Name_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\"> ";
+			mOutput << startstr << "<Name_array id=\"" << arrayId << "\" count=\"" << names.size() << "\"> ";
 			for( size_t a = 0; a < names.size(); ++a ) {
 				mOutput << names[a] << " ";
             }
@@ -1387,7 +1416,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 			mOutput << startstr << "<technique_common>" << endstr;
 			PushTag();
 
-			mOutput << startstr << "<accessor source=\"#" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr;
+			mOutput << startstr << "<accessor source=\"#" << arrayId << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr;
 			PushTag();
 			
 			mOutput << startstr << "<param name=\"INTERPOLATION\" type=\"name\"></param>" << endstr;
@@ -1409,12 +1438,12 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 		{
 		// samplers
 			const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler");
-			mOutput << startstr << "<sampler id=\"" << XMLEscape(node_idstr) << "\">" << endstr;
+			mOutput << startstr << "<sampler id=\"" << XMLIDEncode(node_idstr) << "\">" << endstr;
 			PushTag();
 			
-			mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr;
-			mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr;
-			mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr;
+			mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr;
+			mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr;
+			mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr;
 			
 			PopTag();
 			mOutput << startstr << "</sampler>" << endstr;
@@ -1426,7 +1455,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 		
 		{
 		// channels
-			mOutput << startstr << "<channel source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLEscape(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
+			mOutput << startstr << "<channel source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLIDEncode(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
 		}
 	}
 	
@@ -1437,8 +1466,6 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
 // ------------------------------------------------------------------------------------------------
 void ColladaExporter::WriteAnimationsLibrary()
 {
-	const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str());
-	
 	if ( mScene->mNumAnimations > 0 ) {
 		mOutput << startstr << "<library_animations>" << endstr;
 		PushTag();
@@ -1546,16 +1573,17 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
         }
     }
 
-    const std::string node_name_escaped = XMLEscape(pNode->mName.data);
+    const std::string node_id = XMLIDEncode(pNode->mName.data);
+    const std::string node_name = XMLEscape(pNode->mName.data);
 	mOutput << startstr << "<node ";
 	if(is_skeleton_root) {
-		mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"" : "") ; // For now, only support one skeleton in a scene.
-		mFoundSkeletonRootNodeID = node_name_escaped;
+		mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id +"\"" : "") ; // For now, only support one skeleton in a scene.
+		mFoundSkeletonRootNodeID = node_id;
 	} else {
-		mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"": "") ;
+		mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id +"\"": "") ;
 	}
 	
-    mOutput << " name=\"" << node_name_escaped
+    mOutput << " name=\"" << node_name
             << "\" type=\"" << node_type
             << "\">" << endstr;
     PushTag();
@@ -1594,14 +1622,14 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
         //check if it is a camera node
         for(size_t i=0; i<mScene->mNumCameras; i++){
             if(mScene->mCameras[i]->mName == pNode->mName){
-                mOutput << startstr <<"<instance_camera url=\"#" << node_name_escaped << "-camera\"/>" << endstr;
+                mOutput << startstr <<"<instance_camera url=\"#" << node_id << "-camera\"/>" << endstr;
                 break;
             }
         }
         //check if it is a light node
         for(size_t i=0; i<mScene->mNumLights; i++){
             if(mScene->mLights[i]->mName == pNode->mName){
-                mOutput << startstr <<"<instance_light url=\"#" << node_name_escaped << "-light\"/>" << endstr;
+                mOutput << startstr <<"<instance_light url=\"#" << node_id << "-light\"/>" << endstr;
                 break;
             }
         }
@@ -1615,15 +1643,17 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
         if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
             continue;
 
+        const std::string meshName = mesh->mName.length == 0 ? GetMeshId(pNode->mMeshes[a]) : mesh->mName.C_Str();
+
         if( mesh->mNumBones == 0 )
         {
-            mOutput << startstr << "<instance_geometry url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "\">" << endstr;
+            mOutput << startstr << "<instance_geometry url=\"#" << XMLIDEncode(meshName) << "\">" << endstr;
             PushTag();
         }
         else
         {
             mOutput << startstr
-                    << "<instance_controller url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "-skin\">"
+                    << "<instance_controller url=\"#" << XMLIDEncode(meshName) << "-skin\">"
                     << endstr;
             PushTag();
 
@@ -1631,7 +1661,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
 			// use the first bone to find skeleton root
 			const aiNode * skeletonRootBoneNode = findSkeletonRootNode( pScene, mesh );
 			if ( skeletonRootBoneNode ) {
-				mFoundSkeletonRootNodeID = XMLEscape( skeletonRootBoneNode->mName.C_Str() );
+				mFoundSkeletonRootNodeID = XMLIDEncode( skeletonRootBoneNode->mName.C_Str() );
 			}
             mOutput << startstr << "<skeleton>#" << mFoundSkeletonRootNodeID << "</skeleton>" << endstr;
         }
@@ -1639,7 +1669,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
         PushTag();
         mOutput << startstr << "<technique_common>" << endstr;
         PushTag();
-        mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLEscape(materials[mesh->mMaterialIndex].name) << "\">" << endstr;
+        mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLIDEncode(materials[mesh->mMaterialIndex].name) << "\">" << endstr;
         PushTag();
         for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
         {

+ 1 - 1
code/Collada/ColladaHelper.h

@@ -583,7 +583,7 @@ struct Image
     /** Embedded image data */
     std::vector<uint8_t> mImageData;
 
-    /** File format hint ofembedded image data */
+    /** File format hint of embedded image data */
     std::string mEmbeddedFormat;
 };
 

+ 3 - 53
code/Collada/ColladaLoader.cpp

@@ -1735,6 +1735,7 @@ void ColladaLoader::BuildMaterials(ColladaParser& pParser, aiScene* /*pScene*/)
 
 // ------------------------------------------------------------------------------------------------
 // Resolves the texture name for the given effect texture entry
+// and loads the texture data 
 aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParser,
     const Collada::Effect& pEffect, const std::string& pName)
 {
@@ -1762,7 +1763,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse
 
         //set default texture file name
         result.Set(name + ".jpg");
-        ConvertPath(result);
+        ColladaParser::UriDecodePath(result);
         return result;
     }
 
@@ -1781,7 +1782,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse
 
 
         // setup format hint
-        if (imIt->second.mEmbeddedFormat.length() > 3) {
+        if (imIt->second.mEmbeddedFormat.length() >= HINTMAXTEXTURELEN) {
             ASSIMP_LOG_WARN("Collada: texture format hint is too long, truncating to 3 characters");
         }
         strncpy(tex->achFormatHint, imIt->second.mEmbeddedFormat.c_str(), 3);
@@ -1802,61 +1803,10 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse
         }
 
         result.Set(imIt->second.mFileName);
-        ConvertPath(result);
     }
     return result;
 }
 
-// ------------------------------------------------------------------------------------------------
-// Convert a path read from a collada file to the usual representation
-void ColladaLoader::ConvertPath(aiString& ss)
-{
-    // TODO: collada spec, p 22. Handle URI correctly.
-    // For the moment we're just stripping the file:// away to make it work.
-    // Windows doesn't seem to be able to find stuff like
-    // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
-    if (0 == strncmp(ss.data, "file://", 7))
-    {
-        ss.length -= 7;
-        memmove(ss.data, ss.data + 7, ss.length);
-        ss.data[ss.length] = '\0';
-    }
-
-    // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes...
-    // I need to filter it without destroying linux paths starting with "/somewhere"
-#if defined( _MSC_VER )
-    if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') {
-#else
-    if (ss.data[0] == '/' && isalpha(ss.data[1]) && ss.data[2] == ':') {
-#endif
-        --ss.length;
-        ::memmove(ss.data, ss.data + 1, ss.length);
-        ss.data[ss.length] = 0;
-    }
-
-    // find and convert all %xy special chars
-    char* out = ss.data;
-    for (const char* it = ss.data; it != ss.data + ss.length; /**/)
-    {
-        if (*it == '%' && (it + 3) < ss.data + ss.length)
-        {
-            // separate the number to avoid dragging in chars from behind into the parsing
-            char mychar[3] = { it[1], it[2], 0 };
-            size_t nbr = strtoul16(mychar);
-            it += 3;
-            *out++ = (char)(nbr & 0xFF);
-        }
-        else
-        {
-            *out++ = *it++;
-        }
-    }
-
-    // adjust length and terminator of the shortened string
-    *out = 0;
-    ss.length = (ptrdiff_t)(out - ss.data);
-}
-
 // ------------------------------------------------------------------------------------------------
 // Reads a float value from an accessor and its data array.
 ai_real ColladaLoader::ReadFloat(const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const

+ 1 - 4
code/Collada/ColladaLoader.h

@@ -94,7 +94,7 @@ public:
 public:
     /** Returns whether the class can handle the format of the given file.
      * See BaseImporter::CanRead() for details. */
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override;
+    bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override;
 
 protected:
     /** Return importer meta information.
@@ -184,9 +184,6 @@ protected:
     aiString FindFilenameForEffectTexture( const ColladaParser& pParser,
         const Collada::Effect& pEffect, const std::string& pName);
 
-    /** Converts a path read from a collada file to the usual representation */
-    void ConvertPath( aiString& ss);
-
     /** Reads a float value from an accessor and its data array.
      * @param pAccessor The accessor to use for reading
      * @param pData The data array to read from

+ 68 - 4
code/Collada/ColladaParser.cpp

@@ -183,13 +183,67 @@ std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) {
                 if (filepath == nullptr)
                     return std::string();
 
-                return std::string(filepath);
+                aiString ai_str(filepath);
+                UriDecodePath(ai_str);
+
+                return std::string(ai_str.C_Str());
             }
         }
     }
     return std::string();
 }
 
+// ------------------------------------------------------------------------------------------------
+// Convert a path read from a collada file to the usual representation
+void ColladaParser::UriDecodePath(aiString& ss)
+{
+    // TODO: collada spec, p 22. Handle URI correctly.
+    // For the moment we're just stripping the file:// away to make it work.
+    // Windows doesn't seem to be able to find stuff like
+    // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
+    if (0 == strncmp(ss.data, "file://", 7))
+    {
+        ss.length -= 7;
+        memmove(ss.data, ss.data + 7, ss.length);
+        ss.data[ss.length] = '\0';
+    }
+
+    // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes...
+    // I need to filter it without destroying linux paths starting with "/somewhere"
+#if defined( _MSC_VER )
+    if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') {
+#else
+    if (ss.data[0] == '/' && isalpha(ss.data[1]) && ss.data[2] == ':') {
+#endif
+        --ss.length;
+        ::memmove(ss.data, ss.data + 1, ss.length);
+        ss.data[ss.length] = 0;
+    }
+
+    // find and convert all %xy special chars
+    char* out = ss.data;
+    for (const char* it = ss.data; it != ss.data + ss.length; /**/)
+    {
+        if (*it == '%' && (it + 3) < ss.data + ss.length)
+        {
+            // separate the number to avoid dragging in chars from behind into the parsing
+            char mychar[3] = { it[1], it[2], 0 };
+            size_t nbr = strtoul16(mychar);
+            it += 3;
+            *out++ = (char)(nbr & 0xFF);
+        }
+        else
+        {
+            *out++ = *it++;
+        }
+    }
+
+    // adjust length and terminator of the shortened string
+    *out = 0;
+    ai_assert(out > ss.data);
+    ss.length = static_cast<ai_uint32>(out - ss.data);
+}
+
 // ------------------------------------------------------------------------------------------------
 // Read bool from text contents of current element
 bool ColladaParser::ReadBoolFromTextContent()
@@ -1120,7 +1174,12 @@ void ColladaParser::ReadImage(Collada::Image& pImage)
                     if (!mReader->isEmptyElement()) {
                         // element content is filename - hopefully
                         const char* sz = TestTextContent();
-                        if (sz)pImage.mFileName = sz;
+                        if (sz)
+                        {
+                            aiString filepath(sz);
+                            UriDecodePath(filepath);
+                            pImage.mFileName = filepath.C_Str();
+                        }
                         TestClosing("init_from");
                     }
                     if (!pImage.mFileName.length()) {
@@ -1153,7 +1212,12 @@ void ColladaParser::ReadImage(Collada::Image& pImage)
                 {
                     // element content is filename - hopefully
                     const char* sz = TestTextContent();
-                    if (sz)pImage.mFileName = sz;
+                    if (sz)
+                    {
+                        aiString filepath(sz);
+                        UriDecodePath(filepath);
+                        pImage.mFileName = filepath.C_Str();
+                    }
                     TestClosing("ref");
                 }
                 else if (IsElement("hex") && !pImage.mFileName.length())
@@ -3056,7 +3120,7 @@ void ColladaParser::ReadMaterialVertexInputBinding(Collada::SemanticMappingTable
     }
 }
 
-void Assimp::ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem& zip_archive)
+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) {

+ 4 - 1
code/Collada/ColladaParser.h

@@ -66,12 +66,15 @@ namespace Assimp
     {
         friend class ColladaLoader;
 
+        /** Converts a path read from a collada file to the usual representation */
+        static void UriDecodePath(aiString& ss);
+
     protected:
         /** Map for generic metadata as aiString */
         typedef std::map<std::string, aiString> StringMetaData;
 
         /** Constructor from XML file */
-        ColladaParser( IOSystem* pIOHandler, const std::string& pFile);
+        ColladaParser(IOSystem* pIOHandler, const std::string& pFile);
 
         /** Destructor */
         ~ColladaParser();

+ 14 - 1
code/Common/BaseImporter.cpp

@@ -67,7 +67,20 @@ using namespace Assimp;
 // Constructor to be privately used by Importer
 BaseImporter::BaseImporter() AI_NO_EXCEPT
 : m_progress() {
-    // nothing to do here
+    /**
+    * Assimp Importer
+    * unit conversions available
+    * if you need another measurment unit add it below.
+    * it's currently defined in assimp that we prefer meters.
+    *
+    * NOTE: Initialised here rather than in the header file
+    * to workaround a VS2013 bug with brace initialisers
+    * */
+    importerUnits[ImporterUnits::M] = 1.0;
+    importerUnits[ImporterUnits::CM] = 0.01;
+    importerUnits[ImporterUnits::MM] = 0.001;
+    importerUnits[ImporterUnits::INCHES] = 0.0254;
+    importerUnits[ImporterUnits::FEET] = 0.3048;
 }
 
 // ------------------------------------------------------------------------------------------------

+ 5 - 9
code/Common/Version.cpp

@@ -46,8 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/scene.h>
 #include "ScenePrivate.h"
 
-static const unsigned int MajorVersion = 5;
-static const unsigned int MinorVersion = 0;
+#include "revision.h"
 
 // --------------------------------------------------------------------------------
 // Legal information string - don't remove this.
@@ -56,9 +55,9 @@ static const char* LEGAL_INFORMATION =
 "Open Asset Import Library (Assimp).\n"
 "A free C/C++ library to import various 3D file formats into applications\n\n"
 
-"(c) 2008-2017, assimp team\n"
+"(c) 2006-2019, assimp team\n"
 "License under the terms and conditions of the 3-clause BSD license\n"
-"http://assimp.sourceforge.net\n"
+"http://assimp.org\n"
 ;
 
 // ------------------------------------------------------------------------------------------------
@@ -70,13 +69,13 @@ ASSIMP_API const char*  aiGetLegalString  ()    {
 // ------------------------------------------------------------------------------------------------
 // Get Assimp minor version
 ASSIMP_API unsigned int aiGetVersionMinor ()    {
-    return MinorVersion;
+    return VER_MINOR;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Get Assimp major version
 ASSIMP_API unsigned int aiGetVersionMajor ()    {
-    return MajorVersion;
+    return VER_MAJOR;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -104,9 +103,6 @@ ASSIMP_API unsigned int aiGetCompileFlags ()    {
     return flags;
 }
 
-// include current build revision, which is even updated from time to time -- :-)
-#include "revision.h"
-
 // ------------------------------------------------------------------------------------------------
 ASSIMP_API unsigned int aiGetVersionRevision() {
     return GitVersion;

+ 1 - 1
code/FBX/FBXConverter.cpp

@@ -77,7 +77,7 @@ namespace Assimp {
 
 #define MAGIC_NODE_TAG "_$AssimpFbx$"
 
-#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
+#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
 
         FBXConverter::FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones )
         : defaultMaterialIndex()

+ 18 - 0
code/glTF/glTFImporter.cpp

@@ -170,6 +170,8 @@ void glTFImporter::ImportMaterials(glTF::Asset& r) {
 
     if (mScene->mNumMaterials == 0) {
         mScene->mNumMaterials = 1;
+        // Delete the array of length zero created above.
+        delete[] mScene->mMaterials;
         mScene->mMaterials = new aiMaterial*[1];
         mScene->mMaterials[0] = new aiMaterial();
     }
@@ -330,6 +332,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
 
                     case PrimitiveMode_LINES: {
                         nFaces = count / 2;
+                        if (nFaces * 2 != count) {
+                            ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
+                            count = nFaces * 2;
+                        }
                         faces = new aiFace[nFaces];
                         for (unsigned int i = 0; i < count; i += 2) {
                             SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1));
@@ -353,6 +359,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
 
                     case PrimitiveMode_TRIANGLES: {
                         nFaces = count / 3;
+                        if (nFaces * 3 != count) {
+                            ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
+                            count = nFaces * 3;
+                        }
                         faces = new aiFace[nFaces];
                         for (unsigned int i = 0; i < count; i += 3) {
                             SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
@@ -395,6 +405,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
 
                 case PrimitiveMode_LINES: {
                     nFaces = count / 2;
+                    if (nFaces * 2 != count) {
+                        ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
+                        count = nFaces * 2;
+                    }
                     faces = new aiFace[nFaces];
                     for (unsigned int i = 0; i < count; i += 2) {
                         SetFace(faces[i / 2], i, i + 1);
@@ -418,6 +432,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
 
                 case PrimitiveMode_TRIANGLES: {
                     nFaces = count / 3;
+                    if (nFaces * 3 != count) {
+                        ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
+                        count = nFaces * 3;
+                    }
                     faces = new aiFace[nFaces];
                     for (unsigned int i = 0; i < count; i += 3) {
                         SetFace(faces[i / 3], i, i + 1, i + 2);

+ 16 - 0
code/glTF2/glTF2Importer.cpp

@@ -530,6 +530,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
 
                     case PrimitiveMode_LINES: {
                         nFaces = count / 2;
+                        if (nFaces * 2 != count) {
+                            ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
+                            count = nFaces * 2;
+                        }
                         faces = new aiFace[nFaces];
                         for (unsigned int i = 0; i < count; i += 2) {
                             SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1));
@@ -553,6 +557,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
 
                     case PrimitiveMode_TRIANGLES: {
                         nFaces = count / 3;
+                        if (nFaces * 3 != count) {
+                            ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
+                            count = nFaces * 3;
+                        }
                         faces = new aiFace[nFaces];
                         for (unsigned int i = 0; i < count; i += 3) {
                             SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
@@ -604,6 +612,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
 
                 case PrimitiveMode_LINES: {
                     nFaces = count / 2;
+                    if (nFaces * 2 != count) {
+                        ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
+                        count = nFaces * 2;
+                    }
                     faces = new aiFace[nFaces];
                     for (unsigned int i = 0; i < count; i += 2) {
                         SetFace(faces[i / 2], i, i + 1);
@@ -627,6 +639,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
 
                 case PrimitiveMode_TRIANGLES: {
                     nFaces = count / 3;
+                    if (nFaces * 3 != count) {
+                        ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
+                        count = nFaces * 3;
+                    }
                     faces = new aiFace[nFaces];
                     for (unsigned int i = 0; i < count; i += 3) {
                         SetFace(faces[i / 3], i, i + 1, i + 2);

+ 6 - 9
include/assimp/BaseImporter.h

@@ -196,16 +196,13 @@ public:
     /**
      * Assimp Importer
      * unit conversions available 
-     * if you need another measurment unit add it below.
-     * it's currently defined in assimp that we prefer meters.
+     * NOTE: Valid options are initialised in the
+     * constructor in the implementation file to
+     * work around a VS2013 compiler bug if support
+     * for that compiler is dropped in the future
+     * initialisation can be moved back here
      * */
-    std::map<ImporterUnits, double> importerUnits = {
-        {ImporterUnits::M, 1},
-        {ImporterUnits::CM, 0.01},
-        {ImporterUnits::MM, 0.001},
-        {ImporterUnits::INCHES, 0.0254},
-        {ImporterUnits::FEET, 0.3048}
-    };
+    std::map<ImporterUnits, double> importerUnits;
 
     virtual void SetApplicationUnits( const ImporterUnits& unit )
     {

+ 13 - 8
include/assimp/defs.h

@@ -128,16 +128,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * GENBOUNDINGBOXES */
 //////////////////////////////////////////////////////////////////////////
 
-#ifdef _MSC_VER
+#ifdef _WIN32
 #   undef ASSIMP_API
-
     //////////////////////////////////////////////////////////////////////////
     /* Define 'ASSIMP_BUILD_DLL_EXPORT' to build a DLL of the library */
     //////////////////////////////////////////////////////////////////////////
 #   ifdef ASSIMP_BUILD_DLL_EXPORT
 #       define ASSIMP_API __declspec(dllexport)
 #       define ASSIMP_API_WINONLY __declspec(dllexport)
-#       pragma warning (disable : 4251)
 
     //////////////////////////////////////////////////////////////////////////
     /* Define 'ASSIMP_DLL' before including Assimp to link to ASSIMP in
@@ -150,7 +148,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #       define ASSIMP_API
 #       define ASSIMP_API_WINONLY
 #   endif
+#elif defined(SWIG)
+
+    /* Do nothing, the relevant defines are all in AssimpSwigPort.i */
+
+#else
+#   define ASSIMP_API __attribute__ ((visibility("default")))
+#   define ASSIMP_API_WINONLY
+#endif
 
+#ifdef _MSC_VER
+#   ifdef ASSIMP_BUILD_DLL_EXPORT
+#       pragma warning (disable : 4251)
+#   endif
     /* Force the compiler to inline a function, if possible
      */
 #   define AI_FORCE_INLINE __forceinline
@@ -158,17 +168,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     /* Tells the compiler that a function never returns. Used in code analysis
      * to skip dead paths (e.g. after an assertion evaluated to false). */
 #   define AI_WONT_RETURN __declspec(noreturn)
-
 #elif defined(SWIG)
 
     /* Do nothing, the relevant defines are all in AssimpSwigPort.i */
 
 #else
-
 #   define AI_WONT_RETURN
-
-#   define ASSIMP_API __attribute__ ((visibility("default")))
-#   define ASSIMP_API_WINONLY
 #   define AI_FORCE_INLINE inline
 #endif // (defined _MSC_VER)
 

BIN
test/models/Collada/duck.zae


BIN
test/models/glTF/IncorrectVertexArrays/Cube.bin


+ 283 - 0
test/models/glTF/IncorrectVertexArrays/Cube_v1.gltf

@@ -0,0 +1,283 @@
+{
+   "accessors" : {
+      "accessor_0" : {
+         "bufferView" : "bufferView_0",
+         "byteOffset" : 0,
+         "componentType" : 5123,
+         "count" : 36,
+         "max" : [
+            35
+         ],
+         "min" : [
+            0
+         ],
+         "type" : "SCALAR"
+      },
+      "accessor_1" : {
+         "bufferView" : "bufferView_1",
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 36,
+         "max" : [
+            1.000000,
+            1.000000,
+            1.000001
+         ],
+         "min" : [
+            -1.000000,
+            -1.000000,
+            -1.000000
+         ],
+         "type" : "VEC3"
+      },
+      "accessor_2" : {
+         "bufferView" : "bufferView_2",
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 36,
+         "max" : [
+            1.000000,
+            1.000000,
+            1.000000
+         ],
+         "min" : [
+            -1.000000,
+            -1.000000,
+            -1.000000
+         ],
+         "type" : "VEC3"
+      },
+      "accessor_3" : {
+         "bufferView" : "bufferView_3",
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 36,
+         "max" : [
+            1.000000,
+            -0.000000,
+            -0.000000,
+            1.000000
+         ],
+         "min" : [
+            0.000000,
+            -0.000000,
+            -1.000000,
+            -1.000000
+         ],
+         "type" : "VEC4"
+      },
+      "accessor_4" : {
+         "bufferView" : "bufferView_4",
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 36,
+         "max" : [
+            1.000000,
+            1.000000
+         ],
+         "min" : [
+            -1.000000,
+            -1.000000
+         ],
+         "type" : "VEC2"
+      },
+      "accessor_5" : {
+         "bufferView" : "bufferView_1",
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 36,
+         "type" : "VEC3"
+       },
+       "accessor_6" : {
+         "bufferView" : "bufferView_1",
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 35,
+         "type" : "VEC3"
+       },
+       "accessor_7" : {
+         "bufferView" : "bufferView_0",
+         "byteOffset" : 0,
+         "componentType" : 5123,
+         "count" : 35,
+         "max" : [
+            35
+         ],
+         "min" : [
+            0
+         ],
+         "type" : "SCALAR"
+      }
+   },
+   "asset" : {
+      "generator" : "VKTS glTF 2.0 exporter",
+      "version" : "1.0"
+   },
+   "bufferViews" : {
+      "bufferView_0" : {
+         "buffer" : "buffer_0",
+         "byteLength" : 72,
+         "byteOffset" : 0,
+         "target" : 34963
+      },
+      "bufferView_1" : {
+         "buffer" : "buffer_0",
+         "byteLength" : 432,
+         "byteOffset" : 72,
+         "target" : 34962
+      },
+      "bufferView_2" : {
+         "buffer" : "buffer_0",
+         "byteLength" : 432,
+         "byteOffset" : 504,
+         "target" : 34962
+      },
+      "bufferView_3" : {
+         "buffer" : "buffer_0",
+         "byteLength" : 576,
+         "byteOffset" : 936,
+         "target" : 34962
+      },
+      "bufferView_4" : {
+         "buffer" : "buffer_0",
+         "byteLength" : 288,
+         "byteOffset" : 1512,
+         "target" : 34962
+      }
+   },
+   "buffers" : {
+      "buffer_0" : {
+         "byteLength" : 514,
+         "uri" : "Cube.bin"
+      }
+   },
+   "meshes" : {
+      "mesh_0" : {
+         "name" : "Cube",
+         "primitives" : [
+            {
+               "attributes" : {
+                  "POSITION" : "accessor_1"
+               },
+               "mode" : 4
+            }
+         ]
+      },
+      "mesh_1" : {
+         "name" : "TruncatedCube",
+         "primitives" : [ {
+           "attributes" : {
+             "POSITION" : "accessor_6"
+           },
+		   "mode" : 4
+         } ]
+       },
+	   "mesh_2" : {
+         "name" : "Lines",
+         "primitives" : [ {
+           "attributes" : {
+             "POSITION" : "accessor_5"
+           },
+		   "mode" : 1
+         } ]
+       },
+       "mesh_3" : {
+         "name" : "TruncatedLines",
+         "primitives" : [ {
+           "attributes" : {
+             "POSITION" : "accessor_6"
+           },
+		   "mode" : 1
+         } ]
+       },
+       "mesh_4" : {
+          "name" : "IndexedCube",
+          "primitives" : [ {
+             "attributes" : {
+                "POSITION" : "accessor_1"
+             },
+             "mode" : 4,
+             "indices" : "accessor_0"
+          } ]
+       },
+       "mesh_5" : {
+          "name" : "TruncatedIndexedCube",
+          "primitives" : [ {
+             "attributes" : {
+                "POSITION" : "accessor_6"
+             },
+             "mode" : 4,
+             "indices" : "accessor_7"
+          } ]
+       },
+       "mesh_6" : {
+         "name" : "IndexedLines",
+         "primitives" : [ {
+           "attributes" : {
+             "POSITION" : "accessor_5"
+           },
+		   "mode" : 1,
+         "indices" : "accessor_0"
+         } ]
+       },
+       "mesh_7" : {
+         "name" : "TruncatedIndexedLines",
+         "primitives" : [ {
+           "attributes" : {
+             "POSITION" : "accessor_6"
+           },
+		   "mode" : 1,
+         "indices" : "accessor_7"
+         } ]
+       }
+   },
+   "nodes" : {
+      "node_0" : {
+         "meshes" : [ "mesh_0" ],
+         "name" : "Cube"
+      },
+      "node_1" : {
+         "meshes" : [ "mesh_1" ],
+         "name" : "TruncatedCube",
+         "translation": [ 2.5, 0.0, 2.5 ]
+      },
+	  "node_2" : {
+		"meshes" : [ "mesh_2" ],
+      "name" : "Lines",
+      "translation": [ 2.5, 0.0, 0.0 ]
+	  },	  
+     "node_3" : {
+		"meshes" : [ "mesh_3" ],
+      "name" : "TruncatedLines",
+      "translation": [ 2.5, 0.0, -2.5 ]
+	  },
+     "node_4" : {
+       "meshes" : [ "mesh_4" ],
+       "name" : "IndexedCube",
+       "translation": [ -2.5, 0.0, 2.5 ]
+     },
+     "node_5" : {
+       "meshes" : [ "mesh_5" ],
+       "name" : "TruncatedIndexedCube",
+       "translation": [ -2.5, 0.0, 0.0 ]
+     },
+     	"node_6" : {
+		"meshes" : [ "mesh_6" ],
+      "name" : "IndexedLines",
+      "translation": [ -2.5, 0.0, -2.5 ]
+	  },
+     "node_7" : {
+		"meshes" : [ "mesh_7" ],
+      "name" : "TruncatedIndexedLines",
+      "translation": [ 0.0, 0.0, -2.5 ]
+	  }
+   },
+   "scene" : "defaultScene",
+   "scenes" : {
+      "defaultScene" : {
+         "nodes" : [
+            "node_0", "node_1", "node_2", "node_3", "node_4", "node_5", "node_6", "node_7"
+         ]
+      }
+   }
+}

BIN
test/models/glTF2/IncorrectVertexArrays/Cube.bin


+ 286 - 0
test/models/glTF2/IncorrectVertexArrays/Cube.gltf

@@ -0,0 +1,286 @@
+{
+   "accessors" : [
+      {
+         "bufferView" : 0,
+         "byteOffset" : 0,
+         "componentType" : 5123,
+         "count" : 36,
+         "max" : [
+            35
+         ],
+         "min" : [
+            0
+         ],
+         "type" : "SCALAR"
+      },
+      {
+         "bufferView" : 1,
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 36,
+         "max" : [
+            1.000000,
+            1.000000,
+            1.000001
+         ],
+         "min" : [
+            -1.000000,
+            -1.000000,
+            -1.000000
+         ],
+         "type" : "VEC3"
+      },
+      {
+         "bufferView" : 2,
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 36,
+         "max" : [
+            1.000000,
+            1.000000,
+            1.000000
+         ],
+         "min" : [
+            -1.000000,
+            -1.000000,
+            -1.000000
+         ],
+         "type" : "VEC3"
+      },
+      {
+         "bufferView" : 3,
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 36,
+         "max" : [
+            1.000000,
+            -0.000000,
+            -0.000000,
+            1.000000
+         ],
+         "min" : [
+            0.000000,
+            -0.000000,
+            -1.000000,
+            -1.000000
+         ],
+         "type" : "VEC4"
+      },
+      {
+         "bufferView" : 4,
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 36,
+         "max" : [
+            1.000000,
+            1.000000
+         ],
+         "min" : [
+            -1.000000,
+            -1.000000
+         ],
+         "type" : "VEC2"
+      },
+      {
+         "bufferView" : 1,
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 36,
+         "type" : "VEC3"
+       },
+       {
+         "bufferView" : 1,
+         "byteOffset" : 0,
+         "componentType" : 5126,
+         "count" : 35,
+         "type" : "VEC3"
+       },
+       {
+         "bufferView" : 0,
+         "byteOffset" : 0,
+         "componentType" : 5123,
+         "count" : 35,
+         "max" : [
+            35
+         ],
+         "min" : [
+            0
+         ],
+         "type" : "SCALAR"
+      }
+   ],
+   "asset" : {
+      "generator" : "VKTS glTF 2.0 exporter",
+      "version" : "2.0"
+   },
+   "bufferViews" : [
+      {
+         "buffer" : 0,
+         "byteLength" : 72,
+         "byteOffset" : 0,
+         "target" : 34963
+      },
+      {
+         "buffer" : 0,
+         "byteLength" : 432,
+         "byteOffset" : 72,
+         "target" : 34962
+      },
+      {
+         "buffer" : 0,
+         "byteLength" : 432,
+         "byteOffset" : 504,
+         "target" : 34962
+      },
+      {
+         "buffer" : 0,
+         "byteLength" : 576,
+         "byteOffset" : 936,
+         "target" : 34962
+      },
+      {
+         "buffer" : 0,
+         "byteLength" : 288,
+         "byteOffset" : 1512,
+         "target" : 34962
+      }
+   ],
+   "buffers" : [
+      {
+         "byteLength" : 514,
+         "uri" : "Cube.bin"
+      }
+   ],
+   "meshes" : [
+      {
+         "name" : "Cube",
+         "primitives" : [
+            {
+               "attributes" : {
+                  "POSITION" : 1
+               },
+               "mode" : 4
+            }
+         ]
+      },
+      {
+         "name" : "TruncatedCube",
+         "primitives" : [ {
+           "attributes" : {
+             "POSITION" : 6
+           },
+		   "mode" : 4
+         } ]
+       },
+	   {
+         "name" : "Lines",
+         "primitives" : [ {
+           "attributes" : {
+             "POSITION" : 5
+           },
+		   "mode" : 1
+         } ]
+       },
+       {
+         "name" : "TruncatedLines",
+         "primitives" : [ {
+           "attributes" : {
+             "POSITION" : 6
+           },
+		   "mode" : 1
+         } ]
+       },
+       {
+          "name" : "IndexedCube",
+          "primitives" : [ {
+             "attributes" : {
+                "POSITION" : 1
+             },
+             "mode" : 4,
+             "indices" : 0
+          } ]
+       },
+       {
+          "name" : "TruncatedIndexedCube",
+          "primitives" : [ {
+             "attributes" : {
+                "POSITION" : 6
+             },
+             "mode" : 4,
+             "indices" : 7
+          } ]
+       },
+       {
+         "name" : "IndexedLines",
+         "primitives" : [ {
+           "attributes" : {
+             "POSITION" : 5
+           },
+		   "mode" : 1,
+         "indices" : 0
+         } ]
+       },
+       {
+         "name" : "TruncatedIndexedLines",
+         "primitives" : [ {
+           "attributes" : {
+             "POSITION" : 6
+           },
+		   "mode" : 1,
+         "indices" : 7
+         } ]
+       }
+   ],
+   "nodes" : [
+      {
+         "mesh" : 0,
+         "name" : "Cube"
+      },
+      {
+         "mesh" : 1,
+         "name" : "TruncatedCube",
+         "translation": [ 2.5, 0.0, 2.5 ]
+      },
+	  {
+		"mesh" : 2,
+      "name" : "Lines",
+      "translation": [ 2.5, 0.0, 0.0 ]
+	  },	  
+     {
+		"mesh" : 3,
+      "name" : "TruncatedLines",
+      "translation": [ 2.5, 0.0, -2.5 ]
+	  },
+     {
+       "mesh" : 4,
+       "name" : "IndexedCube",
+       "translation": [ -2.5, 0.0, 2.5 ]
+     },
+     {
+       "mesh" : 5,
+       "name" : "TruncatedIndexedCube",
+       "translation": [ -2.5, 0.0, 0.0 ]
+     },
+     	{
+		"mesh" : 6,
+      "name" : "IndexedLines",
+      "translation": [ -2.5, 0.0, -2.5 ]
+	  },
+     {
+		"mesh" : 7,
+      "name" : "TruncatedIndexedLines",
+      "translation": [ 0.0, 0.0, -2.5 ]
+	  }
+   ],
+   "samplers" : [
+      {}
+   ],
+   "scene" : 0,
+   "scenes" : [
+      {
+         "nodes" : [
+            0, 1, 2, 3, 4, 5, 6, 7
+         ]
+      }
+   ]
+}

+ 45 - 5
test/unit/utColladaImportExport.cpp

@@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "AbstractImportExportBase.h"
 
 #include <assimp/Importer.hpp>
+#include <assimp/scene.h>
 #include <assimp/postprocess.h>
 
 using namespace Assimp;
@@ -52,8 +53,19 @@ class utColladaImportExport : public AbstractImportExportBase {
 public:
     virtual bool importerTest() {
         Assimp::Importer importer;
-        const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/Collada/duck.dae", aiProcess_ValidateDataStructure );
-        return nullptr != scene;
+        const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck.dae", aiProcess_ValidateDataStructure);
+        if (scene == nullptr)
+            return false;
+
+        // Expected number of items
+        EXPECT_EQ(scene->mNumMeshes, 1u);
+        EXPECT_EQ(scene->mNumMaterials, 1u);
+        EXPECT_EQ(scene->mNumAnimations, 0u);
+        EXPECT_EQ(scene->mNumTextures, 0u);
+        EXPECT_EQ(scene->mNumLights, 1u);
+        EXPECT_EQ(scene->mNumCameras, 1u);
+
+        return true;
     }
 };
 
@@ -64,9 +76,37 @@ TEST_F(utColladaImportExport, importBlenFromFileTest) {
 class utColladaZaeImportExport : public AbstractImportExportBase {
 public:
     virtual bool importerTest() {
-        Assimp::Importer importer;
-        const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck.zae", aiProcess_ValidateDataStructure);
-        return nullptr != scene;
+        {
+            Assimp::Importer importer;
+            const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck.zae", aiProcess_ValidateDataStructure);
+            if (scene == nullptr)
+                return false;
+
+            // Expected number of items
+            EXPECT_EQ(scene->mNumMeshes, 1u);
+            EXPECT_EQ(scene->mNumMaterials, 1u);
+            EXPECT_EQ(scene->mNumAnimations, 0u);
+            EXPECT_EQ(scene->mNumTextures, 1u);
+            EXPECT_EQ(scene->mNumLights, 1u);
+            EXPECT_EQ(scene->mNumCameras, 1u);
+        }
+
+        {
+            Assimp::Importer importer;
+            const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck_nomanifest.zae", aiProcess_ValidateDataStructure);
+            if (scene == nullptr)
+                return false;
+
+            // Expected number of items
+            EXPECT_EQ(scene->mNumMeshes, 1u);
+            EXPECT_EQ(scene->mNumMaterials, 1u);
+            EXPECT_EQ(scene->mNumAnimations, 0u);
+            EXPECT_EQ(scene->mNumTextures, 1u);
+            EXPECT_EQ(scene->mNumLights, 1u);
+            EXPECT_EQ(scene->mNumCameras, 1u);
+        }
+
+        return true;
     }
 };
 

+ 1 - 1
test/unit/utVersion.cpp

@@ -48,7 +48,7 @@ TEST_F( utVersion, aiGetLegalStringTest ) {
     EXPECT_NE( lv, nullptr );
     std::string text( lv );
 
-    size_t pos( text.find( std::string( "2017" ) ) );
+    size_t pos( text.find( std::string( "2019" ) ) );
     EXPECT_NE( pos, std::string::npos );
 }
 

+ 23 - 0
test/unit/utglTF2ImportExport.cpp

@@ -382,6 +382,29 @@ TEST_F(utglTF2ImportExport, import_cameras) {
     EXPECT_NE(nullptr, scene);
 }
 
+TEST_F(utglTF2ImportExport, incorrect_vertex_arrays) {
+    Assimp::Importer importer;
+    const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/IncorrectVertexArrays/Cube.gltf",
+        aiProcess_ValidateDataStructure);
+    EXPECT_NE(nullptr, scene);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 36u);
+    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 12u);
+    EXPECT_EQ(scene->mMeshes[1]->mNumVertices, 35u);
+    EXPECT_EQ(scene->mMeshes[1]->mNumFaces, 11u);
+    EXPECT_EQ(scene->mMeshes[2]->mNumVertices, 36u);
+    EXPECT_EQ(scene->mMeshes[2]->mNumFaces, 18u);
+    EXPECT_EQ(scene->mMeshes[3]->mNumVertices, 35u);
+    EXPECT_EQ(scene->mMeshes[3]->mNumFaces, 17u);
+    EXPECT_EQ(scene->mMeshes[4]->mNumVertices, 36u);
+    EXPECT_EQ(scene->mMeshes[4]->mNumFaces, 12u);
+    EXPECT_EQ(scene->mMeshes[5]->mNumVertices, 35u);
+    EXPECT_EQ(scene->mMeshes[5]->mNumFaces, 11u);
+    EXPECT_EQ(scene->mMeshes[6]->mNumVertices, 36u);
+    EXPECT_EQ(scene->mMeshes[6]->mNumFaces, 18u);
+    EXPECT_EQ(scene->mMeshes[7]->mNumVertices, 35u);
+    EXPECT_EQ(scene->mMeshes[7]->mNumFaces, 17u);
+}
+
 #ifndef ASSIMP_BUILD_NO_EXPORT
 TEST_F( utglTF2ImportExport, exportglTF2FromFileTest ) {
     EXPECT_TRUE( exporterTest() );

+ 25 - 0
test/unit/utglTFImportExport.cpp

@@ -46,6 +46,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/Importer.hpp>
 #include <assimp/postprocess.h>
 
+#include <assimp/scene.h>
+
 using namespace Assimp;
 
 class utglTFImportExport : public AbstractImportExportBase {
@@ -60,3 +62,26 @@ public:
 TEST_F( utglTFImportExport, importglTFFromFileTest ) {
     EXPECT_TRUE( importerTest() );
 }
+
+TEST_F(utglTFImportExport, incorrect_vertex_arrays) {
+    Assimp::Importer importer;
+    const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF/IncorrectVertexArrays/Cube_v1.gltf",
+        aiProcess_ValidateDataStructure);
+    EXPECT_NE(nullptr, scene);
+    EXPECT_EQ(scene->mMeshes[0]->mNumVertices, 36u);
+    EXPECT_EQ(scene->mMeshes[0]->mNumFaces, 12u);
+    EXPECT_EQ(scene->mMeshes[1]->mNumVertices, 35u);
+    EXPECT_EQ(scene->mMeshes[1]->mNumFaces, 11u);
+    EXPECT_EQ(scene->mMeshes[2]->mNumVertices, 36u);
+    EXPECT_EQ(scene->mMeshes[2]->mNumFaces, 18u);
+    EXPECT_EQ(scene->mMeshes[3]->mNumVertices, 35u);
+    EXPECT_EQ(scene->mMeshes[3]->mNumFaces, 17u);
+    EXPECT_EQ(scene->mMeshes[4]->mNumVertices, 36u);
+    EXPECT_EQ(scene->mMeshes[4]->mNumFaces, 12u);
+    EXPECT_EQ(scene->mMeshes[5]->mNumVertices, 35u);
+    EXPECT_EQ(scene->mMeshes[5]->mNumFaces, 11u);
+    EXPECT_EQ(scene->mMeshes[6]->mNumVertices, 36u);
+    EXPECT_EQ(scene->mMeshes[6]->mNumFaces, 18u);
+    EXPECT_EQ(scene->mMeshes[7]->mNumVertices, 35u);
+    EXPECT_EQ(scene->mMeshes[7]->mNumFaces, 17u);
+}