Explorar el Código

Merge branch 'master' into fix-multi-uvset-coord

Kim Kulling hace 6 años
padre
commit
6e75d7a839
Se han modificado 100 ficheros con 3606 adiciones y 2533 borrados
  1. 2 0
      .github/FUNDING.yml
  2. 2 1
      .travis.sh
  3. 26 0
      BUILDBINARIES_EXAMPLE.bat
  4. 13 17
      CMakeLists.txt
  5. 4 2
      appveyor.yml
  6. 2 6
      code/3MF/D3MFImporter.cpp
  7. 5 346
      code/3MF/D3MFOpcPackage.cpp
  8. 3 4
      code/3MF/D3MFOpcPackage.h
  9. 1 1
      code/AMF/AMFImporter.cpp
  10. 13 13
      code/AMF/AMFImporter_Postprocess.cpp
  11. 4 2
      code/CMakeLists.txt
  12. 4 3
      code/Collada/ColladaExporter.cpp
  13. 2 6
      code/Collada/ColladaHelper.h
  14. 251 222
      code/Collada/ColladaLoader.cpp
  15. 5 5
      code/Collada/ColladaLoader.h
  16. 357 280
      code/Collada/ColladaParser.cpp
  17. 7 0
      code/Collada/ColladaParser.h
  18. 25 2
      code/Common/BaseImporter.cpp
  19. 60 101
      code/Common/DefaultIOSystem.cpp
  20. 5 30
      code/Common/Exporter.cpp
  21. 29 0
      code/Common/SceneCombiner.cpp
  22. 539 0
      code/Common/ZipArchiveIOSystem.cpp
  23. 51 62
      code/FBX/FBXConverter.cpp
  24. 2 17
      code/FBX/FBXConverter.h
  25. 1 5
      code/FBX/FBXExportProperty.cpp
  26. 84 36
      code/FBX/FBXExporter.cpp
  27. 1 1
      code/FBX/FBXExporter.h
  28. 8 5
      code/FBX/FBXImporter.cpp
  29. 11 12
      code/FBX/FBXMeshGeometry.cpp
  30. 3 4
      code/Importer/IFC/IFCCurve.cpp
  31. 1 1
      code/Importer/IFC/IFCGeometry.cpp
  32. 3 3
      code/Importer/IFC/IFCOpenings.cpp
  33. 1 1
      code/MD2/MD2Loader.cpp
  34. 2 1
      code/MD5/MD5Loader.cpp
  35. 1 1
      code/MD5/MD5Parser.cpp
  36. 16 25
      code/MDL/MDLLoader.cpp
  37. 1 17
      code/MDL/MDLLoader.h
  38. 1 17
      code/Material/MaterialSystem.cpp
  39. 1 4
      code/Obj/ObjFileImporter.cpp
  40. 2 2
      code/Obj/ObjFileParser.cpp
  41. 3 3
      code/PostProcessing/CalcTangentsProcess.cpp
  42. 3 3
      code/PostProcessing/ComputeUVMappingProcess.cpp
  43. 0 25
      code/PostProcessing/JoinVerticesProcess.cpp
  44. 29 0
      code/PostProcessing/MakeVerboseFormat.cpp
  45. 7 0
      code/PostProcessing/MakeVerboseFormat.h
  46. 115 10
      code/PostProcessing/ScaleProcess.cpp
  47. 11 2
      code/PostProcessing/ScaleProcess.h
  48. 1 1
      code/PostProcessing/ValidateDataStructure.cpp
  49. 8 8
      code/Q3BSP/Q3BSPFileImporter.cpp
  50. 6 6
      code/Q3BSP/Q3BSPFileImporter.h
  51. 3 2
      code/Q3BSP/Q3BSPFileParser.cpp
  52. 6 7
      code/Q3BSP/Q3BSPFileParser.h
  53. 0 325
      code/Q3BSP/Q3BSPZipArchive.cpp
  54. 0 135
      code/Q3BSP/Q3BSPZipArchive.h
  55. 3 3
      code/X/XFileParser.cpp
  56. 1 1
      code/X3D/X3DExporter.cpp
  57. 30 30
      code/X3D/X3DImporter.cpp
  58. 3 3
      code/X3D/X3DImporter_Geometry2D.cpp
  59. 7 7
      code/X3D/X3DImporter_Geometry3D.cpp
  60. 1 1
      code/X3D/X3DImporter_Networking.cpp
  61. 40 40
      code/X3D/X3DImporter_Postprocess.cpp
  62. 6 6
      code/X3D/X3DImporter_Rendering.cpp
  63. 1 1
      code/X3D/X3DImporter_Texturing.cpp
  64. 8 61
      code/glTF/glTFAsset.h
  65. 10 192
      code/glTF/glTFAsset.inl
  66. 5 3
      code/glTF/glTFAssetWriter.inl
  67. 193 0
      code/glTF/glTFCommon.cpp
  68. 248 0
      code/glTF/glTFCommon.h
  69. 4 1
      code/glTF/glTFExporter.cpp
  70. 24 58
      code/glTF/glTFImporter.cpp
  71. 9 60
      code/glTF2/glTF2Asset.h
  72. 9 194
      code/glTF2/glTF2Asset.inl
  73. 1 1
      code/glTF2/glTF2AssetWriter.inl
  74. 42 17
      code/glTF2/glTF2Importer.cpp
  75. 2 2
      code/res/assimp.rc
  76. 4 4
      contrib/gtest/test/gtest-param-test_test.cc
  77. 1 1
      contrib/irrXML/irrXML.h
  78. 59 2
      include/assimp/BaseImporter.h
  79. 19 10
      include/assimp/MathFunctions.h
  80. 2 0
      include/assimp/SceneCombiner.h
  81. 87 0
      include/assimp/ZipArchiveIOSystem.h
  82. 0 2
      include/assimp/camera.h
  83. 7 0
      include/assimp/config.h.in
  84. 33 13
      include/assimp/material.h
  85. 4 5
      include/assimp/matrix4x4.inl
  86. 1 0
      include/assimp/scene.h
  87. 13 8
      include/assimp/types.h
  88. 2 2
      port/PyAssimp/pyassimp/helper.py
  89. 10 0
      revision.h.in
  90. 1 0
      samples/SimpleOpenGL/CMakeLists.txt
  91. 22 22
      samples/SimpleOpenGL/Sample_SimpleOpenGL.c
  92. BIN
      test/models/Collada/duck.zae
  93. BIN
      test/models/Collada/duck_nomanifest.zae
  94. 571 0
      test/models/FBX/close_to_identity_transforms.fbx
  95. 1 0
      test/models/FBX/transparentTest.fbx
  96. 98 0
      test/models/glTF2/cameras/Cameras.gltf
  97. BIN
      test/models/glTF2/cameras/simpleSquare.bin
  98. BIN
      test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube.bin
  99. 282 0
      test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube.gltf
  100. 1 1
      test/unit/SceneDiffer.cpp

+ 2 - 0
.github/FUNDING.yml

@@ -0,0 +1,2 @@
+patreon: assimp
+ko_fi: kimkulling

+ 2 - 1
.travis.sh

@@ -7,7 +7,8 @@
 #
 function generate() {
     OPTIONS="-DASSIMP_WERROR=ON"
-
+    OPTIONS="$OPTIONS -DASSIMP_NO_EXPORT=NO"
+    
     if [ "$DISABLE_EXPORTERS" = "YES" ] ; then
         OPTIONS="$OPTIONS -DASSIMP_NO_EXPORT=YES"
     else

+ 26 - 0
BUILDBINARIES_EXAMPLE.bat

@@ -0,0 +1,26 @@
+:: This is an example file to generate binaries using Windows Operating System
+:: This script is configured to be executed from the source directory
+
+:: Compiled binaries will be placed in BINARIES_DIR\code\CONFIG
+
+:: NOTE
+:: The build process will generate a config.h file that is placed in BINARIES_DIR\include
+:: This file must be merged with SOURCE_DIR\include
+:: You should write yourself a script that copies the files where you want them.
+:: Also see: https://github.com/assimp/assimp/pull/2646
+
+SET SOURCE_DIR=.
+
+:: For generators see "cmake --help"
+SET GENERATOR=Visual Studio 15 2017
+
+SET BINARIES_DIR="./BINARIES/Win32"
+cmake CMakeLists.txt -G "%GENERATOR%" -S %SOURCE_DIR% -B %BINARIES_DIR%
+cmake --build %BINARIES_DIR% --config release
+
+SET BINARIES_DIR="./BINARIES/x64"
+cmake CMakeLists.txt -G "%GENERATOR% Win64" -S %SOURCE_DIR% -B %BINARIES_DIR%
+cmake --build %BINARIES_DIR% --config debug
+cmake --build %BINARIES_DIR% --config release
+
+PAUSE

+ 13 - 17
CMakeLists.txt

@@ -173,7 +173,6 @@ SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VER
 SET (ASSIMP_SOVERSION 5)
 
 SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" )
-
 if(NOT HUNTER_ENABLED)
   # Enable C++11 support globally
   set_property( GLOBAL PROPERTY CXX_STANDARD 11 )
@@ -254,6 +253,7 @@ ELSEIF(MSVC)
   IF(MSVC12)
     ADD_COMPILE_OPTIONS(/wd4351)
   ENDIF()
+  SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob2 /DEBUG:FULL /Zi")
 ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
   IF(NOT HUNTER_ENABLED)
     SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}")
@@ -271,22 +271,20 @@ ELSEIF( CMAKE_COMPILER_IS_MINGW )
     SET(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
     SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
   ENDIF()
-  SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long -Wa,-mbig-obj ${CMAKE_CXX_FLAGS}")
+  SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long -Wa,-mbig-obj -O3 ${CMAKE_CXX_FLAGS}")
   SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
   ADD_DEFINITIONS( -U__STRICT_ANSI__ )
 ENDIF()
 
 IF ( IOS AND NOT HUNTER_ENABLED)
-
-IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
-  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -Og")
-  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -Og")
-ELSE()
-  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3")
-  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
-  # Experimental for pdb generation
-ENDIF()
-
+  IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -Og")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -Og")
+  ELSE()
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
+    # Experimental for pdb generation
+  ENDIF()
 ENDIF( IOS AND NOT HUNTER_ENABLED)
 
 IF (ASSIMP_COVERALLS)
@@ -341,7 +339,7 @@ SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE STRING
 
 get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
 
-IF (INJECT_DEBUG_POSTFIX AND (CMAKE_BUILD_TYPE STREQUAL "Debug"))
+IF (INJECT_DEBUG_POSTFIX AND (is_multi_config OR CMAKE_BUILD_TYPE STREQUAL "Debug"))
   SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Debug Postfix for lib, samples and tools")
 ELSE()
   SET(CMAKE_DEBUG_POSTFIX "" CACHE STRING "Debug Postfix for lib, samples and tools")
@@ -559,17 +557,15 @@ ENDIF(NOT HUNTER_ENABLED)
 
 ADD_SUBDIRECTORY( code/ )
 IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
+  # The viewer for windows only
   IF ( WIN32 AND DirectX_D3DX9_LIBRARY )
     OPTION ( ASSIMP_BUILD_ASSIMP_VIEW "If the Assimp view tool is built. (requires DirectX)" ${DirectX_FOUND} )
     IF ( ASSIMP_BUILD_ASSIMP_VIEW )
       ADD_SUBDIRECTORY( tools/assimp_view/ )
     ENDIF ( ASSIMP_BUILD_ASSIMP_VIEW )
   ENDIF ( WIN32 AND DirectX_D3DX9_LIBRARY )
-
+  # Te command line tool
   ADD_SUBDIRECTORY( tools/assimp_cmd/ )
-IF (NOT IOS)
-  ADD_SUBDIRECTORY( tools/assimp_qt_viewer/ )
-ENDIF (NOT IOS)
 ENDIF ( ASSIMP_BUILD_ASSIMP_TOOLS )
 
 IF ( ASSIMP_BUILD_SAMPLES)

+ 4 - 2
appveyor.yml

@@ -14,9 +14,9 @@ matrix:
   fast_finish: true
     
 image:
-  - Visual Studio 2013
   - Visual Studio 2015
   - Visual Studio 2017
+  - MinGW  
     
 platform:
   - Win32
@@ -27,11 +27,13 @@ configuration: Release
 install:
   - set PATH=C:\Ruby24-x64\bin;%PATH%
   - set CMAKE_DEFINES -DASSIMP_WERROR=ON
-  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set CMAKE_GENERATOR_NAME=Visual Studio 12 2013
+  - if [%COMPILER%]==[MinGW] set PATH=C:\MinGW\bin;%PATH%
   - 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 "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_NAME% Win64
   - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" .
+  # Rename sh.exe as sh.exe in PATH interferes with MinGW
+  - 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
   - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/1/d/8/1d8137db-b5bb-4925-8c5d-927424a2e4de/vc_redist.x86.exe -OutFile .\packaging\windows-innosetup\vc_redist.x86.exe

+ 2 - 6
code/3MF/D3MFImporter.cpp

@@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/importerdesc.h>
 #include <assimp/StringComparison.h>
 #include <assimp/StringUtils.h>
+#include <assimp/ZipArchiveIOSystem.h>
 
 #include <string>
 #include <vector>
@@ -58,11 +59,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <memory>
 
 #include "D3MFOpcPackage.h"
-#ifdef ASSIMP_USE_HUNTER
-#  include <minizip/unzip.h>
-#else
-#  include <unzip.h>
-#endif
 #include <assimp/irrXMLWrapper.h>
 #include "3MFXmlTags.h"
 #include <assimp/fast_atof.h>
@@ -453,7 +449,7 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo
         if ( nullptr == pIOHandler ) {
             return false;
         }
-        if ( !D3MF::D3MFOpcPackage::isZipArchive( pIOHandler, filename ) ) {
+        if ( !ZipArchiveIOSystem::isZipArchive( pIOHandler, filename ) ) {
             return false;
         }
         D3MF::D3MFOpcPackage opcPackage( pIOHandler, filename );

+ 5 - 346
code/3MF/D3MFOpcPackage.cpp

@@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/IOSystem.hpp>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/ai_assert.h>
+#include <assimp/ZipArchiveIOSystem.h>
 
 #include <cstdlib>
 #include <memory>
@@ -56,344 +57,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <map>
 #include <algorithm>
 #include <cassert>
-#ifdef ASSIMP_USE_HUNTER
-#  include <minizip/unzip.h>
-#else
-#  include <unzip.h>
-#endif
 #include "3MFXmlTags.h"
 
 namespace Assimp {
 
 namespace D3MF {
-
-class IOSystem2Unzip {
-public:
-    static voidpf open(voidpf opaque, const char* filename, int mode);
-    static uLong read(voidpf opaque, voidpf stream, void* buf, uLong size);
-    static uLong write(voidpf opaque, voidpf stream, const void* buf, uLong size);
-    static long tell(voidpf opaque, voidpf stream);
-    static long seek(voidpf opaque, voidpf stream, uLong offset, int origin);
-    static int close(voidpf opaque, voidpf stream);
-    static int testerror(voidpf opaque, voidpf stream);
-    static zlib_filefunc_def get(IOSystem* pIOHandler);
-};
-
-voidpf IOSystem2Unzip::open(voidpf opaque, const char* filename, int mode) {
-    IOSystem* io_system = reinterpret_cast<IOSystem*>(opaque);
-
-    const char* mode_fopen = NULL;
-    if((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) {
-        mode_fopen = "rb";
-    } else {
-        if(mode & ZLIB_FILEFUNC_MODE_EXISTING) {
-            mode_fopen = "r+b";
-        } else {
-            if(mode & ZLIB_FILEFUNC_MODE_CREATE) {
-                mode_fopen = "wb";
-            }
-        }
-    }
-
-    return (voidpf) io_system->Open(filename, mode_fopen);
-}
-
-uLong IOSystem2Unzip::read(voidpf /*opaque*/, voidpf stream, void* buf, uLong size) {
-    IOStream* io_stream = (IOStream*) stream;
-
-    return static_cast<uLong>(io_stream->Read(buf, 1, size));
-}
-
-uLong IOSystem2Unzip::write(voidpf /*opaque*/, voidpf stream, const void* buf, uLong size) {
-    IOStream* io_stream = (IOStream*) stream;
-
-    return static_cast<uLong>(io_stream->Write(buf, 1, size));
-}
-
-long IOSystem2Unzip::tell(voidpf /*opaque*/, voidpf stream) {
-    IOStream* io_stream = (IOStream*) stream;
-
-    return static_cast<long>(io_stream->Tell());
-}
-
-long IOSystem2Unzip::seek(voidpf /*opaque*/, voidpf stream, uLong offset, int origin) {
-    IOStream* io_stream = (IOStream*) stream;
-
-    aiOrigin assimp_origin;
-    switch (origin) {
-        default:
-        case ZLIB_FILEFUNC_SEEK_CUR:
-            assimp_origin = aiOrigin_CUR;
-            break;
-        case ZLIB_FILEFUNC_SEEK_END:
-            assimp_origin = aiOrigin_END;
-            break;
-        case ZLIB_FILEFUNC_SEEK_SET:
-            assimp_origin = aiOrigin_SET;
-            break;
-    }
-
-    return (io_stream->Seek(offset, assimp_origin) == aiReturn_SUCCESS ? 0 : -1);
-}
-
-int IOSystem2Unzip::close(voidpf opaque, voidpf stream) {
-    IOSystem* io_system = (IOSystem*) opaque;
-    IOStream* io_stream = (IOStream*) stream;
-
-    io_system->Close(io_stream);
-
-    return 0;
-}
-
-int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) {
-    return 0;
-}
-
-zlib_filefunc_def IOSystem2Unzip::get(IOSystem* pIOHandler) {
-    zlib_filefunc_def mapping;
-
-#ifdef ASSIMP_USE_HUNTER
-    mapping.zopen_file = (open_file_func)open;
-    mapping.zread_file = (read_file_func)read;
-    mapping.zwrite_file = (write_file_func)write;
-    mapping.ztell_file = (tell_file_func)tell;
-    mapping.zseek_file = (seek_file_func)seek;
-    mapping.zclose_file = (close_file_func)close;
-    mapping.zerror_file = (error_file_func)testerror;
-#else
-    mapping.zopen_file = open;
-    mapping.zread_file = read;
-    mapping.zwrite_file = write;
-    mapping.ztell_file = tell;
-    mapping.zseek_file = seek;
-    mapping.zclose_file = close;
-    mapping.zerror_file = testerror;
-#endif
-    mapping.opaque = reinterpret_cast<voidpf>(pIOHandler);
-
-    return mapping;
-}
-
-class ZipFile : public IOStream {
-    friend class D3MFZipArchive;
-
-public:
-    explicit ZipFile(size_t size);
-    virtual ~ZipFile();
-    size_t Read(void* pvBuffer, size_t pSize, size_t pCount );
-    size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/);
-    size_t FileSize() const;
-    aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/);
-    size_t Tell() const;
-    void Flush();
-
-private:
-    void *m_Buffer;
-    size_t m_Size;
-};
-
-ZipFile::ZipFile(size_t size)
-: m_Buffer( nullptr )
-, m_Size(size) {
-    ai_assert(m_Size != 0);
-    m_Buffer = ::malloc(m_Size);
-}
-
-ZipFile::~ZipFile() {
-    ::free(m_Buffer);
-    m_Buffer = NULL;
-}
-
-size_t ZipFile::Read(void* pvBuffer, size_t pSize, size_t pCount) {
-    const size_t size = pSize * pCount;
-    ai_assert(size <= m_Size);
-
-    std::memcpy(pvBuffer, m_Buffer, size);
-
-    return size;
-}
-
-size_t ZipFile::Write(const void* pvBuffer, size_t size, size_t pCount ) {
-    const size_t size_to_write( size * pCount );
-    if ( 0 == size_to_write ) {
-        return 0U;
-    }
-    return 0U;
-}
-
-size_t ZipFile::FileSize() const {
-    return m_Size;
-}
-
-aiReturn ZipFile::Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) {
-    return aiReturn_FAILURE;
-}
-
-size_t ZipFile::Tell() const {
-    return 0;
-}
-
-void ZipFile::Flush() {
-    // empty
-}
-
-class D3MFZipArchive : public IOSystem {
-public:
-    static const unsigned int FileNameSize = 256;
-
-    D3MFZipArchive(IOSystem* pIOHandler, const std::string & rFile);
-    ~D3MFZipArchive();
-    bool Exists(const char* pFile) const;
-    char getOsSeparator() const;
-    IOStream* Open(const char* pFile, const char* pMode = "rb");
-    void Close(IOStream* pFile);
-    bool isOpen() const;
-    void getFileList(std::vector<std::string> &rFileList);
-
-private:
-    bool mapArchive();
-
-private:
-    unzFile m_ZipFileHandle;
-    std::map<std::string, ZipFile*> m_ArchiveMap;
-};
-
-// ------------------------------------------------------------------------------------------------
-//  Constructor.
-D3MFZipArchive::D3MFZipArchive(IOSystem* pIOHandler, const std::string& rFile)
-: m_ZipFileHandle( nullptr )
-, m_ArchiveMap() {
-    if (! rFile.empty()) {                
-        zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler);            
-
-        m_ZipFileHandle = unzOpen2(rFile.c_str(), &mapping);
-        if(m_ZipFileHandle != nullptr ) {
-            mapArchive();
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Destructor.
-D3MFZipArchive::~D3MFZipArchive() {
-    for(auto &file : m_ArchiveMap) {
-        delete file.second;
-    }
-    m_ArchiveMap.clear();
-
-    if(m_ZipFileHandle != nullptr) {
-        unzClose(m_ZipFileHandle);
-        m_ZipFileHandle = nullptr;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Returns true, if the archive is already open.
-bool D3MFZipArchive::isOpen() const {
-    return (m_ZipFileHandle != nullptr );
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Returns true, if the filename is part of the archive.
-bool D3MFZipArchive::Exists(const char* pFile) const {
-    ai_assert(pFile != nullptr );
-
-    if ( pFile == nullptr ) {
-        return false;
-    }
-
-    std::string filename(pFile);
-    std::map<std::string, ZipFile*>::const_iterator it = m_ArchiveMap.find(filename);
-    bool exist( false );
-    if(it != m_ArchiveMap.end()) {
-        exist = true;
-    }
-
-    return exist;
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Returns the separator delimiter.
-char D3MFZipArchive::getOsSeparator() const {
-#ifndef _WIN32
-    return '/';
-#else
-    return '\\';
-#endif
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Opens a file, which is part of the archive.
-IOStream *D3MFZipArchive::Open(const char* pFile, const char* /*pMode*/) {
-    ai_assert(pFile != NULL);
-
-    IOStream* result = NULL;
-
-    std::map<std::string, ZipFile*>::iterator it = m_ArchiveMap.find(pFile);
-
-    if(it != m_ArchiveMap.end()) {
-        result = static_cast<IOStream*>(it->second);
-    }
-
-    return result;
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Close a filestream.
-void D3MFZipArchive::Close(IOStream *pFile) {
-    (void)(pFile);
-    ai_assert(pFile != NULL);
-
-    // We don't do anything in case the file would be opened again in the future
-}
-// ------------------------------------------------------------------------------------------------
-//  Returns the file-list of the archive.
-void D3MFZipArchive::getFileList(std::vector<std::string> &rFileList) {
-    rFileList.clear();
-
-    for(const auto &file : m_ArchiveMap) {
-        rFileList.push_back(file.first);
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Maps the archive content.
-bool D3MFZipArchive::mapArchive() {
-    bool success = false;
-
-    if(m_ZipFileHandle != NULL) {
-        if(m_ArchiveMap.empty()) {
-            //  At first ensure file is already open
-            if(unzGoToFirstFile(m_ZipFileHandle) == UNZ_OK) {
-                // Loop over all files
-                do {
-                    char filename[FileNameSize];
-                    unz_file_info fileInfo;
-
-                    if(unzGetCurrentFileInfo(m_ZipFileHandle, &fileInfo, filename, FileNameSize, NULL, 0, NULL, 0) == UNZ_OK) {
-                        // The file has EXACTLY the size of uncompressed_size. In C
-                        // you need to mark the last character with '\0', so add
-                        // another character
-                        if(fileInfo.uncompressed_size != 0 && unzOpenCurrentFile(m_ZipFileHandle) == UNZ_OK) {
-                            std::pair<std::map<std::string, ZipFile*>::iterator, bool> result = m_ArchiveMap.insert(std::make_pair(filename, new ZipFile(fileInfo.uncompressed_size)));
-
-                            if(unzReadCurrentFile(m_ZipFileHandle, result.first->second->m_Buffer, fileInfo.uncompressed_size) == (long int) fileInfo.uncompressed_size) {
-                                if(unzCloseCurrentFile(m_ZipFileHandle) == UNZ_OK) {
-                                    // Nothing to do anymore...
-                                }
-                            }
-                        }
-                    }
-                } while(unzGoToNextFile(m_ZipFileHandle) != UNZ_END_OF_LIST_OF_FILE);
-            }
-        }
-
-        success = true;
-    }
-
-    return success;
-}
-
 // ------------------------------------------------------------------------------------------------
 
 typedef std::shared_ptr<OpcPackageRelationship> OpcPackageRelationshipPtr;
@@ -453,7 +121,7 @@ public:
 D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
 : mRootStream(nullptr)
 , mZipArchive() {    
-    mZipArchive.reset( new D3MF::D3MFZipArchive( pIOHandler, rFile ) );    
+    mZipArchive.reset( new ZipArchiveIOSystem( pIOHandler, rFile ) );
     if(!mZipArchive->isOpen()) {
         throw DeadlyImportError("Failed to open file " + rFile+ ".");
     }
@@ -481,14 +149,14 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
 
             ASSIMP_LOG_DEBUG(rootFile);
 
+            mZipArchive->Close(fileStream);
+
             mRootStream = mZipArchive->Open(rootFile.c_str());
             ai_assert( mRootStream != nullptr );
             if ( nullptr == mRootStream ) {
                 throw DeadlyExportError( "Cannot open root-file in archive : " + rootFile );
             }
 
-            mZipArchive->Close( fileStream );
-
         } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
             ASSIMP_LOG_WARN_F("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES",file);
         } else {
@@ -499,7 +167,7 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
 }
 
 D3MFOpcPackage::~D3MFOpcPackage() {
-    // empty
+    mZipArchive->Close(mRootStream);
 }
 
 IOStream* D3MFOpcPackage::RootStream() const {
@@ -516,15 +184,6 @@ bool D3MFOpcPackage::validate() {
     return mZipArchive->Exists( ModelRef.c_str() );
 }
 
-bool D3MFOpcPackage::isZipArchive( IOSystem* pIOHandler, const std::string& rFile ) {
-    D3MF::D3MFZipArchive ar( pIOHandler, rFile );
-    if ( !ar.isOpen() ) {
-        return false;
-    }
-
-    return true;
-}
-
 std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) {
     std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(stream));
     std::unique_ptr<XmlReader> xml(irr::io::createIrrXMLReader(xmlStream.get()));

+ 3 - 4
code/3MF/D3MFOpcPackage.h

@@ -49,6 +49,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/irrXMLWrapper.h>
 
 namespace Assimp {
+    class ZipArchiveIOSystem;
+
 namespace D3MF {
 
 using XmlReader = irr::io::IrrXMLReader ;
@@ -60,22 +62,19 @@ struct OpcPackageRelationship {
     std::string target;
 };
 
-class D3MFZipArchive;
-
 class D3MFOpcPackage {
 public:
     D3MFOpcPackage( IOSystem* pIOHandler, const std::string& rFile );
     ~D3MFOpcPackage();
     IOStream* RootStream() const;
     bool validate();
-    static bool isZipArchive( IOSystem* pIOHandler, const std::string& rFile );
 
 protected:
     std::string ReadPackageRootRelationship(IOStream* stream);
 
 private:
     IOStream* mRootStream;
-    std::unique_ptr<D3MFZipArchive> mZipArchive;
+    std::unique_ptr<ZipArchiveIOSystem> mZipArchive;
 };
 
 } // Namespace D3MF

+ 1 - 1
code/AMF/AMFImporter.cpp

@@ -83,7 +83,7 @@ void AMFImporter::Clear()
 	mMaterial_Converted.clear();
 	mTexture_Converted.clear();
 	// Delete all elements
-	if(mNodeElement_List.size())
+	if(!mNodeElement_List.empty())
 	{
 		for(CAMFImporter_NodeElement* ne: mNodeElement_List) { delete ne; }
 

+ 13 - 13
code/AMF/AMFImporter_Postprocess.cpp

@@ -66,7 +66,7 @@ aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*
     aiColor4D tcol;
 
 	// Check if stored data are supported.
-	if(Composition.size() != 0)
+	if(!Composition.empty())
 	{
 		throw DeadlyImportError("IME. GetColor for composition");
 	}
@@ -321,7 +321,7 @@ void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace
     };
 
 	pOutputList_Separated.clear();
-	if(pInputList.size() == 0) return;
+	if(pInputList.empty()) return;
 
 	do
 	{
@@ -334,19 +334,19 @@ void AMFImporter::PostprocessHelper_SplitFacesByTextureID(std::list<SComplexFace
 			{
 				auto it_old = it;
 
-				it++;
+				++it;
 				face_list_cur.push_back(*it_old);
 				pInputList.erase(it_old);
 			}
 			else
 			{
-				it++;
+				++it;
 			}
 		}
 
-		if(face_list_cur.size() > 0) pOutputList_Separated.push_back(face_list_cur);
+		if(!face_list_cur.empty()) pOutputList_Separated.push_back(face_list_cur);
 
-	} while(pInputList.size() > 0);
+	} while(!pInputList.empty());
 }
 
 void AMFImporter::Postprocess_AddMetadata(const std::list<CAMFImporter_NodeElement_Metadata*>& metadataList, aiNode& sceneNode) const
@@ -712,7 +712,7 @@ std::list<unsigned int> mesh_idx;
 	}// for(const CAMFImporter_NodeElement* ne_child: pNodeElement.Child)
 
 	// if meshes was created then assign new indices with current aiNode
-	if(mesh_idx.size() > 0)
+	if(!mesh_idx.empty())
 	{
 		std::list<unsigned int>::const_iterator mit = mesh_idx.begin();
 
@@ -787,7 +787,7 @@ std::list<aiNode*> ch_node;
 	}// for(const CAMFImporter_NodeElement* ne: pConstellation.Child)
 
 	// copy found aiNode's as children
-	if(ch_node.size() == 0) throw DeadlyImportError("<constellation> must have at least one <instance>.");
+	if(ch_node.empty()) throw DeadlyImportError("<constellation> must have at least one <instance>.");
 
 	size_t ch_idx = 0;
 
@@ -883,13 +883,13 @@ nl_clean_loop:
 	if(node_list.size() > 1)
 	{
 		// walk through all nodes
-		for(std::list<aiNode*>::iterator nl_it = node_list.begin(); nl_it != node_list.end(); nl_it++)
+		for(std::list<aiNode*>::iterator nl_it = node_list.begin(); nl_it != node_list.end(); ++nl_it)
 		{
 			// and try to find them in another top nodes.
 			std::list<aiNode*>::const_iterator next_it = nl_it;
 
-			next_it++;
-			for(; next_it != node_list.end(); next_it++)
+			++next_it;
+			for(; next_it != node_list.end(); ++next_it)
 			{
 				if((*next_it)->FindNode((*nl_it)->mName) != nullptr)
 				{
@@ -907,7 +907,7 @@ nl_clean_loop:
 	//
 	//
 	// Nodes
-	if(node_list.size() > 0)
+	if(!node_list.empty())
 	{
 		std::list<aiNode*>::const_iterator nl_it = node_list.begin();
 
@@ -924,7 +924,7 @@ nl_clean_loop:
 
 	//
 	// Meshes
-	if(mesh_list.size() > 0)
+	if(!mesh_list.empty())
 	{
 		std::list<aiMesh*>::const_iterator ml_it = mesh_list.begin();
 

+ 4 - 2
code/CMakeLists.txt

@@ -104,6 +104,7 @@ SET( PUBLIC_HEADERS
   ${HEADER_PATH}/Exporter.hpp
   ${HEADER_PATH}/DefaultIOStream.h
   ${HEADER_PATH}/DefaultIOSystem.h
+  ${HEADER_PATH}/ZipArchiveIOSystem.h
   ${HEADER_PATH}/SceneCombiner.h
   ${HEADER_PATH}/fast_atof.h
   ${HEADER_PATH}/qnan.h
@@ -172,6 +173,7 @@ SET( Common_SRCS
   Common/DefaultProgressHandler.h
   Common/DefaultIOStream.cpp
   Common/DefaultIOSystem.cpp
+  Common/ZipArchiveIOSystem.cpp
   Common/PolyTools.h
   Common/Importer.cpp
   Common/IFF.h
@@ -688,8 +690,6 @@ ADD_ASSIMP_IMPORTER( Q3BSP
   Q3BSP/Q3BSPFileParser.cpp
   Q3BSP/Q3BSPFileImporter.h
   Q3BSP/Q3BSPFileImporter.cpp
-  Q3BSP/Q3BSPZipArchive.h
-  Q3BSP/Q3BSPZipArchive.cpp
 )
 
 ADD_ASSIMP_IMPORTER( RAW
@@ -766,6 +766,8 @@ ADD_ASSIMP_EXPORTER( X3D
 )
 
 ADD_ASSIMP_IMPORTER( GLTF
+  glTF/glTFCommon.h
+  glTF/glTFCommon.cpp
   glTF/glTFAsset.h
   glTF/glTFAsset.inl
   glTF/glTFAssetWriter.h

+ 4 - 3
code/Collada/ColladaExporter.cpp

@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "ColladaExporter.h"
 #include <assimp/Bitmap.h>
+#include <assimp/MathFunctions.h>
 #include <assimp/fast_atof.h>
 #include <assimp/SceneCombiner.h>
 #include <assimp/StringUtils.h>
@@ -155,7 +156,7 @@ void ColladaExporter::WriteFile() {
 // ------------------------------------------------------------------------------------------------
 // Writes the asset header
 void ColladaExporter::WriteHeader() {
-    static const ai_real epsilon = ai_real( 0.00001 );
+    static const ai_real epsilon = Math::getEpsilon<ai_real>();
     static const aiQuaternion x_rot(aiMatrix3x3(
         0, -1,  0,
         1,  0,  0,
@@ -317,7 +318,7 @@ void ColladaExporter::WriteTextures() {
 
             std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char*) texture->achFormatHint);
 
-            std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + name, "wb"));
+            std::unique_ptr<IOStream> outfile(mIOSystem->Open(mPath + mIOSystem->getOsSeparator() + name, "wb"));
             if(outfile == NULL) {
                 throw DeadlyExportError("could not open output texture file: " + mPath + name);
             }
@@ -1671,4 +1672,4 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
 }
 
 #endif
-#endif
+#endif

+ 2 - 6
code/Collada/ColladaHelper.h

@@ -580,15 +580,11 @@ struct Image
 {
     std::string mFileName;
 
-    /** If image file name is zero, embedded image data
-     */
+    /** Embedded image data */
     std::vector<uint8_t> mImageData;
 
-    /** If image file name is zero, file format of
-     *  embedded image data.
-     */
+    /** File format hint ofembedded image data */
     std::string mEmbeddedFormat;
-
 };
 
 /** An animation channel. */

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 251 - 222
code/Collada/ColladaLoader.cpp


+ 5 - 5
code/Collada/ColladaLoader.h

@@ -94,20 +94,20 @@ 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;
+    bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override;
 
 protected:
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details
      */
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc* GetInfo () const override;
 
-    void SetupProperties(const Importer* pImp);
+    void SetupProperties(const Importer* pImp) override;
 
     /** Imports the given file into the given scene structure.
      * See BaseImporter::InternReadFile() for details
      */
-    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
 
     /** Recursively constructs a scene node for the given parser node and returns it. */
     aiNode* BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode);
@@ -120,7 +120,7 @@ protected:
     void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode,
         aiNode* pTarget);
 		
-    aiMesh *findMesh(std::string meshid);
+    aiMesh *findMesh(const std::string& meshid);
 
     /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */
     aiMesh* CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 357 - 280
code/Collada/ColladaParser.cpp


+ 7 - 0
code/Collada/ColladaParser.h

@@ -54,6 +54,7 @@
 
 namespace Assimp
 {
+    class ZipArchiveIOSystem;
 
     // ------------------------------------------------------------------------------------------
     /** Parser helper class for the Collada loader.
@@ -75,6 +76,9 @@ namespace Assimp
         /** Destructor */
         ~ColladaParser();
 
+        /** Attempts to read the ZAE manifest and returns the DAE to open */
+        static std::string ReadZaeManifest(ZipArchiveIOSystem &zip_archive);
+
         /** Reads the contents of the file */
         void ReadContents();
 
@@ -235,6 +239,9 @@ namespace Assimp
         // Processes bind_vertex_input and bind elements
         void ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl);
 
+        /** Reads embedded textures from a ZAE archive*/
+        void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive);
+
     protected:
         /** Aborts the file reading with an exception */
         AI_WONT_RETURN void ThrowException( const std::string& pError) const AI_WONT_RETURN_SUFFIX;

+ 25 - 2
code/Common/BaseImporter.cpp

@@ -76,9 +76,25 @@ BaseImporter::~BaseImporter() {
     // nothing to do here
 }
 
+void BaseImporter::UpdateImporterScale( Importer* pImp )
+{
+    ai_assert(pImp != nullptr);
+    ai_assert(importerScale != 0.0);
+    ai_assert(fileScale != 0.0);
+
+    double activeScale = importerScale * fileScale;
+
+    // Set active scaling
+    pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, static_cast<float>( activeScale) );
+
+    ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f", activeScale );
+}
+
 // ------------------------------------------------------------------------------------------------
 // Imports the given file and returns the imported data.
-aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
+aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
+
+
     m_progress = pImp->GetProgressHandler();
     if (nullptr == m_progress) {
         return nullptr;
@@ -100,6 +116,11 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
     {
         InternReadFile( pFile, sc.get(), &filter);
 
+        // Calculate import scale hook - required because pImp not available anywhere else
+        // passes scale into ScaleProcess
+        UpdateImporterScale(pImp);
+
+
     } catch( const std::exception& err )    {
         // extract error description
         m_ErrorText = err.what();
@@ -112,7 +133,7 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
 }
 
 // ------------------------------------------------------------------------------------------------
-void BaseImporter::SetupProperties(const Importer* /*pImp*/)
+void BaseImporter::SetupProperties(const Importer* pImp)
 {
     // the default implementation does nothing
 }
@@ -588,6 +609,8 @@ aiScene* BatchLoader::GetImport( unsigned int which )
     return nullptr;
 }
 
+
+
 // ------------------------------------------------------------------------------------------------
 void BatchLoader::LoadAll()
 {

+ 60 - 101
code/Common/DefaultIOSystem.cpp

@@ -61,83 +61,66 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 using namespace Assimp;
 
-// maximum path length
-// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
-#ifdef PATH_MAX
-#   define PATHLIMIT PATH_MAX
-#else
-#   define PATHLIMIT 4096
+#ifdef _WIN32
+static std::wstring Utf8ToWide(const char* in)
+{
+    int size = MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0);
+    // size includes terminating null; std::wstring adds null automatically
+    std::wstring out(static_cast<size_t>(size) - 1, L'\0');
+    MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size);
+    return out;
+}
+
+static std::string WideToUtf8(const wchar_t* in)
+{
+    int size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
+    // size includes terminating null; std::string adds null automatically
+    std::string out(static_cast<size_t>(size) - 1, '\0');
+    WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr);
+    return out;
+}
 #endif
 
 // ------------------------------------------------------------------------------------------------
 // Tests for the existence of a file at the given path.
-bool DefaultIOSystem::Exists( const char* pFile) const
+bool DefaultIOSystem::Exists(const char* pFile) const
 {
 #ifdef _WIN32
-    wchar_t fileName16[PATHLIMIT];
-
-#ifndef WindowsStore
-    bool isUnicode = IsTextUnicode(pFile, static_cast<int>(strlen(pFile)), NULL) != 0;
-    if (isUnicode) {
-
-        MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT);
-        struct __stat64 filestat;
-        if (0 != _wstat64(fileName16, &filestat)) {
-            return false;
-        }
-    } else {
-#endif
-        FILE* file = ::fopen(pFile, "rb");
-        if (!file)
-            return false;
-
-        ::fclose(file);
-#ifndef WindowsStore
+    struct __stat64 filestat;
+    if (_wstat64(Utf8ToWide(pFile).c_str(), &filestat) != 0) {
+        return false;
     }
-#endif
 #else
-    FILE* file = ::fopen( pFile, "rb");
-    if( !file)
+    FILE* file = ::fopen(pFile, "rb");
+    if (!file)
         return false;
 
-    ::fclose( file);
+    ::fclose(file);
 #endif
     return true;
 }
 
 // ------------------------------------------------------------------------------------------------
 // Open a new file with a given path.
-IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode)
+IOStream* DefaultIOSystem::Open(const char* strFile, const char* strMode)
 {
-    ai_assert(NULL != strFile);
-    ai_assert(NULL != strMode);
+    ai_assert(strFile != nullptr);
+    ai_assert(strMode != nullptr);
     FILE* file;
 #ifdef _WIN32
-    wchar_t fileName16[PATHLIMIT];
-#ifndef WindowsStore
-    bool isUnicode = IsTextUnicode(strFile, static_cast<int>(strlen(strFile)), NULL) != 0;
-    if (isUnicode) {
-        MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT);
-        std::string mode8(strMode);
-        file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str());
-    } else {
-#endif
-        file = ::fopen(strFile, strMode);
-#ifndef WindowsStore
-    }
-#endif
+    file = ::_wfopen(Utf8ToWide(strFile).c_str(), Utf8ToWide(strMode).c_str());
 #else
     file = ::fopen(strFile, strMode);
 #endif
-    if (nullptr == file)
+    if (!file)
         return nullptr;
 
-    return new DefaultIOStream(file, (std::string) strFile);
+    return new DefaultIOStream(file, strFile);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Closes the given file and releases all resources associated with it.
-void DefaultIOSystem::Close( IOStream* pFile)
+void DefaultIOSystem::Close(IOStream* pFile)
 {
     delete pFile;
 }
@@ -155,78 +138,56 @@ char DefaultIOSystem::getOsSeparator() const
 
 // ------------------------------------------------------------------------------------------------
 // IOSystem default implementation (ComparePaths isn't a pure virtual function)
-bool IOSystem::ComparePaths (const char* one, const char* second) const
+bool IOSystem::ComparePaths(const char* one, const char* second) const
 {
-    return !ASSIMP_stricmp(one,second);
+    return !ASSIMP_stricmp(one, second);
 }
 
 // ------------------------------------------------------------------------------------------------
 // Convert a relative path into an absolute path
-inline static void MakeAbsolutePath (const char* in, char* _out)
+inline static std::string MakeAbsolutePath(const char* in)
 {
-    ai_assert(in && _out);
-#if defined( _MSC_VER ) || defined( __MINGW32__ )
-#ifndef WindowsStore
-    bool isUnicode = IsTextUnicode(in, static_cast<int>(strlen(in)), NULL) != 0;
-    if (isUnicode) {
-        wchar_t out16[PATHLIMIT];
-        wchar_t in16[PATHLIMIT];
-        MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT);
-        wchar_t* ret = ::_wfullpath(out16, in16, PATHLIMIT);
-        if (ret) {
-            WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr);
-        }
-        if (!ret) {
-            // preserve the input path, maybe someone else is able to fix
-            // the path before it is accessed (e.g. our file system filter)
-            ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
-            strcpy(_out, in);
-        }
-
-    } else {
-#endif
-        char* ret = :: _fullpath(_out, in, PATHLIMIT);
-        if (!ret) {
-            // preserve the input path, maybe someone else is able to fix
-            // the path before it is accessed (e.g. our file system filter)
-            ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
-            strcpy(_out, in);
-        }
-#ifndef WindowsStore
+    ai_assert(in);
+    std::string out;
+#ifdef _WIN32
+    wchar_t* ret = ::_wfullpath(nullptr, Utf8ToWide(in).c_str(), 0);
+    if (ret) {
+        out = WideToUtf8(ret);
+        free(ret);
     }
-#endif
 #else
-    // use realpath
-    char* ret = realpath(in, _out);
-    if(!ret) {
+    char* ret = realpath(in, nullptr);
+    if (ret) {
+        out = ret;
+        free(ret);
+    }
+#endif
+    if (!ret) {
         // preserve the input path, maybe someone else is able to fix
         // the path before it is accessed (e.g. our file system filter)
         ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
-        strcpy(_out,in);
+        out = in;
     }
-#endif
+    return out;
 }
 
 // ------------------------------------------------------------------------------------------------
 // DefaultIOSystem's more specialized implementation
-bool DefaultIOSystem::ComparePaths (const char* one, const char* second) const
+bool DefaultIOSystem::ComparePaths(const char* one, const char* second) const
 {
     // chances are quite good both paths are formatted identically,
     // so we can hopefully return here already
-    if( !ASSIMP_stricmp(one,second) )
+    if (!ASSIMP_stricmp(one, second))
         return true;
 
-    char temp1[PATHLIMIT];
-    char temp2[PATHLIMIT];
-
-    MakeAbsolutePath (one, temp1);
-    MakeAbsolutePath (second, temp2);
+    std::string temp1 = MakeAbsolutePath(one);
+    std::string temp2 = MakeAbsolutePath(second);
 
-    return !ASSIMP_stricmp(temp1,temp2);
+    return !ASSIMP_stricmp(temp1, temp2);
 }
 
 // ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::fileName( const std::string &path )
+std::string DefaultIOSystem::fileName(const std::string& path)
 {
     std::string ret = path;
     std::size_t last = ret.find_last_of("\\/");
@@ -235,16 +196,16 @@ std::string DefaultIOSystem::fileName( const std::string &path )
 }
 
 // ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::completeBaseName( const std::string &path )
+std::string DefaultIOSystem::completeBaseName(const std::string& path)
 {
     std::string ret = fileName(path);
     std::size_t pos = ret.find_last_of('.');
-    if(pos != ret.npos) ret = ret.substr(0, pos);
+    if (pos != std::string::npos) ret = ret.substr(0, pos);
     return ret;
 }
 
 // ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::absolutePath( const std::string &path )
+std::string DefaultIOSystem::absolutePath(const std::string& path)
 {
     std::string ret = path;
     std::size_t last = ret.find_last_of("\\/");
@@ -253,5 +214,3 @@ std::string DefaultIOSystem::absolutePath( const std::string &path )
 }
 
 // ------------------------------------------------------------------------------------------------
-
-#undef PATHLIMIT

+ 5 - 30
code/Common/Exporter.cpp

@@ -315,34 +315,6 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha
     return pimpl->blob;
 }
 
-// ------------------------------------------------------------------------------------------------
-bool IsVerboseFormat(const aiMesh* mesh) {
-    // avoid slow vector<bool> specialization
-    std::vector<unsigned int> seen(mesh->mNumVertices,0);
-    for(unsigned int i = 0; i < mesh->mNumFaces; ++i) {
-        const aiFace& f = mesh->mFaces[i];
-        for(unsigned int j = 0; j < f.mNumIndices; ++j) {
-            if(++seen[f.mIndices[j]] == 2) {
-                // found a duplicate index
-                return false;
-            }
-        }
-    }
-
-    return true;
-}
-
-// ------------------------------------------------------------------------------------------------
-bool IsVerboseFormat(const aiScene* pScene) {
-    for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
-        if(!IsVerboseFormat(pScene->mMeshes[i])) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
 // ------------------------------------------------------------------------------------------------
 aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath,
         unsigned int pPreprocessing, const ExportProperties* pProperties) {
@@ -352,7 +324,7 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
     // format. They will likely not be aware that there is a flag in the scene to indicate
     // this, however. To avoid surprises and bug reports, we check for duplicates in
     // meshes upfront.
-    const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || IsVerboseFormat(pScene);
+    const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || MakeVerboseFormatProcess::IsVerboseFormat(pScene);
 
     pimpl->mProgressHandler->UpdateFileWrite(0, 4);
 
@@ -472,7 +444,10 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
                 }
 
                 ExportProperties emptyProperties;  // Never pass NULL ExportProperties so Exporters don't have to worry.
-                exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties);
+                ExportProperties* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties;
+                                pProp->SetPropertyBool("bJoinIdenticalVertices", must_join_again);
+                                exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
+                exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
 
                 pimpl->mProgressHandler->UpdateFileWrite(4, 4);
             } catch (DeadlyExportError& err) {

+ 29 - 0
code/Common/SceneCombiner.cpp

@@ -1091,6 +1091,35 @@ void SceneCombiner::Copy( aiMesh** _dest, const aiMesh* src ) {
         aiFace& f = dest->mFaces[i];
         GetArrayCopy(f.mIndices,f.mNumIndices);
     }
+
+    // make a deep copy of all blend shapes
+    CopyPtrArray(dest->mAnimMeshes, dest->mAnimMeshes, dest->mNumAnimMeshes);
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy(aiAnimMesh** _dest, const aiAnimMesh* src) {
+    if (nullptr == _dest || nullptr == src) {
+        return;
+    }
+
+    aiAnimMesh* dest = *_dest = new aiAnimMesh();
+
+    // get a flat copy
+    ::memcpy(dest, src, sizeof(aiAnimMesh));
+
+    // and reallocate all arrays
+    GetArrayCopy(dest->mVertices, dest->mNumVertices);
+    GetArrayCopy(dest->mNormals, dest->mNumVertices);
+    GetArrayCopy(dest->mTangents, dest->mNumVertices);
+    GetArrayCopy(dest->mBitangents, dest->mNumVertices);
+
+    unsigned int n = 0;
+    while (dest->HasTextureCoords(n))
+        GetArrayCopy(dest->mTextureCoords[n++], dest->mNumVertices);
+
+    n = 0;
+    while (dest->HasVertexColors(n))
+        GetArrayCopy(dest->mColors[n++], dest->mNumVertices);
 }
 
 // ------------------------------------------------------------------------------------------------

+ 539 - 0
code/Common/ZipArchiveIOSystem.cpp

@@ -0,0 +1,539 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  ZipArchiveIOSystem.cpp
+ *  @brief Zip File I/O implementation for #Importer
+ */
+
+#include <assimp/ZipArchiveIOSystem.h>
+#include <assimp/BaseImporter.h>
+
+#include <assimp/ai_assert.h>
+
+#include <map>
+#include <memory>
+
+#ifdef ASSIMP_USE_HUNTER
+#  include <minizip/unzip.h>
+#else
+#  include <unzip.h>
+#endif
+
+namespace Assimp {
+    // ----------------------------------------------------------------
+    // Wraps an existing Assimp::IOSystem for unzip
+    class IOSystem2Unzip {
+    public:
+        static voidpf open(voidpf opaque, const char* filename, int mode);
+        static uLong read(voidpf opaque, voidpf stream, void* buf, uLong size);
+        static uLong write(voidpf opaque, voidpf stream, const void* buf, uLong size);
+        static long tell(voidpf opaque, voidpf stream);
+        static long seek(voidpf opaque, voidpf stream, uLong offset, int origin);
+        static int close(voidpf opaque, voidpf stream);
+        static int testerror(voidpf opaque, voidpf stream);
+        static zlib_filefunc_def get(IOSystem* pIOHandler);
+    };
+
+    voidpf IOSystem2Unzip::open(voidpf opaque, const char* filename, int mode) {
+        IOSystem* io_system = reinterpret_cast<IOSystem*>(opaque);
+
+        const char* mode_fopen = nullptr;
+        if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
+            mode_fopen = "rb";
+        }
+        else {
+            if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
+                mode_fopen = "r+b";
+            }
+            else {
+                if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
+                    mode_fopen = "wb";
+                }
+            }
+        }
+
+        return (voidpf)io_system->Open(filename, mode_fopen);
+    }
+
+    uLong IOSystem2Unzip::read(voidpf /*opaque*/, voidpf stream, void* buf, uLong size) {
+        IOStream* io_stream = (IOStream*)stream;
+
+        return static_cast<uLong>(io_stream->Read(buf, 1, size));
+    }
+
+    uLong IOSystem2Unzip::write(voidpf /*opaque*/, voidpf stream, const void* buf, uLong size) {
+        IOStream* io_stream = (IOStream*)stream;
+
+        return static_cast<uLong>(io_stream->Write(buf, 1, size));
+    }
+
+    long IOSystem2Unzip::tell(voidpf /*opaque*/, voidpf stream) {
+        IOStream* io_stream = (IOStream*)stream;
+
+        return static_cast<long>(io_stream->Tell());
+    }
+
+    long IOSystem2Unzip::seek(voidpf /*opaque*/, voidpf stream, uLong offset, int origin) {
+        IOStream* io_stream = (IOStream*)stream;
+
+        aiOrigin assimp_origin;
+        switch (origin) {
+        default:
+        case ZLIB_FILEFUNC_SEEK_CUR:
+            assimp_origin = aiOrigin_CUR;
+            break;
+        case ZLIB_FILEFUNC_SEEK_END:
+            assimp_origin = aiOrigin_END;
+            break;
+        case ZLIB_FILEFUNC_SEEK_SET:
+            assimp_origin = aiOrigin_SET;
+            break;
+        }
+
+        return (io_stream->Seek(offset, assimp_origin) == aiReturn_SUCCESS ? 0 : -1);
+    }
+
+    int IOSystem2Unzip::close(voidpf opaque, voidpf stream) {
+        IOSystem* io_system = (IOSystem*)opaque;
+        IOStream* io_stream = (IOStream*)stream;
+
+        io_system->Close(io_stream);
+
+        return 0;
+    }
+
+    int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) {
+        return 0;
+    }
+
+    zlib_filefunc_def IOSystem2Unzip::get(IOSystem* pIOHandler) {
+        zlib_filefunc_def mapping;
+
+#ifdef ASSIMP_USE_HUNTER
+        mapping.zopen_file = (open_file_func)open;
+        mapping.zread_file = (read_file_func)read;
+        mapping.zwrite_file = (write_file_func)write;
+        mapping.ztell_file = (tell_file_func)tell;
+        mapping.zseek_file = (seek_file_func)seek;
+        mapping.zclose_file = (close_file_func)close;
+        mapping.zerror_file = (error_file_func)testerror;
+#else
+        mapping.zopen_file = open;
+        mapping.zread_file = read;
+        mapping.zwrite_file = write;
+        mapping.ztell_file = tell;
+        mapping.zseek_file = seek;
+        mapping.zclose_file = close;
+        mapping.zerror_file = testerror;
+#endif
+        mapping.opaque = reinterpret_cast<voidpf>(pIOHandler);
+
+        return mapping;
+    }
+
+    // ----------------------------------------------------------------
+    // A read-only file inside a ZIP
+
+    class ZipFile : public IOStream {
+        friend class ZipFileInfo;
+        explicit ZipFile(size_t size);
+    public:
+        virtual ~ZipFile();
+
+        // IOStream interface
+        size_t Read(void* pvBuffer, size_t pSize, size_t pCount) override;
+        size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) override { return 0; }
+        size_t FileSize() const override;
+        aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override;
+        size_t Tell() const override;
+        void Flush() override {}
+
+    private:
+        size_t m_Size = 0;
+        size_t m_SeekPtr = 0;
+        std::unique_ptr<uint8_t[]> m_Buffer;
+    };
+
+
+    // ----------------------------------------------------------------
+    // Info about a read-only file inside a ZIP
+    class ZipFileInfo
+    {
+    public:
+        explicit ZipFileInfo(unzFile zip_handle, size_t size);
+
+        // Allocate and Extract data from the ZIP
+        ZipFile * Extract(unzFile zip_handle) const;
+
+    private:
+        size_t m_Size = 0;
+        unz_file_pos_s m_ZipFilePos;
+    };
+
+    ZipFileInfo::ZipFileInfo(unzFile zip_handle, size_t size)
+        : m_Size(size) {
+        ai_assert(m_Size != 0);
+        // Workaround for MSVC 2013 - C2797
+        m_ZipFilePos.num_of_file = 0;
+        m_ZipFilePos.pos_in_zip_directory = 0;
+        unzGetFilePos(zip_handle, &(m_ZipFilePos));
+    }
+
+    ZipFile * ZipFileInfo::Extract(unzFile zip_handle) const {
+        // Find in the ZIP. This cannot fail
+        unz_file_pos_s *filepos = const_cast<unz_file_pos_s*>(&(m_ZipFilePos));
+        if (unzGoToFilePos(zip_handle, filepos) != UNZ_OK)
+            return nullptr;
+
+        if (unzOpenCurrentFile(zip_handle) != UNZ_OK)
+            return nullptr;
+
+        ZipFile *zip_file = new ZipFile(m_Size);
+
+        if (unzReadCurrentFile(zip_handle, zip_file->m_Buffer.get(), static_cast<unsigned int>(m_Size)) != static_cast<int>(m_Size))
+        {
+            // Failed, release the memory
+            delete zip_file;
+            zip_file = nullptr;
+        }
+
+        ai_assert(unzCloseCurrentFile(zip_handle) == UNZ_OK);
+        return zip_file;
+    }
+
+    ZipFile::ZipFile(size_t size)
+        : m_Size(size) {
+        ai_assert(m_Size != 0);
+        m_Buffer = std::unique_ptr<uint8_t[]>(new uint8_t[m_Size]);
+    }
+
+    ZipFile::~ZipFile() {
+    }
+
+    size_t ZipFile::Read(void* pvBuffer, size_t pSize, size_t pCount) {
+        // Should be impossible
+        ai_assert(m_Buffer != nullptr);
+        ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
+
+        // Clip down to file size
+        size_t byteSize = pSize * pCount;
+        if ((byteSize + m_SeekPtr) > m_Size)
+        {
+            pCount = (m_Size - m_SeekPtr) / pSize;
+            byteSize = pSize * pCount;
+            if (byteSize == 0)
+                return 0;
+        }
+
+        std::memcpy(pvBuffer, m_Buffer.get() + m_SeekPtr, byteSize);
+
+        m_SeekPtr += byteSize;
+
+        return pCount;
+    }
+
+    size_t ZipFile::FileSize() const {
+        return m_Size;
+    }
+
+    aiReturn ZipFile::Seek(size_t pOffset, aiOrigin pOrigin) {
+        switch (pOrigin)
+        {
+        case aiOrigin_SET: {
+            if (pOffset > m_Size) return aiReturn_FAILURE;
+            m_SeekPtr = pOffset;
+            return aiReturn_SUCCESS;
+        }
+
+        case aiOrigin_CUR: {
+            if ((pOffset + m_SeekPtr) > m_Size) return aiReturn_FAILURE;
+            m_SeekPtr += pOffset;
+            return aiReturn_SUCCESS;
+        }
+
+        case aiOrigin_END: {
+            if (pOffset > m_Size) return aiReturn_FAILURE;
+            m_SeekPtr = m_Size - pOffset;
+            return aiReturn_SUCCESS;
+        }
+        default:;
+        }
+
+        return aiReturn_FAILURE;
+    }
+
+    size_t ZipFile::Tell() const {
+        return m_SeekPtr;
+    }
+
+    // ----------------------------------------------------------------
+    // pImpl of the Zip Archive IO
+    class ZipArchiveIOSystem::Implement {
+    public:
+        static const unsigned int FileNameSize = 256;
+
+        Implement(IOSystem* pIOHandler, const char* pFilename, const char* pMode);
+        ~Implement();
+
+        bool isOpen() const;
+        void getFileList(std::vector<std::string>& rFileList);
+        void getFileListExtension(std::vector<std::string>& rFileList, const std::string& extension);
+        bool Exists(std::string& filename);
+        IOStream* OpenFile(std::string& filename);
+
+        static void SimplifyFilename(std::string& filename);
+
+    private:
+        void MapArchive();
+
+    private:
+        typedef std::map<std::string, ZipFileInfo> ZipFileInfoMap;
+
+        unzFile m_ZipFileHandle = nullptr;
+        ZipFileInfoMap m_ArchiveMap;
+    };
+
+    ZipArchiveIOSystem::Implement::Implement(IOSystem* pIOHandler, const char* pFilename, const char* pMode) {
+        ai_assert(strcmp(pMode, "r") == 0);
+        ai_assert(pFilename != nullptr);
+        if (pFilename[0] == 0)
+            return;
+
+        zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler);
+        m_ZipFileHandle = unzOpen2(pFilename, &mapping);
+    }
+
+    ZipArchiveIOSystem::Implement::~Implement() {
+        m_ArchiveMap.clear();
+
+        if (m_ZipFileHandle != nullptr) {
+            unzClose(m_ZipFileHandle);
+            m_ZipFileHandle = nullptr;
+        }
+    }
+
+    void ZipArchiveIOSystem::Implement::MapArchive() {
+        if (m_ZipFileHandle == nullptr)
+            return;
+
+        if (!m_ArchiveMap.empty())
+            return;
+
+        //  At first ensure file is already open
+        if (unzGoToFirstFile(m_ZipFileHandle) != UNZ_OK)
+            return;
+
+        // Loop over all files
+        do {
+            char filename[FileNameSize];
+            unz_file_info fileInfo;
+
+            if (unzGetCurrentFileInfo(m_ZipFileHandle, &fileInfo, filename, FileNameSize, nullptr, 0, nullptr, 0) == UNZ_OK) {
+                if (fileInfo.uncompressed_size != 0) {
+                    std::string filename_string(filename, fileInfo.size_filename);
+                    SimplifyFilename(filename_string);
+                    m_ArchiveMap.emplace(filename_string, ZipFileInfo(m_ZipFileHandle, fileInfo.uncompressed_size));
+                }
+            }
+        } while (unzGoToNextFile(m_ZipFileHandle) != UNZ_END_OF_LIST_OF_FILE);
+    }
+
+    bool ZipArchiveIOSystem::Implement::isOpen() const {
+        return (m_ZipFileHandle != nullptr);
+    }
+
+    void ZipArchiveIOSystem::Implement::getFileList(std::vector<std::string>& rFileList) {
+        MapArchive();
+        rFileList.clear();
+
+        for (const auto &file : m_ArchiveMap) {
+            rFileList.push_back(file.first);
+        }
+    }
+
+    void ZipArchiveIOSystem::Implement::getFileListExtension(std::vector<std::string>& rFileList, const std::string& extension) {
+        MapArchive();
+        rFileList.clear();
+
+        for (const auto &file : m_ArchiveMap) {
+            if (extension == BaseImporter::GetExtension(file.first))
+                rFileList.push_back(file.first);
+        }
+    }
+
+    bool ZipArchiveIOSystem::Implement::Exists(std::string& filename) {
+        MapArchive();
+
+        ZipFileInfoMap::const_iterator it = m_ArchiveMap.find(filename);
+        return (it != m_ArchiveMap.end());
+    }
+
+    IOStream * ZipArchiveIOSystem::Implement::OpenFile(std::string& filename) {
+        MapArchive();
+
+        SimplifyFilename(filename);
+
+        // Find in the map
+        ZipFileInfoMap::const_iterator zip_it = m_ArchiveMap.find(filename);
+        if (zip_it == m_ArchiveMap.cend())
+            return nullptr;
+
+        const ZipFileInfo &zip_file = (*zip_it).second;
+        return zip_file.Extract(m_ZipFileHandle);
+    }
+
+    inline void ReplaceAll(std::string& data, const std::string& before, const std::string& after) {
+        size_t pos = data.find(before);
+        while (pos != std::string::npos)
+        {
+            data.replace(pos, before.size(), after);
+            pos = data.find(before, pos + after.size());
+        }
+    }
+
+    inline void ReplaceAllChar(std::string& data, const char before, const char after) {
+        size_t pos = data.find(before);
+        while (pos != std::string::npos)
+        {
+            data[pos] = after;
+            pos = data.find(before, pos + 1);
+        }
+    }
+
+    void ZipArchiveIOSystem::Implement::SimplifyFilename(std::string& filename)
+    {
+        ReplaceAllChar(filename, '\\', '/');
+
+        // Remove all . and / from the beginning of the path
+        size_t pos = filename.find_first_not_of("./");
+        if (pos != 0)
+            filename.erase(0, pos);
+
+        // Simplify "my/folder/../file.png" constructions, if any
+        static const std::string relative("/../");
+        const size_t relsize = relative.size() - 1;
+        pos = filename.find(relative);
+        while (pos != std::string::npos)
+        {
+            // Previous slash
+            size_t prevpos = filename.rfind('/', pos - 1);
+            if (prevpos == pos)
+                filename.erase(0, pos + relative.size());
+            else
+                filename.erase(prevpos, pos + relsize - prevpos);
+
+            pos = filename.find(relative);
+        }
+    }
+
+    ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem* pIOHandler, const char* pFilename, const char* pMode)
+        : pImpl(new Implement(pIOHandler, pFilename, pMode)) {
+    }
+
+    // ----------------------------------------------------------------
+    // The ZipArchiveIO
+    ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem* pIOHandler, const std::string& rFilename, const char* pMode)
+        : pImpl(new Implement(pIOHandler, rFilename.c_str(), pMode))
+    {
+    }
+
+    ZipArchiveIOSystem::~ZipArchiveIOSystem() {
+        delete pImpl;
+    }
+
+    bool ZipArchiveIOSystem::Exists(const char* pFilename) const {
+        ai_assert(pFilename != nullptr);
+
+        if (pFilename == nullptr) {
+            return false;
+        }
+
+        std::string filename(pFilename);
+        return pImpl->Exists(filename);
+    }
+
+    // This is always '/' in a ZIP
+    char ZipArchiveIOSystem::getOsSeparator() const {
+        return '/';
+    }
+
+    // Only supports Reading
+    IOStream * ZipArchiveIOSystem::Open(const char* pFilename, const char* pMode) {
+        ai_assert(pFilename != nullptr);
+
+        for (size_t i = 0; pMode[i] != 0; ++i)
+        {
+            ai_assert(pMode[i] != 'w');
+            if (pMode[i] == 'w')
+                return nullptr;
+        }
+
+        std::string filename(pFilename);
+        return pImpl->OpenFile(filename);
+    }
+
+    void ZipArchiveIOSystem::Close(IOStream* pFile) {
+        delete pFile;
+    }
+
+    bool ZipArchiveIOSystem::isOpen() const {
+        return (pImpl->isOpen());
+    }
+
+    void ZipArchiveIOSystem::getFileList(std::vector<std::string>& rFileList) const {
+        return pImpl->getFileList(rFileList);
+    }
+
+    void ZipArchiveIOSystem::getFileListExtension(std::vector<std::string>& rFileList, const std::string& extension) const {
+        return pImpl->getFileListExtension(rFileList, extension);
+    }
+
+    bool ZipArchiveIOSystem::isZipArchive(IOSystem* pIOHandler, const char* pFilename) {
+        Implement tmp(pIOHandler, pFilename, "r");
+        return tmp.isOpen();
+    }
+
+    bool ZipArchiveIOSystem::isZipArchive(IOSystem* pIOHandler, const std::string& rFilename) {
+        return isZipArchive(pIOHandler, rFilename.c_str());
+    }
+
+}

+ 51 - 62
code/FBX/FBXConverter.cpp

@@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "FBXImporter.h"
 
 #include <assimp/StringComparison.h>
+#include <assimp/MathFunctions.h>
 
 #include <assimp/scene.h>
 
@@ -66,6 +67,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <vector>
 #include <sstream>
 #include <iomanip>
+#include <cstdint>
 
 
 namespace Assimp {
@@ -77,7 +79,7 @@ namespace Assimp {
 
 #define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
 
-        FBXConverter::FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit )
+        FBXConverter::FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones )
         : defaultMaterialIndex()
         , lights()
         , cameras()
@@ -89,8 +91,7 @@ namespace Assimp {
         , mNodeNames()
         , anim_fps()
         , out(out)
-        , doc(doc)
-        , mCurrentUnit(FbxUnit::cm) {
+        , doc(doc) {
             // animations need to be converted first since this will
             // populate the node_anim_chain_bits map, which is needed
             // to determine which nodes need to be generated.
@@ -118,7 +119,6 @@ namespace Assimp {
 
             ConvertGlobalSettings();
             TransferDataToScene();
-            ConvertToUnitScale(unit);
 
             // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
             // to make sure the scene passes assimp's validation. FBX files
@@ -554,7 +554,7 @@ namespace Assimp {
                 return;
             }
 
-            const float angle_epsilon = 1e-6f;
+            const float angle_epsilon = Math::getEpsilon<float>();
 
             out = aiMatrix4x4();
 
@@ -684,30 +684,37 @@ namespace Assimp {
             bool ok;
 
             aiMatrix4x4 chain[TransformationComp_MAXIMUM];
+
+            ai_assert(TransformationComp_MAXIMUM < 32);
+            std::uint32_t chainBits = 0;
+            // A node won't need a node chain if it only has these.
+            const std::uint32_t chainMaskSimple = (1 << TransformationComp_Translation) + (1 << TransformationComp_Scaling) + (1 << TransformationComp_Rotation);
+            // A node will need a node chain if it has any of these.
+            const std::uint32_t chainMaskComplex = ((1 << (TransformationComp_MAXIMUM)) - 1) - chainMaskSimple;
+
             std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
 
             // generate transformation matrices for all the different transformation components
-            const float zero_epsilon = 1e-6f;
+            const float zero_epsilon = Math::getEpsilon<float>();
             const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
-            bool is_complex = false;
 
             const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
             if (ok && PreRotation.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_PreRotation);
 
                 GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[TransformationComp_PreRotation]);
             }
 
             const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props, "PostRotation", ok);
             if (ok && PostRotation.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_PostRotation);
 
                 GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[TransformationComp_PostRotation]);
             }
 
             const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props, "RotationPivot", ok);
             if (ok && RotationPivot.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_RotationPivot) | (1 << TransformationComp_RotationPivotInverse);
 
                 aiMatrix4x4::Translation(RotationPivot, chain[TransformationComp_RotationPivot]);
                 aiMatrix4x4::Translation(-RotationPivot, chain[TransformationComp_RotationPivotInverse]);
@@ -715,21 +722,21 @@ namespace Assimp {
 
             const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props, "RotationOffset", ok);
             if (ok && RotationOffset.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_RotationOffset);
 
                 aiMatrix4x4::Translation(RotationOffset, chain[TransformationComp_RotationOffset]);
             }
 
             const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props, "ScalingOffset", ok);
             if (ok && ScalingOffset.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_ScalingOffset);
 
                 aiMatrix4x4::Translation(ScalingOffset, chain[TransformationComp_ScalingOffset]);
             }
 
             const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props, "ScalingPivot", ok);
             if (ok && ScalingPivot.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_ScalingPivot) | (1 << TransformationComp_ScalingPivotInverse);
 
                 aiMatrix4x4::Translation(ScalingPivot, chain[TransformationComp_ScalingPivot]);
                 aiMatrix4x4::Translation(-ScalingPivot, chain[TransformationComp_ScalingPivotInverse]);
@@ -737,22 +744,28 @@ namespace Assimp {
 
             const aiVector3D& Translation = PropertyGet<aiVector3D>(props, "Lcl Translation", ok);
             if (ok && Translation.SquareLength() > zero_epsilon) {
+                chainBits = chainBits | (1 << TransformationComp_Translation);
+
                 aiMatrix4x4::Translation(Translation, chain[TransformationComp_Translation]);
             }
 
             const aiVector3D& Scaling = PropertyGet<aiVector3D>(props, "Lcl Scaling", ok);
             if (ok && (Scaling - all_ones).SquareLength() > zero_epsilon) {
+                chainBits = chainBits | (1 << TransformationComp_Scaling);
+
                 aiMatrix4x4::Scaling(Scaling, chain[TransformationComp_Scaling]);
             }
 
             const aiVector3D& Rotation = PropertyGet<aiVector3D>(props, "Lcl Rotation", ok);
             if (ok && Rotation.SquareLength() > zero_epsilon) {
+                chainBits = chainBits | (1 << TransformationComp_Rotation);
+
                 GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
             }
 
             const aiVector3D& GeometricScaling = PropertyGet<aiVector3D>(props, "GeometricScaling", ok);
             if (ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_GeometricScaling);
                 aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]);
                 aiVector3D GeometricScalingInverse = GeometricScaling;
                 bool canscale = true;
@@ -767,13 +780,14 @@ namespace Assimp {
                     }
                 }
                 if (canscale) {
+                    chainBits = chainBits | (1 << TransformationComp_GeometricScalingInverse);
                     aiMatrix4x4::Scaling(GeometricScalingInverse, chain[TransformationComp_GeometricScalingInverse]);
                 }
             }
 
             const aiVector3D& GeometricRotation = PropertyGet<aiVector3D>(props, "GeometricRotation", ok);
             if (ok && GeometricRotation.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_GeometricRotation) | (1 << TransformationComp_GeometricRotationInverse);
                 GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotation]);
                 GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotationInverse]);
                 chain[TransformationComp_GeometricRotationInverse].Inverse();
@@ -781,7 +795,7 @@ namespace Assimp {
 
             const aiVector3D& GeometricTranslation = PropertyGet<aiVector3D>(props, "GeometricTranslation", ok);
             if (ok && GeometricTranslation.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_GeometricTranslation) | (1 << TransformationComp_GeometricTranslationInverse);
                 aiMatrix4x4::Translation(GeometricTranslation, chain[TransformationComp_GeometricTranslation]);
                 aiMatrix4x4::Translation(-GeometricTranslation, chain[TransformationComp_GeometricTranslationInverse]);
             }
@@ -789,12 +803,12 @@ namespace Assimp {
             // is_complex needs to be consistent with NeedsComplexTransformationChain()
             // or the interplay between this code and the animation converter would
             // not be guaranteed.
-            ai_assert(NeedsComplexTransformationChain(model) == is_complex);
+            ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0));
 
             // now, if we have more than just Translation, Scaling and Rotation,
             // we need to generate a full node chain to accommodate for assimp's
             // lack to express pivots and offsets.
-            if (is_complex && doc.Settings().preservePivots) {
+            if ((chainBits & chainMaskComplex) && doc.Settings().preservePivots) {
                 FBXImporter::LogInfo("generating full transformation chain for node: " + name);
 
                 // query the anim_chain_bits dictionary to find out which chain elements
@@ -807,7 +821,7 @@ namespace Assimp {
                 for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
                     const TransformationComp comp = static_cast<TransformationComp>(i);
 
-                    if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) {
+                    if ((chainBits & bit) == 0 && (anim_chain_bitmask & bit) == 0) {
                         continue;
                     }
 
@@ -1988,6 +2002,21 @@ namespace Assimp {
             TrySetTextureProperties(out_mat, textures, "Maya|SpecularTexture", aiTextureType_SPECULAR, mesh);
             TrySetTextureProperties(out_mat, textures, "Maya|FalloffTexture", aiTextureType_OPACITY, mesh);
             TrySetTextureProperties(out_mat, textures, "Maya|ReflectionMapTexture", aiTextureType_REFLECTION, mesh);
+            
+            // Maya PBR
+            TrySetTextureProperties(out_mat, textures, "Maya|baseColor|file", aiTextureType_BASE_COLOR, mesh);
+            TrySetTextureProperties(out_mat, textures, "Maya|normalCamera|file", aiTextureType_NORMAL_CAMERA, mesh);
+            TrySetTextureProperties(out_mat, textures, "Maya|emissionColor|file", aiTextureType_EMISSION_COLOR, mesh);
+            TrySetTextureProperties(out_mat, textures, "Maya|metalness|file", aiTextureType_METALNESS, mesh);
+            TrySetTextureProperties(out_mat, textures, "Maya|diffuseRoughness|file", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
+            
+            // Maya stingray
+            TrySetTextureProperties(out_mat, textures, "Maya|TEX_color_map|file", aiTextureType_BASE_COLOR, mesh);
+            TrySetTextureProperties(out_mat, textures, "Maya|TEX_normal_map|file", aiTextureType_NORMAL_CAMERA, mesh);
+            TrySetTextureProperties(out_mat, textures, "Maya|TEX_emissive_map|file", aiTextureType_EMISSION_COLOR, mesh);
+            TrySetTextureProperties(out_mat, textures, "Maya|TEX_metallic_map|file", aiTextureType_METALNESS, mesh);
+            TrySetTextureProperties(out_mat, textures, "Maya|TEX_roughness_map|file", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
+            TrySetTextureProperties(out_mat, textures, "Maya|TEX_ao_map|file", aiTextureType_AMBIENT_OCCLUSION, mesh);            
         }
 
         void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh)
@@ -2939,7 +2968,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
                 TransformationCompDefaultValue(comp)
                 );
 
-            const float epsilon = 1e-6f;
+            const float epsilon = Math::getEpsilon<float>();
             return (dyn_val - static_val).SquareLength() < epsilon;
         }
 
@@ -3522,46 +3551,6 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
             out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
         }
 
-        void FBXConverter::ConvertToUnitScale( FbxUnit unit ) {
-            if (mCurrentUnit == unit) {
-                return;
-            }
-
-            ai_real scale = 1.0;
-            if (mCurrentUnit == FbxUnit::cm) {
-                if (unit == FbxUnit::m) {
-                    scale = (ai_real)0.01;
-                } else if (unit == FbxUnit::km) {
-                    scale = (ai_real)0.00001;
-                }
-            } else if (mCurrentUnit == FbxUnit::m) {
-                if (unit == FbxUnit::cm) {
-                    scale = (ai_real)100.0;
-                } else if (unit == FbxUnit::km) {
-                    scale = (ai_real)0.001;
-                }
-            } else if (mCurrentUnit == FbxUnit::km) {
-                if (unit == FbxUnit::cm) {
-                    scale = (ai_real)100000.0;
-                } else if (unit == FbxUnit::m) {
-                    scale = (ai_real)1000.0;
-                }
-            }
-            
-            for (auto mesh : meshes) {
-                if (nullptr == mesh) {
-                    continue;
-                }
-
-                if (mesh->HasPositions()) {
-                    for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
-                        aiVector3D &pos = mesh->mVertices[i];
-                        pos *= scale;
-                    }
-                }
-            }
-        }
-
         void FBXConverter::TransferDataToScene()
         {
             ai_assert(!out->mMeshes);
@@ -3615,9 +3604,9 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
         }
 
         // ------------------------------------------------------------------------------------------------
-        void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit)
+        void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones)
         {
-            FBXConverter converter(out, doc, removeEmptyBones, unit);
+            FBXConverter converter(out, doc, removeEmptyBones);
         }
 
     } // !FBX

+ 2 - 17
code/FBX/FBXConverter.h

@@ -76,23 +76,13 @@ namespace Assimp {
 namespace FBX {
 
 class Document;
-
-enum class FbxUnit {
-    cm = 0,
-    m,
-    km,
-    NumUnits,
-
-    Undefined
-};
-
 /** 
  *  Convert a FBX #Document to #aiScene
  *  @param out Empty scene to be populated
  *  @param doc Parsed FBX document
  *  @param removeEmptyBones Will remove bones, which do not have any references to vertices.
  */
-void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit);
+void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones);
 
 /** Dummy class to encapsulate the conversion process */
 class FBXConverter {
@@ -123,7 +113,7 @@ public:
     };
 
 public:
-    FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit);
+    FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones);
     ~FBXConverter();
 
 private:
@@ -430,10 +420,6 @@ private:
 
     void ConvertGlobalSettings();
 
-    // ------------------------------------------------------------------------------------------------
-    //  Will perform the conversion from a given unit to the requested unit.
-    void ConvertToUnitScale(FbxUnit unit);
-
     // ------------------------------------------------------------------------------------------------
     // copy generated meshes, animations, lights, cameras and textures to the output scene
     void TransferDataToScene();
@@ -470,7 +456,6 @@ private:
 
     aiScene* const out;
     const FBX::Document& doc;
-    FbxUnit mCurrentUnit;
 };
 
 }

+ 1 - 5
code/FBX/FBXExportProperty.cpp

@@ -59,11 +59,7 @@ namespace FBX {
 
 FBXExportProperty::FBXExportProperty(bool v)
 : type('C')
-, data(1) {
-    data = {
-        uint8_t(v)
-    };
-}
+, data(1, uint8_t(v)) {}
 
 FBXExportProperty::FBXExportProperty(int16_t v)
 : type('Y')

+ 84 - 36
code/FBX/FBXExporter.cpp

@@ -67,6 +67,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <vector>
 #include <array>
 #include <unordered_set>
+#include <numeric>
 
 // RESOURCES:
 // https://code.blender.org/2013/08/fbx-binary-file-format-specification/
@@ -1005,6 +1006,9 @@ void FBXExporter::WriteObjects ()
     object_node.EndProperties(outstream, binary, indent);
     object_node.BeginChildren(outstream, binary, indent);
 
+    bool bJoinIdenticalVertices = mProperties->GetPropertyBool("bJoinIdenticalVertices", true);
+    std::vector<std::vector<int32_t>> vVertexIndice;//save vertex_indices as it is needed later
+
     // geometry (aiMesh)
     mesh_uids.clear();
     indent = 1;
@@ -1031,21 +1035,35 @@ void FBXExporter::WriteObjects ()
         std::vector<int32_t> vertex_indices;
         // map of vertex value to its index in the data vector
         std::map<aiVector3D,size_t> index_by_vertex_value;
-        int32_t index = 0;
-        for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
-            aiVector3D vtx = m->mVertices[vi];
-            auto elem = index_by_vertex_value.find(vtx);
-            if (elem == index_by_vertex_value.end()) {
-                vertex_indices.push_back(index);
-                index_by_vertex_value[vtx] = index;
-                flattened_vertices.push_back(vtx[0]);
-                flattened_vertices.push_back(vtx[1]);
-                flattened_vertices.push_back(vtx[2]);
-                ++index;
-            } else {
-                vertex_indices.push_back(int32_t(elem->second));
+        if(bJoinIdenticalVertices){
+            int32_t index = 0;
+            for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
+                aiVector3D vtx = m->mVertices[vi];
+                auto elem = index_by_vertex_value.find(vtx);
+                if (elem == index_by_vertex_value.end()) {
+                    vertex_indices.push_back(index);
+                    index_by_vertex_value[vtx] = index;
+                    flattened_vertices.push_back(vtx[0]);
+                    flattened_vertices.push_back(vtx[1]);
+                    flattened_vertices.push_back(vtx[2]);
+                    ++index;
+                } else {
+                    vertex_indices.push_back(int32_t(elem->second));
+                }
+            }
+        }
+        else { // do not join vertex, respect the export flag
+            vertex_indices.resize(m->mNumVertices);
+            std::iota(vertex_indices.begin(), vertex_indices.end(), 0);
+            for(unsigned int v = 0; v < m->mNumVertices; ++ v) {
+                aiVector3D vtx = m->mVertices[v];
+                flattened_vertices.push_back(vtx.x);
+                flattened_vertices.push_back(vtx.y);
+                flattened_vertices.push_back(vtx.z);
             }
         }
+        vVertexIndice.push_back(vertex_indices);
+
         FBX::Node::WritePropertyNode(
             "Vertices", flattened_vertices, outstream, binary, indent
         );
@@ -1116,6 +1134,51 @@ void FBXExporter::WriteObjects ()
             normals.End(outstream, binary, indent, true);
         }
 
+        // colors, if any
+        // TODO only one color channel currently
+        const int32_t colorChannelIndex = 0;
+        if (m->HasVertexColors(colorChannelIndex)) {
+            FBX::Node vertexcolors("LayerElementColor", int32_t(colorChannelIndex));
+            vertexcolors.Begin(outstream, binary, indent);
+            vertexcolors.DumpProperties(outstream, binary, indent);
+            vertexcolors.EndProperties(outstream, binary, indent);
+            vertexcolors.BeginChildren(outstream, binary, indent);
+            indent = 3;
+            FBX::Node::WritePropertyNode(
+                "Version", int32_t(101), outstream, binary, indent
+            );
+            char layerName[8];
+            sprintf(layerName, "COLOR_%d", colorChannelIndex);
+            FBX::Node::WritePropertyNode(
+                "Name", (const char*)layerName, outstream, binary, indent
+            );
+            FBX::Node::WritePropertyNode(
+                "MappingInformationType", "ByPolygonVertex",
+                outstream, binary, indent
+            );
+            FBX::Node::WritePropertyNode(
+                "ReferenceInformationType", "Direct",
+                outstream, binary, indent
+            );
+            std::vector<double> color_data;
+            color_data.reserve(4 * polygon_data.size());
+            for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
+                const aiFace &f = m->mFaces[fi];
+                for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) {
+                    const aiColor4D &c = m->mColors[colorChannelIndex][f.mIndices[pvi]];
+                    color_data.push_back(c.r);
+                    color_data.push_back(c.g);
+                    color_data.push_back(c.b);
+                    color_data.push_back(c.a);
+                }
+            }
+            FBX::Node::WritePropertyNode(
+                "Colors", color_data, outstream, binary, indent
+            );
+            indent = 2;
+            vertexcolors.End(outstream, binary, indent, true);
+        }
+        
         // uvs, if any
         for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) {
             if (m->mNumUVComponents[uvi] > 2) {
@@ -1209,6 +1272,11 @@ void FBXExporter::WriteObjects ()
         le.AddChild("Type", "LayerElementNormal");
         le.AddChild("TypedIndex", int32_t(0));
         layer.AddChild(le);
+        // TODO only 1 color channel currently
+        le = FBX::Node("LayerElement");
+        le.AddChild("Type", "LayerElementColor");
+        le.AddChild("TypedIndex", int32_t(0));
+        layer.AddChild(le);
         le = FBX::Node("LayerElement");
         le.AddChild("Type", "LayerElementMaterial");
         le.AddChild("TypedIndex", int32_t(0));
@@ -1748,28 +1816,8 @@ void FBXExporter::WriteObjects ()
         // connect it
         connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
 
-        // we will be indexing by vertex...
-        // but there might be a different number of "vertices"
-        // between assimp and our output FBX.
-        // this code is cut-and-pasted from the geometry section above...
-        // ideally this should not be so.
-        // ---
-        // index of original vertex in vertex data vector
-        std::vector<int32_t> vertex_indices;
-        // map of vertex value to its index in the data vector
-        std::map<aiVector3D,size_t> index_by_vertex_value;
-        int32_t index = 0;
-        for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
-            aiVector3D vtx = m->mVertices[vi];
-            auto elem = index_by_vertex_value.find(vtx);
-            if (elem == index_by_vertex_value.end()) {
-                vertex_indices.push_back(index);
-                index_by_vertex_value[vtx] = index;
-                ++index;
-            } else {
-                vertex_indices.push_back(int32_t(elem->second));
-            }
-        }
+        //computed before
+        std::vector<int32_t>& vertex_indices = vVertexIndice[mi];
 
         // TODO, FIXME: this won't work if anything is not in the bind pose.
         // for now if such a situation is detected, we throw an exception.
@@ -2435,7 +2483,7 @@ void FBXExporter::WriteModelNodes(
 void FBXExporter::WriteAnimationCurveNode(
     StreamWriterLE& outstream,
     int64_t uid,
-    std::string name, // "T", "R", or "S"
+    const std::string& name, // "T", "R", or "S"
     aiVector3D default_value,
     std::string property_name, // "Lcl Translation" etc
     int64_t layer_uid,

+ 1 - 1
code/FBX/FBXExporter.h

@@ -156,7 +156,7 @@ namespace Assimp
         void WriteAnimationCurveNode(
             StreamWriterLE& outstream,
             int64_t uid,
-            std::string name, // "T", "R", or "S"
+            const std::string& name, // "T", "R", or "S"
             aiVector3D default_value,
             std::string property_name, // "Lcl Translation" etc
             int64_t animation_layer_uid,

+ 8 - 5
code/FBX/FBXImporter.cpp

@@ -185,12 +185,15 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
         // take the raw parse-tree and convert it to a FBX DOM
         Document doc(parser,settings);
 
-        FbxUnit unit(FbxUnit::cm);
-        if (settings.convertToMeters) {
-            unit = FbxUnit::m;
-        }
         // convert the FBX DOM to aiScene
-        ConvertToAssimpScene(pScene,doc, settings.removeEmptyBones, unit);
+        ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones);
+
+        // size relative to cm
+        float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor();
+
+        // Set FBX file scale is relative to CM must be converted to M for
+        // assimp universal format (M)
+        SetFileScale( size_relative_to_cm * 0.01f);
 
         std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
     }

+ 11 - 12
code/FBX/FBXMeshGeometry.cpp

@@ -610,8 +610,11 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
     const std::string& ReferenceInformationType)
 {
     const size_t face_count = m_faces.size();
-    ai_assert(face_count);
-
+    if( 0 == face_count )
+    {
+        return;
+    }
+    
     // materials are handled separately. First of all, they are assigned per-face
     // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect
     // has a slightly different meaning for materials.
@@ -622,16 +625,14 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
         if (materials_out.empty()) {
             FBXImporter::LogError(Formatter::format("expected material index, ignoring"));
             return;
-        }
-        else if (materials_out.size() > 1) {
+        } else if (materials_out.size() > 1) {
             FBXImporter::LogWarn(Formatter::format("expected only a single material index, ignoring all except the first one"));
             materials_out.clear();
         }
 
         materials_out.resize(m_vertices.size());
         std::fill(materials_out.begin(), materials_out.end(), materials_out.at(0));
-    }
-    else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
+    } else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
         materials_out.resize(face_count);
 
         if(materials_out.size() != face_count) {
@@ -640,18 +641,16 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
             );
             return;
         }
-    }
-    else {
+    } else {
         FBXImporter::LogError(Formatter::format("ignoring material assignments, access type not implemented: ")
             << MappingInformationType << "," << ReferenceInformationType);
     }
 }
 // ------------------------------------------------------------------------------------------------
 ShapeGeometry::ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
-    : Geometry(id, element, name, doc)
-{
-    const Scope* sc = element.Compound();
-    if (!sc) {
+: Geometry(id, element, name, doc) {
+    const Scope *sc = element.Compound();
+    if (nullptr == sc) {
         DOMError("failed to read Geometry object (class: Shape), no data scope found");
     }
     const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element);

+ 3 - 4
code/Importer/IFC/IFCCurve.cpp

@@ -311,10 +311,9 @@ class TrimmedCurve : public BoundedCurve {
 public:
     // --------------------------------------------------
     TrimmedCurve(const Schema_2x3::IfcTrimmedCurve& entity, ConversionData& conv)
-        : BoundedCurve(entity,conv)
+        : BoundedCurve(entity,conv),
+          base(std::shared_ptr<const Curve>(Curve::Convert(entity.BasisCurve,conv)))
     {
-        base = std::shared_ptr<const Curve>(Curve::Convert(entity.BasisCurve,conv));
-
         typedef std::shared_ptr<const STEP::EXPRESS::DataType> Entry;
 
         // for some reason, trimmed curves can either specify a parametric value
@@ -500,7 +499,7 @@ bool Curve::InRange(IfcFloat u) const {
     if (IsClosed()) {
         return true;
     }
-    const IfcFloat epsilon = 1e-5;
+    const IfcFloat epsilon = Math::getEpsilon<float>();
     return u - range.first > -epsilon && range.second - u > -epsilon;
 }
 #endif

+ 1 - 1
code/Importer/IFC/IFCGeometry.cpp

@@ -128,7 +128,7 @@ void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t m
         outer_polygon_it = begin + master_bounds;
     }
     else {
-        for(iit = begin; iit != end; iit++) {
+        for(iit = begin; iit != end; ++iit) {
             // find the polygon with the largest area and take it as the outer bound.
             IfcVector3& n = normals[std::distance(begin,iit)];
             const IfcFloat area = n.SquareLength();

+ 3 - 3
code/Importer/IFC/IFCOpenings.cpp

@@ -593,7 +593,7 @@ typedef std::vector<std::pair<
 bool BoundingBoxesAdjacent(const BoundingBox& bb, const BoundingBox& ibb)
 {
     // TODO: I'm pretty sure there is a much more compact way to check this
-    const IfcFloat epsilon = 1e-5f;
+    const IfcFloat epsilon = Math::getEpsilon<float>();
     return  (std::fabs(bb.second.x - ibb.first.x) < epsilon && bb.first.y <= ibb.second.y && bb.second.y >= ibb.first.y) ||
         (std::fabs(bb.first.x - ibb.second.x) < epsilon && ibb.first.y <= bb.second.y && ibb.second.y >= bb.first.y) ||
         (std::fabs(bb.second.y - ibb.first.y) < epsilon && bb.first.x <= ibb.second.x && bb.second.x >= ibb.first.x) ||
@@ -681,7 +681,7 @@ bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1,
 // ------------------------------------------------------------------------------------------------
 void FindAdjacentContours(ContourVector::iterator current, const ContourVector& contours)
 {
-    const IfcFloat sqlen_epsilon = static_cast<IfcFloat>(1e-8);
+    const IfcFloat sqlen_epsilon = static_cast<IfcFloat>(Math::getEpsilon<float>());
     const BoundingBox& bb = (*current).bb;
 
     // What is to be done here is to populate the skip lists for the contour
@@ -758,7 +758,7 @@ void FindAdjacentContours(ContourVector::iterator current, const ContourVector&
 // ------------------------------------------------------------------------------------------------
 AI_FORCE_INLINE bool LikelyBorder(const IfcVector2& vdelta)
 {
-    const IfcFloat dot_point_epsilon = static_cast<IfcFloat>(1e-5);
+    const IfcFloat dot_point_epsilon = static_cast<IfcFloat>(Math::getEpsilon<float>());
     return std::fabs(vdelta.x * vdelta.y) < dot_point_epsilon;
 }
 

+ 1 - 1
code/MD2/MD2Loader.cpp

@@ -344,7 +344,7 @@ void MD2Importer::InternReadFile( const std::string& pFile,
         if (pcSkins->name[0])
         {
             aiString szString;
-            const size_t iLen = ::strlen(pcSkins->name);
+            const ai_uint32 iLen = (ai_uint32) ::strlen(pcSkins->name);
             ::memcpy(szString.data,pcSkins->name,iLen);
             szString.data[iLen] = '\0';
             szString.length = iLen;

+ 2 - 1
code/MD5/MD5Loader.cpp

@@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "MD5Loader.h"
 #include <assimp/StringComparison.h>
 #include <assimp/fast_atof.h>
+#include <assimp/MathFunctions.h>
 #include <assimp/SkeletonMeshBuilder.h>
 #include <assimp/Importer.hpp>
 #include <assimp/scene.h>
@@ -64,7 +65,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 
 // Minimum weight value. Weights inside [-n ... n] are ignored
-#define AI_MD5_WEIGHT_EPSILON 1e-5f
+#define AI_MD5_WEIGHT_EPSILON Math::getEpsilon<float>()
 
 
 static const aiImporterDesc desc = {

+ 1 - 1
code/MD5/MD5Parser.cpp

@@ -235,7 +235,7 @@ bool MD5Parser::ParseSection(Section& out)
     const char* szStart = ++sz; \
 	while('\"'!=*sz)++sz; \
     const char* szEnd = (sz++); \
-    out.length = (size_t)(szEnd - szStart); \
+    out.length = (ai_uint32) (szEnd - szStart); \
     ::memcpy(out.data,szStart,out.length); \
     out.data[out.length] = '\0';
 // ------------------------------------------------------------------------------------------------

+ 16 - 25
code/MDL/MDLLoader.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -94,23 +92,24 @@ static const aiImporterDesc desc = {
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 MDLImporter::MDLImporter()
-    : configFrameID(),
-    mBuffer(),
-    iGSFileVersion(),
-    pIOHandler(),
-    pScene(),
-    iFileSize()
-{}
+: configFrameID()
+, mBuffer()
+, iGSFileVersion()
+, pIOHandler()
+, pScene()
+, iFileSize() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
-MDLImporter::~MDLImporter()
-{}
+MDLImporter::~MDLImporter() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool MDLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
-{
+bool MDLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
     const std::string extension = GetExtension(pFile);
 
     // if check for extension is not enough, check for the magic tokens
@@ -404,23 +403,15 @@ void MDLImporter::InternReadFile_Quake1() {
 
     // now get a pointer to the first frame in the file
     BE_NCONST MDL::Frame* pcFrames = (BE_NCONST MDL::Frame*)szCurrent;
-    BE_NCONST MDL::SimpleFrame* pcFirstFrame;
+    MDL::SimpleFrame* pcFirstFrame;
 
     if (0 == pcFrames->type) {
         // get address of single frame
-        pcFirstFrame = &pcFrames->frame;
+        pcFirstFrame =( MDL::SimpleFrame*) &pcFrames->frame;
     } else {
         // get the first frame in the group
-
-#if 1
-        // FIXME: the cast is wrong and cause a warning on clang 5.0
-        // disable this code for now, fix it later
-        ai_assert(false && "Bad pointer cast");
-        pcFirstFrame = nullptr; // Workaround: msvc++ C4703 error
-#else
-        BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames;
-        pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type);
-#endif
+        BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*) pcFrames;
+        pcFirstFrame = &(pcFrames2->frames[0]);
     }
     BE_NCONST MDL::Vertex* pcVertices = (BE_NCONST MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name));
     VALIDATE_FILE_SIZE((const unsigned char*)(pcVertices + pcHeader->num_verts));

+ 1 - 17
code/MDL/MDLLoader.h

@@ -89,16 +89,12 @@ public:
     MDLImporter();
     ~MDLImporter();
 
-
-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;
 
-
     // -------------------------------------------------------------------
     /** Called prior to ReadFile().
     * The function is a request to the importer to update its configuration
@@ -107,8 +103,6 @@ public:
     void SetupProperties(const Importer* pImp);
 
 protected:
-
-
     // -------------------------------------------------------------------
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details
@@ -122,8 +116,6 @@ protected:
     void InternReadFile( const std::string& pFile, aiScene* pScene,
         IOSystem* pIOHandler);
 
-protected:
-
     // -------------------------------------------------------------------
     /** Import a quake 1 MDL file (IDPO)
     */
@@ -154,7 +146,6 @@ protected:
     void SizeCheck(const void* szPos);
     void SizeCheck(const void* szPos, const char* szFile, unsigned int iLine);
 
-
     // -------------------------------------------------------------------
     /** Validate the header data structure of a game studio MDL7 file
      * \param pcHeader Input header to be validated
@@ -167,7 +158,6 @@ protected:
      */
     void ValidateHeader_Quake1(const MDL::Header* pcHeader);
 
-
     // -------------------------------------------------------------------
     /** Try to load a  palette from the current directory (colormap.lmp)
      *  If it is not found the default palette of Quake1 is returned
@@ -179,9 +169,8 @@ protected:
      */
     void FreePalette(const unsigned char* pszColorMap);
 
-
     // -------------------------------------------------------------------
-    /** Load a paletized texture from the file and convert it to 32bpp
+    /** Load a palletized texture from the file and convert it to 32bpp
     */
     void CreateTextureARGB8_3DGS_MDL3(const unsigned char* szData);
 
@@ -195,7 +184,6 @@ protected:
         unsigned int iType,
         unsigned int* piSkip);
 
-
     // -------------------------------------------------------------------
     /** Used to load textures from MDL5
      * \param szData Input data
@@ -206,7 +194,6 @@ protected:
         unsigned int iType,
         unsigned int* piSkip);
 
-
     // -------------------------------------------------------------------
     /** Checks whether a texture can be replaced with a single color
      * This is useful for all file formats before MDL7 (all those
@@ -218,14 +205,12 @@ protected:
     */
     aiColor4D ReplaceTextureWithColor(const aiTexture* pcTexture);
 
-
     // -------------------------------------------------------------------
     /** Converts the absolute texture coordinates in MDL5 files to
      *  relative in a range between 0 and 1
     */
     void CalculateUVCoordinates_MDL5();
 
-
     // -------------------------------------------------------------------
     /** Read an UV coordinate from the file. If the file format is not
      * MDL5, the function calculates relative texture coordinates
@@ -245,7 +230,6 @@ protected:
      */
     void SetupMaterialProperties_3DGS_MDL5_Quake1( );
 
-
     // -------------------------------------------------------------------
     /** Parse a skin lump in a MDL7/HMP7 file with all of its features
      *  variant 1: Current cursor position is the beginning of the skin header

+ 1 - 17
code/Material/MaterialSystem.cpp

@@ -545,23 +545,7 @@ aiReturn aiMaterial::AddProperty (const aiString* pInput,
     unsigned int type,
     unsigned int index)
 {
-    // We don't want to add the whole buffer .. write a 32 bit length
-    // prefix followed by the zero-terminated UTF8 string.
-    // (HACK) I don't want to break the ABI now, but we definitely
-    // ought to change aiString::mLength to uint32_t one day.
-    if (sizeof(size_t) == 8) {
-        aiString copy = *pInput;
-        uint32_t* s = reinterpret_cast<uint32_t*>(&copy.length);
-        s[1] = static_cast<uint32_t>(pInput->length);
-
-        return AddBinaryProperty(s+1,
-            static_cast<unsigned int>(pInput->length+1+4),
-            pKey,
-            type,
-            index,
-            aiPTI_String);
-    }
-    ai_assert(sizeof(size_t)==4);
+    ai_assert(sizeof(ai_uint32)==4);
     return AddBinaryProperty(pInput,
         static_cast<unsigned int>(pInput->length+1+4),
         pKey,

+ 1 - 4
code/Obj/ObjFileImporter.cpp

@@ -79,10 +79,7 @@ using namespace std;
 ObjFileImporter::ObjFileImporter()
 : m_Buffer()
 , m_pRootObject( nullptr )
-, m_strAbsPath( "" ) {
-    DefaultIOSystem io;
-    m_strAbsPath = io.getOsSeparator();
-}
+, m_strAbsPath( std::string(1, DefaultIOSystem().getOsSeparator()) ) {}
 
 // ------------------------------------------------------------------------------------------------
 //  Destructor.

+ 2 - 2
code/Obj/ObjFileParser.cpp

@@ -244,8 +244,8 @@ void ObjFileParser::copyNextWord(char *pBuffer, size_t length) {
     size_t index = 0;
     m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
     if ( *m_DataIt == '\\' ) {
-        m_DataIt++;
-        m_DataIt++;
+        ++m_DataIt;
+        ++m_DataIt;
         m_DataIt = getNextWord<DataArrayIt>( m_DataIt, m_DataItEnd );
     }
     while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) {

+ 3 - 3
code/PostProcessing/CalcTangentsProcess.cpp

@@ -212,7 +212,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
             // project tangent and bitangent into the plane formed by the vertex' normal
             aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
             aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
-            localTangent.Normalize(); localBitangent.Normalize();
+            localTangent.NormalizeSafe(); localBitangent.NormalizeSafe();
 
             // reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN.
             bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z);
@@ -220,10 +220,10 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
             if (invalid_tangent != invalid_bitangent) {
                 if (invalid_tangent) {
                     localTangent = meshNorm[p] ^ localBitangent;
-                    localTangent.Normalize();
+                    localTangent.NormalizeSafe();
                 } else {
                     localBitangent = localTangent ^ meshNorm[p];
-                    localBitangent.Normalize();
+                    localBitangent.NormalizeSafe();
                 }
             }
 

+ 3 - 3
code/PostProcessing/ComputeUVMappingProcess.cpp

@@ -354,12 +354,12 @@ void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D&
     }
     else if (axis * base_axis_z >= angle_epsilon)   {
         FindMeshCenter(mesh, center, min, max);
-        diffu = max.y - min.y;
-        diffv = max.z - min.z;
+        diffu = max.x - min.x;
+        diffv = max.y - min.y;
 
         for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt)  {
             const aiVector3D& pos = mesh->mVertices[pnt];
-            out[pnt].Set((pos.y - min.y) / diffu,(pos.x - min.x) / diffv,0.0);
+            out[pnt].Set((pos.x - min.x) / diffu,(pos.y - min.y) / diffv,0.0);
         }
     }
     // slower code path in case the mapping axis is not one of the coordinate system axes

+ 0 - 25
code/PostProcessing/JoinVerticesProcess.cpp

@@ -431,31 +431,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
             bone->mWeights = new aiVertexWeight[bone->mNumWeights];
             memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight));
         }
-        else {
-
-            /*  NOTE:
-             *
-             *  In the algorithm above we're assuming that there are no vertices
-             *  with a different bone weight setup at the same position. That wouldn't
-             *  make sense, but it is not absolutely impossible. SkeletonMeshBuilder
-             *  for example generates such input data if two skeleton points
-             *  share the same position. Again this doesn't make sense but is
-             *  reality for some model formats (MD5 for example uses these special
-             *  nodes as attachment tags for its weapons).
-             *
-             *  Then it is possible that a bone has no weights anymore .... as a quick
-             *  workaround, we're just removing these bones. If they're animated,
-             *  model geometry might be modified but at least there's no risk of a crash.
-             */
-            delete bone;
-            --pMesh->mNumBones;
-            for (unsigned int n = a; n < pMesh->mNumBones; ++n)  {
-                pMesh->mBones[n] = pMesh->mBones[n+1];
-            }
-
-            --a;
-            ASSIMP_LOG_WARN("Removing bone -> no weights remaining");
-        }
     }
     return pMesh->mNumVertices;
 }

+ 29 - 0
code/PostProcessing/MakeVerboseFormat.cpp

@@ -224,3 +224,32 @@ bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
     }
     return (pcMesh->mNumVertices != iOldNumVertices);
 }
+
+
+// ------------------------------------------------------------------------------------------------
+bool IsMeshInVerboseFormat(const aiMesh* mesh) {
+    // avoid slow vector<bool> specialization
+    std::vector<unsigned int> seen(mesh->mNumVertices,0);
+    for(unsigned int i = 0; i < mesh->mNumFaces; ++i) {
+        const aiFace& f = mesh->mFaces[i];
+        for(unsigned int j = 0; j < f.mNumIndices; ++j) {
+            if(++seen[f.mIndices[j]] == 2) {
+                // found a duplicate index
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool MakeVerboseFormatProcess::IsVerboseFormat(const aiScene* pScene) {
+    for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+        if(!IsMeshInVerboseFormat(pScene->mMeshes[i])) {
+            return false;
+        }
+    }
+
+    return true;
+}

+ 7 - 0
code/PostProcessing/MakeVerboseFormat.h

@@ -94,6 +94,13 @@ public:
     * @param pScene The imported data to work at. */
     void Execute( aiScene* pScene);
 
+public:
+
+    // -------------------------------------------------------------------
+    /** Checks whether the scene is already in verbose format.
+    * @param pScene The data to check. 
+    * @return true if the scene is already in verbose format. */
+    static bool IsVerboseFormat(const aiScene* pScene);
 
 private:
 

+ 115 - 10
code/PostProcessing/ScaleProcess.cpp

@@ -39,19 +39,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ----------------------------------------------------------------------
 */
-#ifndef ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS
-
 #include "ScaleProcess.h"
 
 #include <assimp/scene.h>
 #include <assimp/postprocess.h>
+#include <assimp/BaseImporter.h>
 
 namespace Assimp {
 
 ScaleProcess::ScaleProcess()
 : BaseProcess()
 , mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) {
-    // empty
 }
 
 ScaleProcess::~ScaleProcess() {
@@ -71,10 +69,26 @@ bool ScaleProcess::IsActive( unsigned int pFlags ) const {
 }
 
 void ScaleProcess::SetupProperties( const Importer* pImp ) {
-    mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0 );
+    // User scaling
+    mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 1.0f );
+
+    // File scaling * Application Scaling
+    float importerScale = pImp->GetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, 1.0f );
+
+    // apply scale to the scale 
+    // helps prevent bugs with backward compatibility for anyone using normal scaling.
+    mScale *= importerScale;
 }
 
 void ScaleProcess::Execute( aiScene* pScene ) {
+    if(mScale == 1.0f)  {
+        return; // nothing to scale
+    }
+    
+    ai_assert( mScale != 0 );
+    ai_assert( nullptr != pScene );
+    ai_assert( nullptr != pScene->mRootNode );
+
     if ( nullptr == pScene ) {
         return;
     }
@@ -82,22 +96,113 @@ void ScaleProcess::Execute( aiScene* pScene ) {
     if ( nullptr == pScene->mRootNode ) {
         return;
     }
+    
+    // Process animations and update position transform to new unit system
+    for( unsigned int animationID = 0; animationID < pScene->mNumAnimations; animationID++ )
+    {
+        aiAnimation* animation = pScene->mAnimations[animationID];
+
+        for( unsigned int animationChannel = 0; animationChannel < animation->mNumChannels; animationChannel++)
+        {
+            aiNodeAnim* anim = animation->mChannels[animationChannel];
+            
+            for( unsigned int posKey = 0; posKey < anim->mNumPositionKeys; posKey++)
+            {
+                aiVectorKey& vectorKey = anim->mPositionKeys[posKey];
+                vectorKey.mValue *= mScale;
+            }
+        }
+    }
+
+    for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++)
+    {
+        aiMesh *mesh = pScene->mMeshes[meshID]; 
+        
+        // Reconstruct mesh vertexes to the new unit system
+        for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++)
+        {
+            aiVector3D& vertex = mesh->mVertices[vertexID];
+            vertex *= mScale;
+        }
+
+
+        // bone placement / scaling
+        for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++)
+        {
+            // Reconstruct matrix by transform rather than by scale 
+            // This prevent scale values being changed which can
+            // be meaningful in some cases 
+            // like when you want the modeller to see 1:1 compatibility.
+            aiBone* bone = mesh->mBones[boneID];
+
+            aiVector3D pos, scale;
+            aiQuaternion rotation;
+
+            bone->mOffsetMatrix.Decompose( scale, rotation, pos);
+            
+            aiMatrix4x4 translation;
+            aiMatrix4x4::Translation( pos * mScale, translation );
+            
+            aiMatrix4x4 scaling;
+            aiMatrix4x4::Scaling( aiVector3D(scale), scaling );
+
+            aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix());
+
+            bone->mOffsetMatrix = translation * RotMatrix * scaling;
+        }
+
+
+        // animation mesh processing
+        // convert by position rather than scale.
+        for( unsigned int animMeshID = 0; animMeshID < mesh->mNumAnimMeshes; animMeshID++)
+        {
+            aiAnimMesh * animMesh = mesh->mAnimMeshes[animMeshID];
+            
+            for( unsigned int vertexID = 0; vertexID < animMesh->mNumVertices; vertexID++)
+            {
+                aiVector3D& vertex = animMesh->mVertices[vertexID];
+                vertex *= mScale;
+            }
+        }
+    }
 
     traverseNodes( pScene->mRootNode );
 }
 
-void ScaleProcess::traverseNodes( aiNode *node ) {
+void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) {    
     applyScaling( node );
+
+    for( size_t i = 0; i < node->mNumChildren; i++)
+    {
+        // recurse into the tree until we are done!
+        traverseNodes( node->mChildren[i], nested_node_id+1 ); 
+    }
 }
 
 void ScaleProcess::applyScaling( aiNode *currentNode ) {
     if ( nullptr != currentNode ) {
-        currentNode->mTransformation.a1 = currentNode->mTransformation.a1 * mScale;
-        currentNode->mTransformation.b2 = currentNode->mTransformation.b2 * mScale;
-        currentNode->mTransformation.c3 = currentNode->mTransformation.c3 * mScale;
+        // Reconstruct matrix by transform rather than by scale 
+        // This prevent scale values being changed which can
+        // be meaningful in some cases 
+        // like when you want the modeller to 
+        // see 1:1 compatibility.
+        
+        aiVector3D pos, scale;
+        aiQuaternion rotation;
+        currentNode->mTransformation.Decompose( scale, rotation, pos);
+        
+        aiMatrix4x4 translation;
+        aiMatrix4x4::Translation( pos * mScale, translation );
+        
+        aiMatrix4x4 scaling;
+
+        // note: we do not use mScale here, this is on purpose.
+        aiMatrix4x4::Scaling( scale, scaling );
+
+        aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix());
+
+        currentNode->mTransformation = translation * RotMatrix * scaling;
     }
 }
 
 } // Namespace Assimp
-
-#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS

+ 11 - 2
code/PostProcessing/ScaleProcess.h

@@ -39,7 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ----------------------------------------------------------------------
 */
-#pragma once
+#ifndef SCALE_PROCESS_H_
+#define SCALE_PROCESS_H_
 
 #include "Common/BaseProcess.h"
 
@@ -53,6 +54,11 @@ namespace Assimp {
 
 // ---------------------------------------------------------------------------
 /** ScaleProcess: Class to rescale the whole model.
+ * Now rescales animations, bones, and blend shapes properly.
+ * Please note this will not write to 'scale' transform it will rewrite mesh 
+ * and matrixes so that your scale values 
+ * from your model package are preserved, so this is completely intentional
+ * bugs should be reported as soon as they are found.
 */
 class ASSIMP_API ScaleProcess : public BaseProcess {
 public:
@@ -78,7 +84,7 @@ public:
     virtual void Execute( aiScene* pScene );
 
 private:
-    void traverseNodes( aiNode *currentNode );
+    void traverseNodes( aiNode *currentNode, unsigned int nested_node_id = 0 );
     void applyScaling( aiNode *currentNode );
 
 private:
@@ -86,3 +92,6 @@ private:
 };
 
 } // Namespace Assimp
+
+
+#endif // SCALE_PROCESS_H_

+ 1 - 1
code/PostProcessing/ValidateDataStructure.cpp

@@ -958,7 +958,7 @@ void ValidateDSProcess::Validate( const aiString* pString)
 {
     if (pString->length > MAXLEN)
     {
-        ReportError("aiString::length is too large (%lu, maximum is %lu)",
+        ReportError("aiString::length is too large (%u, maximum is %lu)",
             pString->length,MAXLEN);
     }
     const char* sz = pString->data;

+ 8 - 8
code/Q3BSP/Q3BSPFileImporter.cpp

@@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
 
 #include "Q3BSPFileImporter.h"
-#include "Q3BSPZipArchive.h"
 #include "Q3BSPFileParser.h"
 #include "Q3BSPFileData.h"
 
@@ -60,6 +59,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/scene.h>
 #include <assimp/ai_assert.h>
 #include <assimp/DefaultIOSystem.h>
+#include <assimp/ZipArchiveIOSystem.h>
 #include <assimp/importerdesc.h>
 #include <vector>
 #include <sstream>
@@ -181,7 +181,7 @@ const aiImporterDesc* Q3BSPFileImporter::GetInfo () const {
 // ------------------------------------------------------------------------------------------------
 //  Import method.
 void Q3BSPFileImporter::InternReadFile(const std::string &rFile, aiScene* scene, IOSystem* ioHandler) {
-    Q3BSPZipArchive Archive( ioHandler, rFile );
+    ZipArchiveIOSystem Archive( ioHandler, rFile );
     if ( !Archive.isOpen() ) {
         throw DeadlyImportError( "Failed to open file " + rFile + "." );
     }
@@ -223,10 +223,10 @@ void Q3BSPFileImporter::separateMapName( const std::string &importName, std::str
 
 // ------------------------------------------------------------------------------------------------
 //  Returns the first map in the map archive.
-bool Q3BSPFileImporter::findFirstMapInArchive( Q3BSPZipArchive &bspArchive, std::string &mapName ) {
+bool Q3BSPFileImporter::findFirstMapInArchive(ZipArchiveIOSystem &bspArchive, std::string &mapName ) {
     mapName = "";
     std::vector<std::string> fileList;
-    bspArchive.getFileList( fileList );
+    bspArchive.getFileListExtension( fileList, "bsp" );
     if (fileList.empty()) {
         return false;
     }
@@ -249,7 +249,7 @@ bool Q3BSPFileImporter::findFirstMapInArchive( Q3BSPZipArchive &bspArchive, std:
 // ------------------------------------------------------------------------------------------------
 //  Creates the assimp specific data.
 void Q3BSPFileImporter::CreateDataFromImport( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
-        Q3BSPZipArchive *pArchive ) {
+    ZipArchiveIOSystem *pArchive ) {
     if (nullptr == pModel || nullptr == pScene) {
         return;
     }
@@ -418,7 +418,7 @@ void Q3BSPFileImporter::createTriangleTopology( const Q3BSP::Q3BSPModel *pModel,
 // ------------------------------------------------------------------------------------------------
 //  Creates all referenced materials.
 void Q3BSPFileImporter::createMaterials( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
-        Q3BSPZipArchive *pArchive ) {
+    ZipArchiveIOSystem *pArchive ) {
     if ( m_MaterialLookupMap.empty() ) {
         return;
     }
@@ -564,7 +564,7 @@ aiFace *Q3BSPFileImporter::getNextFace( aiMesh *mesh, unsigned int &faceIdx ) {
 // ------------------------------------------------------------------------------------------------
 //  Imports a texture file.
 bool Q3BSPFileImporter::importTextureFromArchive( const Q3BSP::Q3BSPModel *model,
-                                                 Q3BSP::Q3BSPZipArchive *archive, aiScene*,
+                                                 ZipArchiveIOSystem *archive, aiScene*,
                                                  aiMaterial *pMatHelper, int textureId ) {
     if (nullptr == archive || nullptr == pMatHelper ) {
         return false;
@@ -669,7 +669,7 @@ bool Q3BSPFileImporter::importLightmap( const Q3BSP::Q3BSPModel *pModel, aiScene
 
 // ------------------------------------------------------------------------------------------------
 //  Will search for a supported extension.
-bool Q3BSPFileImporter::expandFile(  Q3BSP::Q3BSPZipArchive *pArchive, const std::string &rFilename,
+bool Q3BSPFileImporter::expandFile(ZipArchiveIOSystem *pArchive, const std::string &rFilename,
                                    const std::vector<std::string> &rExtList, std::string &rFile,
                                    std::string &rExt )
 {

+ 6 - 6
code/Q3BSP/Q3BSPFileImporter.h

@@ -54,9 +54,9 @@ struct aiMaterial;
 struct aiTexture;
 
 namespace Assimp {
+    class ZipArchiveIOSystem;
 
 namespace Q3BSP {
-    class Q3BSPZipArchive;
     struct Q3BSPModel;
     struct sQ3BSPFace;
 }
@@ -85,24 +85,24 @@ protected:
     const aiImporterDesc* GetInfo () const;
     void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
     void separateMapName( const std::string &rImportName, std::string &rArchiveName, std::string &rMapName );
-    bool findFirstMapInArchive( Q3BSP::Q3BSPZipArchive &rArchive, std::string &rMapName );
-    void CreateDataFromImport( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, Q3BSP::Q3BSPZipArchive *pArchive );
+    bool findFirstMapInArchive(ZipArchiveIOSystem &rArchive, std::string &rMapName );
+    void CreateDataFromImport( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, ZipArchiveIOSystem *pArchive );
     void CreateNodes( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, aiNode *pParent );
     aiNode *CreateTopology( const Q3BSP::Q3BSPModel *pModel, unsigned int materialIdx,
         std::vector<Q3BSP::sQ3BSPFace*> &rArray, aiMesh  **pMesh );
     void createTriangleTopology( const Q3BSP::Q3BSPModel *pModel, Q3BSP::sQ3BSPFace *pQ3BSPFace, aiMesh* pMesh, unsigned int &rFaceIdx,
         unsigned int &rVertIdx  );
-    void createMaterials( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, Q3BSP::Q3BSPZipArchive *pArchive );
+    void createMaterials( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, ZipArchiveIOSystem *pArchive );
     size_t countData( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const;
     size_t countFaces( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const;
     size_t countTriangles( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const;
     void createMaterialMap( const Q3BSP::Q3BSPModel *pModel);
     aiFace *getNextFace( aiMesh *pMesh, unsigned int &rFaceIdx );
-    bool importTextureFromArchive( const Q3BSP::Q3BSPModel *pModel, Q3BSP::Q3BSPZipArchive *pArchive, aiScene* pScene,
+    bool importTextureFromArchive( const Q3BSP::Q3BSPModel *pModel, ZipArchiveIOSystem *pArchive, aiScene* pScene,
         aiMaterial *pMatHelper, int textureId );
     bool importLightmap( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, aiMaterial *pMatHelper, int lightmapId );
     bool importEntities( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene );
-    bool expandFile(  Q3BSP::Q3BSPZipArchive *pArchive, const std::string &rFilename, const std::vector<std::string> &rExtList,
+    bool expandFile(ZipArchiveIOSystem *pArchive, const std::string &rFilename, const std::vector<std::string> &rExtList,
         std::string &rFile, std::string &rExt );
 
 private:

+ 3 - 2
code/Q3BSP/Q3BSPFileParser.cpp

@@ -45,9 +45,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "Q3BSPFileParser.h"
 #include "Q3BSPFileData.h"
-#include "Q3BSPZipArchive.h"
 #include <vector>
 #include <assimp/DefaultIOSystem.h>
+#include <assimp/ZipArchiveIOSystem.h>
 #include <assimp/ai_assert.h>
 
 namespace Assimp {
@@ -55,7 +55,7 @@ namespace Assimp {
 using namespace Q3BSP;
 
 // ------------------------------------------------------------------------------------------------
-Q3BSPFileParser::Q3BSPFileParser( const std::string &mapName, Q3BSPZipArchive *pZipArchive ) :
+Q3BSPFileParser::Q3BSPFileParser( const std::string &mapName, ZipArchiveIOSystem *pZipArchive ) :
     m_sOffset( 0 ),
     m_Data(),
     m_pModel(nullptr),
@@ -101,6 +101,7 @@ bool Q3BSPFileParser::readData( const std::string &rMapName ) {
     const size_t readSize = pMapFile->Read( &m_Data[0], sizeof( char ), size );
     if ( readSize != size ) {
         m_Data.clear();
+        m_pZipArchive->Close(pMapFile);
         return false;
     }
     m_pZipArchive->Close( pMapFile );

+ 6 - 7
code/Q3BSP/Q3BSPFileParser.h

@@ -48,13 +48,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp
 {
+    class ZipArchiveIOSystem;
+
 namespace Q3BSP
 {
-
-class Q3BSPZipArchive;
-struct Q3BSPModel;
-class ZipFile;
-
+    struct Q3BSPModel;
+    class ZipFile;
 }
 
 // -------------------------------------------------------------------
@@ -62,7 +61,7 @@ class ZipFile;
 class Q3BSPFileParser
 {
 public:
-    Q3BSPFileParser( const std::string &rMapName, Q3BSP::Q3BSPZipArchive *pZipArchive );
+    Q3BSPFileParser( const std::string &rMapName, ZipArchiveIOSystem *pZipArchive );
     ~Q3BSPFileParser();
     Q3BSP::Q3BSPModel *getModel() const;
 
@@ -83,7 +82,7 @@ private:
     size_t m_sOffset;
     std::vector<char> m_Data;
     Q3BSP::Q3BSPModel *m_pModel;
-    Q3BSP::Q3BSPZipArchive *m_pZipArchive;
+    ZipArchiveIOSystem *m_pZipArchive;
 };
 
 } // Namespace Assimp

+ 0 - 325
code/Q3BSP/Q3BSPZipArchive.cpp

@@ -1,325 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2019, assimp team
-
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the
-following conditions are met:
-
-* Redistributions of source code must retain the above
-  copyright notice, this list of conditions and the
-  following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-  copyright notice, this list of conditions and the
-  following disclaimer in the documentation and/or other
-  materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-  contributors may be used to endorse or promote products
-  derived from this software without specific prior
-  written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-----------------------------------------------------------------------
-*/
-
-#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
-
-#include "Q3BSPZipArchive.h"
-#include <cassert>
-#include <cstdlib>
-#include <assimp/ai_assert.h>
-
-namespace Assimp {
-namespace Q3BSP {
-
-voidpf IOSystem2Unzip::open(voidpf opaque, const char* filename, int mode) {
-    IOSystem* io_system = (IOSystem*) opaque;
-
-    const char* mode_fopen = NULL;
-    if((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) {
-        mode_fopen = "rb";
-    } else {
-        if(mode & ZLIB_FILEFUNC_MODE_EXISTING) {
-            mode_fopen = "r+b";
-        } else {
-            if(mode & ZLIB_FILEFUNC_MODE_CREATE) {
-                mode_fopen = "wb";
-            }
-        }
-    }
-
-    return (voidpf) io_system->Open(filename, mode_fopen);
-}
-
-uLong IOSystem2Unzip::read(voidpf /*opaque*/, voidpf stream, void* buf, uLong size) {
-    IOStream* io_stream = (IOStream*) stream;
-
-    return static_cast<uLong>(io_stream->Read(buf, 1, size));
-}
-
-uLong IOSystem2Unzip::write(voidpf /*opaque*/, voidpf stream, const void* buf, uLong size) {
-    IOStream* io_stream = (IOStream*) stream;
-
-    return static_cast<uLong>(io_stream->Write(buf, 1, size));
-}
-
-long IOSystem2Unzip::tell(voidpf /*opaque*/, voidpf stream) {
-    IOStream* io_stream = (IOStream*) stream;
-
-    return static_cast<long>(io_stream->Tell());
-}
-
-long IOSystem2Unzip::seek(voidpf /*opaque*/, voidpf stream, uLong offset, int origin) {
-    IOStream* io_stream = (IOStream*) stream;
-
-    aiOrigin assimp_origin;
-    switch (origin) {
-        default:
-        case ZLIB_FILEFUNC_SEEK_CUR:
-            assimp_origin = aiOrigin_CUR;
-            break;
-        case ZLIB_FILEFUNC_SEEK_END:
-            assimp_origin = aiOrigin_END;
-            break;
-        case ZLIB_FILEFUNC_SEEK_SET:
-            assimp_origin = aiOrigin_SET;
-            break;
-    }
-
-    return (io_stream->Seek(offset, assimp_origin) == aiReturn_SUCCESS ? 0 : -1);
-}
-
-int IOSystem2Unzip::close(voidpf opaque, voidpf stream) {
-    IOSystem* io_system = (IOSystem*) opaque;
-    IOStream* io_stream = (IOStream*) stream;
-
-    io_system->Close(io_stream);
-
-    return 0;
-}
-
-int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) {
-    return 0;
-}
-
-zlib_filefunc_def IOSystem2Unzip::get(IOSystem* pIOHandler) {
-    zlib_filefunc_def mapping;
-
-#ifdef ASSIMP_USE_HUNTER
-    mapping.zopen_file = (open_file_func)open;
-    mapping.zread_file = (read_file_func)read;
-    mapping.zwrite_file = (write_file_func)write;
-    mapping.ztell_file = (tell_file_func)tell;
-    mapping.zseek_file = (seek_file_func)seek;
-    mapping.zclose_file = (close_file_func)close;
-    mapping.zerror_file = (error_file_func)testerror;
-#else
-    mapping.zopen_file = open;
-    mapping.zread_file = read;
-    mapping.zwrite_file = write;
-    mapping.ztell_file = tell;
-    mapping.zseek_file = seek;
-    mapping.zclose_file = close;
-    mapping.zerror_file = testerror;
-#endif
-    mapping.opaque = (voidpf) pIOHandler;
-
-    return mapping;
-}
-
-ZipFile::ZipFile(size_t size) : m_Size(size) {
-    ai_assert(m_Size != 0);
-
-    m_Buffer = malloc(m_Size);
-}
-
-ZipFile::~ZipFile() {
-    free(m_Buffer);
-    m_Buffer = NULL;
-}
-
-size_t ZipFile::Read(void* pvBuffer, size_t pSize, size_t pCount) {
-    const size_t size = pSize * pCount;
-    assert(size <= m_Size);
-
-    std::memcpy(pvBuffer, m_Buffer, size);
-
-    return size;
-}
-
-size_t ZipFile::Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) {
-    return 0;
-}
-
-size_t ZipFile::FileSize() const {
-    return m_Size;
-}
-
-aiReturn ZipFile::Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) {
-    return aiReturn_FAILURE;
-}
-
-size_t ZipFile::Tell() const {
-    return 0;
-}
-
-void ZipFile::Flush() {
-    // empty
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Constructor.
-Q3BSPZipArchive::Q3BSPZipArchive(IOSystem* pIOHandler, const std::string& rFile) : m_ZipFileHandle(NULL), m_ArchiveMap() {
-    if (! rFile.empty()) {
-        zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler);
-
-        m_ZipFileHandle = unzOpen2(rFile.c_str(), &mapping);
-
-        if(m_ZipFileHandle != nullptr) {
-            mapArchive();
-        }
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Destructor.
-Q3BSPZipArchive::~Q3BSPZipArchive() {
-    for(auto &file : m_ArchiveMap) {
-        delete file.second;
-    }
-    m_ArchiveMap.clear();
-
-    if(m_ZipFileHandle != nullptr) {
-        unzClose(m_ZipFileHandle);
-        m_ZipFileHandle = nullptr;
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Returns true, if the archive is already open.
-bool Q3BSPZipArchive::isOpen() const {
-    return (m_ZipFileHandle != nullptr);
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Returns true, if the filename is part of the archive.
-bool Q3BSPZipArchive::Exists(const char* pFile) const {
-    bool exist = false;
-    if (pFile != nullptr) {
-        std::string rFile(pFile);
-        std::map<std::string, ZipFile*>::const_iterator it = m_ArchiveMap.find(rFile);
-
-        if(it != m_ArchiveMap.end()) {
-            exist = true;
-        }
-    }
-
-    return exist;
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Returns the separator delimiter.
-char Q3BSPZipArchive::getOsSeparator() const {
-#ifndef _WIN32
-    return '/';
-#else
-    return '\\';
-#endif
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Opens a file, which is part of the archive.
-IOStream *Q3BSPZipArchive::Open(const char* pFile, const char* /*pMode*/) {
-    ai_assert(pFile != nullptr);
-
-    IOStream* result = nullptr;
-
-    std::map<std::string, ZipFile*>::iterator it = m_ArchiveMap.find(pFile);
-
-    if(it != m_ArchiveMap.end()) {
-        result = (IOStream*) it->second;
-    }
-
-    return result;
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Close a filestream.
-void Q3BSPZipArchive::Close(IOStream *pFile) {
-    (void)(pFile);
-    ai_assert(pFile != nullptr);
-
-    // We don't do anything in case the file would be opened again in the future
-}
-// ------------------------------------------------------------------------------------------------
-//  Returns the file-list of the archive.
-void Q3BSPZipArchive::getFileList(std::vector<std::string> &rFileList) {
-    rFileList.clear();
-
-    for(auto &file : m_ArchiveMap) {
-        rFileList.push_back(file.first);
-    }
-}
-
-// ------------------------------------------------------------------------------------------------
-//  Maps the archive content.
-bool Q3BSPZipArchive::mapArchive() {
-    bool success = false;
-
-    if(m_ZipFileHandle != nullptr) {
-        if(m_ArchiveMap.empty()) {
-            //  At first ensure file is already open
-            if(unzGoToFirstFile(m_ZipFileHandle) == UNZ_OK) {
-                // Loop over all files
-                do {
-                    char filename[FileNameSize];
-                    unz_file_info fileInfo;
-
-                    if(unzGetCurrentFileInfo(m_ZipFileHandle, &fileInfo, filename, FileNameSize, NULL, 0, NULL, 0) == UNZ_OK) {
-                        // The file has EXACTLY the size of uncompressed_size. In C
-                        // you need to mark the last character with '\0', so add
-                        // another character
-                        if(fileInfo.uncompressed_size != 0 && unzOpenCurrentFile(m_ZipFileHandle) == UNZ_OK) {
-                            std::pair<std::map<std::string, ZipFile*>::iterator, bool> result = m_ArchiveMap.insert(std::make_pair(filename, new ZipFile(fileInfo.uncompressed_size)));
-
-                            if(unzReadCurrentFile(m_ZipFileHandle, result.first->second->m_Buffer, fileInfo.uncompressed_size) == (long int) fileInfo.uncompressed_size) {
-                                if(unzCloseCurrentFile(m_ZipFileHandle) == UNZ_OK) {
-                                    // Nothing to do anymore...
-                                }
-                            }
-                        }
-                    }
-                } while(unzGoToNextFile(m_ZipFileHandle) != UNZ_END_OF_LIST_OF_FILE);
-            }
-        }
-
-        success = true;
-    }
-
-    return success;
-}
-
-// ------------------------------------------------------------------------------------------------
-
-} // Namespace Q3BSP
-} // Namespace Assimp
-
-#endif // ASSIMP_BUILD_NO_Q3BSP_IMPORTER

+ 0 - 135
code/Q3BSP/Q3BSPZipArchive.h

@@ -1,135 +0,0 @@
-/*
-Open Asset Import Library (assimp)
-----------------------------------------------------------------------
-
-Copyright (c) 2006-2019, assimp team
-
-
-All rights reserved.
-
-Redistribution and use of this software in source and binary forms,
-with or without modification, are permitted provided that the
-following conditions are met:
-
-* Redistributions of source code must retain the above
-  copyright notice, this list of conditions and the
-  following disclaimer.
-
-* Redistributions in binary form must reproduce the above
-  copyright notice, this list of conditions and the
-  following disclaimer in the documentation and/or other
-  materials provided with the distribution.
-
-* Neither the name of the assimp team, nor the names of its
-  contributors may be used to endorse or promote products
-  derived from this software without specific prior
-  written permission of the assimp team.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-----------------------------------------------------------------------
-*/
-#ifndef AI_Q3BSP_ZIPARCHIVE_H_INC
-#define AI_Q3BSP_ZIPARCHIVE_H_INC
-
-#ifdef ASSIMP_USE_HUNTER
-#  include <minizip/unzip.h>
-#else
-#  include <unzip.h>
-#endif
-#include <assimp/IOStream.hpp>
-#include <assimp/IOSystem.hpp>
-#include <vector>
-#include <map>
-#include <cassert>
-
-namespace Assimp {
-namespace Q3BSP {
-
-// ------------------------------------------------------------------------------------------------
-/// \class      IOSystem2Unzip
-/// \ingroup    Assimp::Q3BSP
-///
-/// \brief
-// ------------------------------------------------------------------------------------------------
-class IOSystem2Unzip {
-public:
-    static voidpf open(voidpf opaque, const char* filename, int mode);
-    static uLong read(voidpf opaque, voidpf stream, void* buf, uLong size);
-    static uLong write(voidpf opaque, voidpf stream, const void* buf, uLong size);
-    static long tell(voidpf opaque, voidpf stream);
-    static long seek(voidpf opaque, voidpf stream, uLong offset, int origin);
-    static int close(voidpf opaque, voidpf stream);
-    static int testerror(voidpf opaque, voidpf stream);
-    static zlib_filefunc_def get(IOSystem* pIOHandler);
-};
-
-// ------------------------------------------------------------------------------------------------
-/// \class      ZipFile
-/// \ingroup    Assimp::Q3BSP
-///
-/// \brief
-// ------------------------------------------------------------------------------------------------
-class ZipFile : public IOStream {
-    friend class Q3BSPZipArchive;
-
-public:
-    explicit ZipFile(size_t size);
-    ~ZipFile();
-    size_t Read(void* pvBuffer, size_t pSize, size_t pCount );
-    size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/);
-    size_t FileSize() const;
-    aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/);
-    size_t Tell() const;
-    void Flush();
-
-private:
-    void* m_Buffer;
-    size_t m_Size;
-};
-
-// ------------------------------------------------------------------------------------------------
-/// \class      Q3BSPZipArchive
-/// \ingroup    Assimp::Q3BSP
-///
-/// \brief  IMplements a zip archive like the WinZip archives. Will be also used to import data
-/// from a P3K archive ( Quake level format ).
-// ------------------------------------------------------------------------------------------------
-class Q3BSPZipArchive : public Assimp::IOSystem {
-public:
-    static const unsigned int FileNameSize = 256;
-
-public:
-    Q3BSPZipArchive(IOSystem* pIOHandler, const std::string & rFile);
-    ~Q3BSPZipArchive();
-    bool Exists(const char* pFile) const;
-    char getOsSeparator() const;
-    IOStream* Open(const char* pFile, const char* pMode = "rb");
-    void Close(IOStream* pFile);
-    bool isOpen() const;
-    void getFileList(std::vector<std::string> &rFileList);
-
-private:
-    bool mapArchive();
-
-private:
-    unzFile m_ZipFileHandle;
-    std::map<std::string, ZipFile*> m_ArchiveMap;
-};
-
-// ------------------------------------------------------------------------------------------------
-
-} // Namespace Q3BSP
-} // Namespace Assimp
-
-#endif // AI_Q3BSP_ZIPARCHIVE_H_INC

+ 3 - 3
code/X/XFileParser.cpp

@@ -596,11 +596,11 @@ void XFileParser::ParseDataObjectMeshNormals( Mesh* pMesh)
     // do not crah when no face definitions are there
     if (numFaces > 0) {
         // normal face creation
-        pMesh->mNormFaces.resize( pMesh->mNormFaces.size() + numFaces );
+        pMesh->mNormFaces.resize( numFaces );
         for( unsigned int a = 0; a < numFaces; ++a ) {
             unsigned int numIndices = ReadInt();
-            pMesh->mNormFaces.push_back( Face() );
-            Face& face = pMesh->mNormFaces.back();
+            pMesh->mNormFaces[a] = Face();
+            Face& face = pMesh->mNormFaces[a];
             for( unsigned int b = 0; b < numIndices; ++b ) {
                 face.mIndices.push_back( ReadInt());
             }

+ 1 - 1
code/X3D/X3DExporter.cpp

@@ -68,7 +68,7 @@ aiMatrix4x4 out_matr;
 	}
 
 	// multiplicate all matrices in reverse order
-	for(std::list<aiMatrix4x4>::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); rit++) out_matr = out_matr * (*rit);
+	for(std::list<aiMatrix4x4>::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit) out_matr = out_matr * (*rit);
 
 	return out_matr;
 }

+ 30 - 30
code/X3D/X3DImporter.cpp

@@ -136,8 +136,8 @@ X3DImporter::~X3DImporter() {
 void X3DImporter::Clear() {
 	NodeElement_Cur = nullptr;
 	// Delete all elements
-	if(NodeElement_List.size()) {
-        for ( std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++ ) {
+	if(!NodeElement_List.empty()) {
+        for ( std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it ) {
             delete *it;
         }
 		NodeElement_List.clear();
@@ -151,7 +151,7 @@ void X3DImporter::Clear() {
 
 bool X3DImporter::FindNodeElement_FromRoot(const std::string& pID, const CX3DImporter_NodeElement::EType pType, CX3DImporter_NodeElement** pElement)
 {
-	for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++)
+	for(std::list<CX3DImporter_NodeElement*>::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it)
 	{
 		if(((*it)->Type == pType) && ((*it)->ID == pID))
 		{
@@ -182,7 +182,7 @@ bool X3DImporter::FindNodeElement_FromNode(CX3DImporter_NodeElement* pStartNode,
 	}// if((pStartNode->Type() == pType) && (pStartNode->ID() == pID))
 
 	// Check childs of pStartNode.
-	for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = pStartNode->Child.begin(); ch_it != pStartNode->Child.end(); ch_it++)
+	for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = pStartNode->Child.begin(); ch_it != pStartNode->Child.end(); ++ch_it)
 	{
 		found = FindNodeElement_FromNode(*ch_it, pID, pType, pElement);
         if ( found )
@@ -614,10 +614,10 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsArrCol3f(const int pAttrIdx, std::ve
 
 	XML_ReadNode_GetAttrVal_AsListCol3f(pAttrIdx, tlist);// read as list
 	// and copy to array
-	if(tlist.size() > 0)
+	if(!tlist.empty())
 	{
 		pValue.reserve(tlist.size());
-		for(std::list<aiColor3D>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
+		for(std::list<aiColor3D>::iterator it = tlist.begin(); it != tlist.end(); ++it) pValue.push_back(*it);
 	}
 }
 
@@ -647,10 +647,10 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsArrCol4f(const int pAttrIdx, std::ve
 
 	XML_ReadNode_GetAttrVal_AsListCol4f(pAttrIdx, tlist);// read as list
 	// and copy to array
-	if(tlist.size() > 0)
+	if(!tlist.empty())
 	{
 		pValue.reserve(tlist.size());
-        for ( std::list<aiColor4D>::iterator it = tlist.begin(); it != tlist.end(); it++ )
+        for ( std::list<aiColor4D>::iterator it = tlist.begin(); it != tlist.end(); ++it )
         {
             pValue.push_back( *it );
         }
@@ -684,10 +684,10 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsArrVec2f(const int pAttrIdx, std::ve
 
 	XML_ReadNode_GetAttrVal_AsListVec2f(pAttrIdx, tlist);// read as list
 	// and copy to array
-	if(tlist.size() > 0)
+	if(!tlist.empty())
 	{
 		pValue.reserve(tlist.size());
-        for ( std::list<aiVector2D>::iterator it = tlist.begin(); it != tlist.end(); it++ )
+        for ( std::list<aiVector2D>::iterator it = tlist.begin(); it != tlist.end(); ++it )
         {
             pValue.push_back( *it );
         }
@@ -722,10 +722,10 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsArrVec3f(const int pAttrIdx, std::ve
 
 	XML_ReadNode_GetAttrVal_AsListVec3f(pAttrIdx, tlist);// read as list
 	// and copy to array
-	if(tlist.size() > 0)
+	if(!tlist.empty())
 	{
 		pValue.reserve(tlist.size());
-        for ( std::list<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); it++ )
+        for ( std::list<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); ++it )
         {
             pValue.push_back( *it );
         }
@@ -823,7 +823,7 @@ void X3DImporter::GeometryHelper_Extend_PointToLine(const std::list<aiVector3D>&
     std::list<aiVector3D>::const_iterator pit = pPoint.begin();
     std::list<aiVector3D>::const_iterator pit_last = pPoint.end();
 
-	pit_last--;
+	--pit_last;
 
     if ( pPoint.size() < 2 )
     {
@@ -837,7 +837,7 @@ void X3DImporter::GeometryHelper_Extend_PointToLine(const std::list<aiVector3D>&
 	{
 		pLine.push_back(*pit);// second point of previous line
 		pLine.push_back(*pit);// first point of next line
-		pit++;
+		++pit;
 	}
 	// add last point of last line
 	pLine.push_back(*pit);
@@ -855,7 +855,7 @@ void X3DImporter::GeometryHelper_Extend_PolylineIdxToLineIdx(const std::list<int
 		{
 			std::list<int32_t>::const_iterator plit_next;
 
-			plit_next = plit, plit_next++;
+			plit_next = plit, ++plit_next;
 			pLineCoordIdx.push_back(*plit);// second point of previous line.
 			pLineCoordIdx.push_back(-1);// delimiter
 			if((*plit_next == (-1)) || (plit_next == pPolylineCoordIdx.end())) break;// current polyline is finished
@@ -910,7 +910,7 @@ void X3DImporter::GeometryHelper_CoordIdxStr2FacesArr(const std::vector<int32_t>
 	pFaces.reserve(f_data.size() / 3);
 	inds.reserve(4);
     //PrintVectorSet("build. ci", pCoordIdx);
-	for(std::vector<int32_t>::iterator it = f_data.begin(); it != f_data.end(); it++)
+	for(std::vector<int32_t>::iterator it = f_data.begin(); it != f_data.end(); ++it)
 	{
 		// when face is got count how many indices in it.
 		if(*it == (-1))
@@ -957,7 +957,7 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor3D
 std::list<aiColor4D> tcol;
 
 	// create RGBA array from RGB.
-	for(std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); it++) tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1));
+	for(std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); ++it) tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1));
 
 	// call existing function for adding RGBA colors
 	MeshGeometry_AddColor(pMesh, tcol, pColorPerVertex);
@@ -997,7 +997,7 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D
                 pMesh.mColors[ 0 ][ pMesh.mFaces[ fi ].mIndices[ vi ] ] = *col_it;
             }
 
-			col_it++;
+			++col_it;
 		}
 	}// if(pColorPerVertex) else
 }
@@ -1008,7 +1008,7 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t
     std::list<aiColor4D> tcol;
 
 	// create RGBA array from RGB.
-    for ( std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); it++ )
+    for ( std::list<aiColor3D>::const_iterator it = pColors.begin(); it != pColors.end(); ++it )
     {
         tcol.push_back( aiColor4D( ( *it ).r, ( *it ).g, ( *it ).b, 1 ) );
     }
@@ -1031,7 +1031,7 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t
 
 	// copy list to array because we are need indexed access to colors.
 	col_arr_copy.reserve(pColors.size());
-    for ( std::list<aiColor4D>::const_iterator it = pColors.begin(); it != pColors.end(); it++ )
+    for ( std::list<aiColor4D>::const_iterator it = pColors.begin(); it != pColors.end(); ++it )
     {
         col_arr_copy.push_back( *it );
     }
@@ -1048,7 +1048,7 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t
 			}
 			// create list with colors for every vertex.
 			col_tgt_arr.resize(pMesh.mNumVertices);
-			for(std::vector<int32_t>::const_iterator colidx_it = pColorIdx.begin(), coordidx_it = pCoordIdx.begin(); colidx_it != pColorIdx.end(); colidx_it++, coordidx_it++)
+			for(std::vector<int32_t>::const_iterator colidx_it = pColorIdx.begin(), coordidx_it = pCoordIdx.begin(); colidx_it != pColorIdx.end(); ++colidx_it, ++coordidx_it)
 			{
                 if ( *colidx_it == ( -1 ) )
                 {
@@ -1121,7 +1121,7 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t
 	}// if(pColorPerVertex) else
 
 	// copy array to list for calling function that add colors.
-	for(std::vector<aiColor4D>::const_iterator it = col_tgt_arr.begin(); it != col_tgt_arr.end(); it++) col_tgt_list.push_back(*it);
+	for(std::vector<aiColor4D>::const_iterator it = col_tgt_arr.begin(); it != col_tgt_arr.end(); ++it) col_tgt_list.push_back(*it);
 	// add prepared colors list to mesh.
 	MeshGeometry_AddColor(pMesh, col_tgt_list, pColorPerVertex);
 }
@@ -1134,7 +1134,7 @@ void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::vector<int32_
 
 	// copy list to array because we are need indexed access to normals.
 	norm_arr_copy.reserve(pNormals.size());
-    for ( std::list<aiVector3D>::const_iterator it = pNormals.begin(); it != pNormals.end(); it++ )
+    for ( std::list<aiVector3D>::const_iterator it = pNormals.begin(); it != pNormals.end(); ++it )
     {
         norm_arr_copy.push_back( *it );
     }
@@ -1147,7 +1147,7 @@ void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::vector<int32_
 			if(pNormalIdx.size() != pCoordIdx.size()) throw DeadlyImportError("Normals and Coords inidces count must be equal.");
 
 			tind.reserve(pNormalIdx.size());
-			for(std::vector<int32_t>::const_iterator it = pNormalIdx.begin(); it != pNormalIdx.end(); it++)
+			for(std::vector<int32_t>::const_iterator it = pNormalIdx.begin(); it != pNormalIdx.end(); ++it)
 			{
 				if(*it != (-1)) tind.push_back(*it);
 			}
@@ -1227,7 +1227,7 @@ void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<aiVector
 			// apply color to all vertices of face
 			for(size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = *norm_it;
 
-			norm_it++;
+			++norm_it;
 		}
 	}// if(pNormalPerVertex) else
 }
@@ -1241,7 +1241,7 @@ void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::vector<int3
 
 	// copy list to array because we are need indexed access to normals.
 	texcoord_arr_copy.reserve(pTexCoords.size());
-	for(std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); it++)
+	for(std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); ++it)
 	{
 		texcoord_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0));
 	}
@@ -1291,7 +1291,7 @@ void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<aiVect
 
 	// copy list to array because we are need convert aiVector2D to aiVector3D and also get indexed access as a bonus.
 	tc_arr_copy.reserve(pTexCoords.size());
-    for ( std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); it++ )
+    for ( std::list<aiVector2D>::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); ++it )
     {
         tc_arr_copy.push_back( aiVector3D( ( *it ).x, ( *it ).y, 0 ) );
     }
@@ -1699,7 +1699,7 @@ void X3DImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy
 		// create nodes tree
 		Postprocess_BuildNode(*NodeElement_Cur, *pScene->mRootNode, mesh_list, mat_list, light_list);
 		// copy needed data to scene
-		if(mesh_list.size() > 0)
+		if(!mesh_list.empty())
 		{
 			std::list<aiMesh*>::const_iterator it = mesh_list.begin();
 
@@ -1708,7 +1708,7 @@ void X3DImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy
 			for(size_t i = 0; i < pScene->mNumMeshes; i++) pScene->mMeshes[i] = *it++;
 		}
 
-		if(mat_list.size() > 0)
+		if(!mat_list.empty())
 		{
 			std::list<aiMaterial*>::const_iterator it = mat_list.begin();
 
@@ -1717,7 +1717,7 @@ void X3DImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSy
 			for(size_t i = 0; i < pScene->mNumMaterials; i++) pScene->mMaterials[i] = *it++;
 		}
 
-		if(light_list.size() > 0)
+		if(!light_list.empty())
 		{
 			std::list<aiLight*>::const_iterator it = light_list.begin();
 

+ 3 - 3
code/X3D/X3DImporter_Geometry2D.cpp

@@ -356,7 +356,7 @@ void X3DImporter::ParseNode_Geometry2D_Polyline2D()
 		std::list<aiVector3D> tlist;
 
 		// convert vec2 to vec3
-		for(std::list<aiVector2D>::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); it2++) tlist.push_back(aiVector3D(it2->x, it2->y, 0));
+		for(std::list<aiVector2D>::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2) tlist.push_back(aiVector3D(it2->x, it2->y, 0));
 
 		// convert point set to line set
 		GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices);
@@ -399,7 +399,7 @@ void X3DImporter::ParseNode_Geometry2D_Polypoint2D()
 		if(!def.empty()) ne->ID = def;
 
 		// convert vec2 to vec3
-		for(std::list<aiVector2D>::iterator it2 = point.begin(); it2 != point.end(); it2++)
+		for(std::list<aiVector2D>::iterator it2 = point.begin(); it2 != point.end(); ++it2)
 		{
 			((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0));
 		}
@@ -500,7 +500,7 @@ void X3DImporter::ParseNode_Geometry2D_TriangleSet2D()
 		if(!def.empty()) ne->ID = def;
 
 		// convert vec2 to vec3
-		for(std::list<aiVector2D>::iterator it2 = vertices.begin(); it2 != vertices.end(); it2++)
+		for(std::list<aiVector2D>::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2)
 		{
 			((CX3DImporter_NodeElement_Geometry2D*)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0));
 		}

+ 7 - 7
code/X3D/X3DImporter_Geometry3D.cpp

@@ -153,11 +153,11 @@ void X3DImporter::ParseNode_Geometry3D_Cone()
 		{
 			StandardShapes::MakeCircle(bottomRadius, tess, tvec);
 			height = -(height / 2);
-			for(std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); it++) it->y = height;// y - because circle made in oXZ.
+			for(std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); ++it) it->y = height;// y - because circle made in oXZ.
 		}
 
 		// copy data from temp array
-		for(std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); it++) ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices.push_back(*it);
+		for(std::vector<aiVector3D>::iterator it = tvec.begin(); it != tvec.end(); ++it) ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices.push_back(*it);
 
 		((CX3DImporter_NodeElement_Geometry3D*)ne)->Solid = solid;
 		((CX3DImporter_NodeElement_Geometry3D*)ne)->NumIndices = 3;
@@ -226,11 +226,11 @@ void X3DImporter::ParseNode_Geometry3D_Cylinder()
 		// copy data from temp arrays
 		std::list<aiVector3D>& vlist = ((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices;// just short alias.
 
-		for(std::vector<aiVector3D>::iterator it = tside.begin(); it != tside.end(); it++) vlist.push_back(*it);
+		for(std::vector<aiVector3D>::iterator it = tside.begin(); it != tside.end(); ++it) vlist.push_back(*it);
 
 		if(top)
 		{
-			for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); it++)
+			for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); ++it)
 			{
 				(*it).y = height;// y - because circle made in oXZ.
 				vlist.push_back(*it);
@@ -239,7 +239,7 @@ void X3DImporter::ParseNode_Geometry3D_Cylinder()
 
 		if(bottom)
 		{
-			for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); it++)
+			for(std::vector<aiVector3D>::iterator it = tcir.begin(); it != tcir.end(); ++it)
 			{
 				(*it).y = -height;// y - because circle made in oXZ.
 				vlist.push_back(*it);
@@ -336,7 +336,7 @@ void X3DImporter::ParseNode_Geometry3D_ElevationGrid()
 					aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi);
 
 					grid_alias.Vertices.push_back(tvec);
-					he_it++;
+					++he_it;
 				}
 			}
 		}// END: create grid vertices list
@@ -977,7 +977,7 @@ void X3DImporter::ParseNode_Geometry3D_Sphere()
 
 		StandardShapes::MakeSphere(tess, tlist);
 		// copy data from temp array and apply scale
-		for(std::vector<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); it++)
+		for(std::vector<aiVector3D>::iterator it = tlist.begin(); it != tlist.end(); ++it)
 		{
 			((CX3DImporter_NodeElement_Geometry3D*)ne)->Vertices.push_back(*it * radius);
 		}

+ 1 - 1
code/X3D/X3DImporter_Networking.cpp

@@ -93,7 +93,7 @@ void X3DImporter::ParseNode_Networking_Inline()
 		// at this place new group mode created and made current, so we can name it.
 		if(!def.empty()) NodeElement_Cur->ID = def;
 
-		if(load && (url.size() > 0))
+		if(load && !url.empty())
 		{
 			std::string full_path = mpIOHandler->CurrentDirectory() + url.front();
 

+ 40 - 40
code/X3D/X3DImporter_Postprocess.cpp

@@ -81,7 +81,7 @@ aiMatrix4x4 X3DImporter::PostprocessHelper_Matrix_GlobalToCurrent() const
 	}
 
 	// multiplicate all matrices in reverse order
-	for(std::list<aiMatrix4x4>::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); rit++) out_matr = out_matr * (*rit);
+	for(std::list<aiMatrix4x4>::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit) out_matr = out_matr * (*rit);
 
 	return out_matr;
 }
@@ -89,7 +89,7 @@ aiMatrix4x4 X3DImporter::PostprocessHelper_Matrix_GlobalToCurrent() const
 void X3DImporter::PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement& pNodeElement, std::list<CX3DImporter_NodeElement*>& pList) const
 {
 	// walk through childs and find for metadata.
-	for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
+	for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it)
 	{
 		if(((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean) || ((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble) ||
 			((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat) || ((*el_it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger) ||
@@ -194,7 +194,7 @@ void X3DImporter::Postprocess_BuildMaterial(const CX3DImporter_NodeElement& pNod
 	aiMaterial& taimat = **pMaterial;// creating alias for convenience.
 
 	// at this point pNodeElement point to <Appearance> node. Walk through childs and add all stored data.
-	for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++)
+	for(std::list<CX3DImporter_NodeElement*>::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it)
 	{
 		if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material)
 		{
@@ -255,7 +255,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		std::vector<aiVector3D> tarr;
 
 		tarr.reserve(tnemesh.Vertices.size());
-		for(std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); it++) tarr.push_back(*it);
+		for(std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) tarr.push_back(*it);
 		*pMesh = StandardShapes::MakeMesh(tarr, static_cast<unsigned int>(tnemesh.NumIndices));// create mesh from vertices using Assimp help.
 
 		return;// mesh is build, nothing to do anymore.
@@ -273,7 +273,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		std::vector<aiVector3D> tarr;
 
 		tarr.reserve(tnemesh.Vertices.size());
-		for(std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); it++) tarr.push_back(*it);
+		for(std::list<aiVector3D>::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) tarr.push_back(*it);
 
 		*pMesh = StandardShapes::MakeMesh(tarr, static_cast<unsigned int>(tnemesh.NumIndices));// create mesh from vertices using Assimp help.
 
@@ -289,7 +289,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		// at first create mesh from existing vertices.
 		*pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIdx, tnemesh.Vertices);
 		// copy additional information from children
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
 				MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
@@ -301,7 +301,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 				MeshGeometry_AddTexCoord(**pMesh, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
 			else
 				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + to_string((*ch_it)->Type) + ".");
-		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
 	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid)
@@ -313,7 +313,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience
 
 		// at first search for <Coordinate> node and create mesh.
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
 			{
@@ -322,7 +322,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		}
 
 		// copy additional information from children
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
 				MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color*)*ch_it)->Value, tnemesh.ColorPerVertex);
@@ -338,7 +338,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
 			else
 				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + to_string((*ch_it)->Type) + ".");
-		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
 	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet)
@@ -348,7 +348,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience
 
 		// at first search for <Coordinate> node and create mesh.
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
 			{
@@ -357,7 +357,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		}
 
 		// copy additional information from children
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			ai_assert(*pMesh);
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
@@ -369,7 +369,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 				{} // skip because already read when mesh created.
 			else
 				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + to_string((*ch_it)->Type) + ".");
-		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
 	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet)
@@ -381,7 +381,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		CX3DImporter_NodeElement_IndexedSet& tnemesh = *((CX3DImporter_NodeElement_IndexedSet*)&pNodeElement);// create alias for convenience
 
 		// at first search for <Coordinate> node and create mesh.
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
 			{
@@ -390,7 +390,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		}
 
 		// copy additional information from children
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			ai_assert(*pMesh);
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
@@ -408,7 +408,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 			else
 				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \
 																	IndexedTriangleStripSet: " + to_string((*ch_it)->Type) + ".");
-		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
 	}// if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet))
@@ -430,7 +430,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
 
 		// at first search for <Coordinate> node and create mesh.
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
 			{
@@ -438,7 +438,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 
 				vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.size());
 				for(std::list<aiVector3D>::const_iterator it = ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.begin();
-					it != ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.end(); it++)
+					it != ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.end(); ++it)
 				{
 					vec_copy.push_back(*it);
 				}
@@ -448,7 +448,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		}
 
 		// copy additional information from children
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			ai_assert(*pMesh);
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
@@ -459,7 +459,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 				{} // skip because already read when mesh created.
 			else
 				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + to_string((*ch_it)->Type) + ".");
-		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
 	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet)
@@ -469,7 +469,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
 
 		// at first search for <Coordinate> node and create mesh.
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
 			{
@@ -478,7 +478,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		}
 
 		// copy additional information from children
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			ai_assert(*pMesh);
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
@@ -489,7 +489,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 				{} // skip because already read when mesh created.
 			else
 				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + to_string((*ch_it)->Type) + ".");
-		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
 	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet)
@@ -499,7 +499,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
 
 		// at first search for <Coordinate> node and create mesh.
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
 			{
@@ -508,7 +508,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		}
 
 		// copy additional information from children
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			if ( nullptr == *pMesh ) {
 				break;
@@ -526,7 +526,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
 			else
 				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + to_string((*ch_it)->Type) + ".");
-		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
 	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet)
@@ -536,7 +536,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
 
 		// at first search for <Coordinate> node and create mesh.
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
 			{
@@ -544,7 +544,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 
 				vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.size());
 				for(std::list<aiVector3D>::const_iterator it = ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.begin();
-					it != ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.end(); it++)
+					it != ((CX3DImporter_NodeElement_Coordinate*)*ch_it)->Value.end(); ++it)
 				{
 					vec_copy.push_back(*it);
 				}
@@ -554,7 +554,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		}
 
 		// copy additional information from children
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			ai_assert(*pMesh);
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
@@ -570,7 +570,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
 			else
 				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + to_string((*ch_it)->Type) + ".");
-		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
 	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet)
@@ -580,7 +580,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		CX3DImporter_NodeElement_Set& tnemesh = *((CX3DImporter_NodeElement_Set*)&pNodeElement);// create alias for convenience
 
 		// at first search for <Coordinate> node and create mesh.
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate)
 			{
@@ -589,7 +589,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 		}
 
 		// copy additional information from children
-		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 		{
 			ai_assert(*pMesh);
 			if((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color)
@@ -605,7 +605,7 @@ void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement& pNodeEle
 				MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate*)*ch_it)->Value);
 			else
 				throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + to_string((*ch_it)->Type) + ".");
-		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ch_it++)
+		}// for(std::list<CX3DImporter_NodeElement*>::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it)
 
 		return;// mesh is build, nothing to do anymore.
 	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet)
@@ -639,16 +639,16 @@ void X3DImporter::Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeEle
 			}
 			else
 			{
-				for(size_t i = 0; i < (size_t)tne_group.Choice; i++) chit_begin++;// forward iterator to chosen node.
+				for(size_t i = 0; i < (size_t)tne_group.Choice; i++) ++chit_begin;// forward iterator to chosen node.
 
 				chit_end = chit_begin;
-				chit_end++;// point end iterator to next element after chosen node.
+				++chit_end;// point end iterator to next element after chosen node.
 			}
 		}// if(tne_group.UseChoice)
 	}// if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group)
 
 	// Reserve memory for fast access and check children.
-	for(std::list<CX3DImporter_NodeElement*>::const_iterator it = chit_begin; it != chit_end; it++)
+	for(std::list<CX3DImporter_NodeElement*>::const_iterator it = chit_begin; it != chit_end; ++it)
 	{// in this loop we do not read metadata because it's already read at begin.
 		if((*it)->Type == CX3DImporter_NodeElement::ENET_Group)
 		{
@@ -677,7 +677,7 @@ void X3DImporter::Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeEle
 	}// for(std::list<CX3DImporter_NodeElement*>::const_iterator it = chit_begin; it != chit_end; it++)
 
 	// copy data about children and meshes to aiNode.
-	if(SceneNode_Child.size() > 0)
+	if(!SceneNode_Child.empty())
 	{
 		std::list<aiNode*>::const_iterator it = SceneNode_Child.begin();
 
@@ -686,7 +686,7 @@ void X3DImporter::Postprocess_BuildNode(const CX3DImporter_NodeElement& pNodeEle
 		for(size_t i = 0; i < pSceneNode.mNumChildren; i++) pSceneNode.mChildren[i] = *it++;
 	}
 
-	if(SceneNode_Mesh.size() > 0)
+	if(!SceneNode_Mesh.empty())
 	{
 		std::list<unsigned int>::const_iterator it = SceneNode_Mesh.begin();
 
@@ -706,7 +706,7 @@ void X3DImporter::Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape& p
     CX3DImporter_NodeElement::EType mesh_type = CX3DImporter_NodeElement::ENET_Invalid;
     unsigned int mat_ind = 0;
 
-	for(std::list<CX3DImporter_NodeElement*>::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++)
+	for(std::list<CX3DImporter_NodeElement*>::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); ++it)
 	{
 		if(PostprocessHelper_ElementIsMesh((*it)->Type))
 		{
@@ -779,7 +779,7 @@ void X3DImporter::Postprocess_CollectMetadata(const CX3DImporter_NodeElement& pN
 		// copy collected metadata to output node.
         pSceneNode.mMetaData = aiMetadata::Alloc( static_cast<unsigned int>(meta_list.size()) );
 		meta_idx = 0;
-		for(std::list<CX3DImporter_NodeElement*>::const_iterator it = meta_list.begin(); it != meta_list.end(); it++, meta_idx++)
+		for(std::list<CX3DImporter_NodeElement*>::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx)
 		{
 			CX3DImporter_NodeElement_Meta* cur_meta = (CX3DImporter_NodeElement_Meta*)*it;
 

+ 6 - 6
code/X3D/X3DImporter_Rendering.cpp

@@ -295,7 +295,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleFanSet()
 		ne_alias.CoordIndex.clear();
 		int counter = 0;
 		int32_t idx[3];
-		for(std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); idx_it++)
+		for(std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it)
 		{
 			idx[2] = *idx_it;
 			if (idx[2] < 0)
@@ -413,7 +413,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleSet()
 		ne_alias.CoordIndex.clear();
 		int counter = 0;
 		int32_t idx[3];
-		for(std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); idx_it++)
+		for(std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it)
 		{
 			idx[counter++] = *idx_it;
 			if (counter > 2)
@@ -519,7 +519,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleStripSet()
 		ne_alias.CoordIndex.clear();
 		int counter = 0;
 		int32_t idx[3];
-		for(std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); idx_it++)
+		for(std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it)
 		{
 			idx[2] = *idx_it;
 			if (idx[2] < 0)
@@ -617,7 +617,7 @@ void X3DImporter::ParseNode_Rendering_LineSet()
 		size_t coord_num = 0;
 
 		ne_alias.CoordIndex.clear();
-		for(std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+		for(std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it)
 		{
 			if(*vc_it < 2) throw DeadlyImportError("LineSet. vertexCount shall be greater than or equal to two.");
 
@@ -765,7 +765,7 @@ void X3DImporter::ParseNode_Rendering_TriangleFanSet()
 		// assign indices for first triangle
 		coord_num_first = 0;
 		coord_num_prev = 1;
-		for(std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+		for(std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it)
 		{
 			if(*vc_it < 3) throw DeadlyImportError("TriangleFanSet. fanCount shall be greater than or equal to three.");
 
@@ -956,7 +956,7 @@ void X3DImporter::ParseNode_Rendering_TriangleStripSet()
 
 		ne_alias.CoordIndex.clear();
 		coord_num_sb = 0;
-		for(std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+		for(std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it)
 		{
 			if(*vc_it < 3) throw DeadlyImportError("TriangleStripSet. stripCount shall be greater than or equal to three.");
 

+ 1 - 1
code/X3D/X3DImporter_Texturing.cpp

@@ -89,7 +89,7 @@ void X3DImporter::ParseNode_Texturing_ImageTexture()
 		((CX3DImporter_NodeElement_ImageTexture*)ne)->RepeatS = repeatS;
 		((CX3DImporter_NodeElement_ImageTexture*)ne)->RepeatT = repeatT;
 		// Attribute "url" can contain list of strings. But we need only one - first.
-		if(url.size() > 0)
+		if(!url.empty())
 			((CX3DImporter_NodeElement_ImageTexture*)ne)->URL = url.front();
 		else
 			((CX3DImporter_NodeElement_ImageTexture*)ne)->URL = "";

+ 8 - 61
code/glTF/glTFAsset.h

@@ -92,38 +92,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #   endif
 #endif
 
+#include "glTF/glTFCommon.h"
+
 namespace glTF
 {
-#ifdef ASSIMP_API
-    using Assimp::IOStream;
-    using Assimp::IOSystem;
-    using std::shared_ptr;
-#else
-    using std::shared_ptr;
-
-    typedef std::runtime_error DeadlyImportError;
-    typedef std::runtime_error DeadlyExportError;
-
-    enum aiOrigin { aiOrigin_SET = 0, aiOrigin_CUR = 1, aiOrigin_END = 2 };
-    class IOSystem;
-    class IOStream
-    {
-        FILE* f;
-    public:
-        IOStream(FILE* file) : f(file) {}
-        ~IOStream() { fclose(f); f = 0; }
-
-        size_t Read(void* b, size_t sz, size_t n) { return fread(b, sz, n, f); }
-        size_t Write(const void* b, size_t sz, size_t n) { return fwrite(b, sz, n, f); }
-        int    Seek(size_t off, aiOrigin orig) { return fseek(f, off, int(orig)); }
-        size_t Tell() const { return ftell(f); }
-
-        size_t FileSize() {
-            long p = Tell(), len = (Seek(0, aiOrigin_END), Tell());
-            return size_t((Seek(p, aiOrigin_SET), len));
-        }
-    };
-#endif
+    using glTFCommon::shared_ptr;
+    using glTFCommon::IOSystem;
+    using glTFCommon::IOStream;
 
     using rapidjson::Value;
     using rapidjson::Document;
@@ -136,37 +111,9 @@ namespace glTF
     struct Light;
     struct Skin;
 
-
-    // Vec/matrix types, as raw float arrays
-    typedef float (vec3)[3];
-    typedef float (vec4)[4];
-    typedef float (mat4)[16];
-
-
-    namespace Util
-    {
-        void EncodeBase64(const uint8_t* in, size_t inLength, std::string& out);
-
-        size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out);
-
-        inline size_t DecodeBase64(const char* in, uint8_t*& out)
-        {
-            return DecodeBase64(in, strlen(in), out);
-        }
-
-        struct DataURI
-        {
-            const char* mediaType;
-            const char* charset;
-            bool base64;
-            const char* data;
-            size_t dataLength;
-        };
-
-        //! Check if a uri is a data URI
-        inline bool ParseDataURI(const char* uri, size_t uriLen, DataURI& out);
-    }
-
+    using glTFCommon::vec3;
+    using glTFCommon::vec4;
+    using glTFCommon::mat4;
 
     //! Magic number for GLB files
     #define AI_GLB_MAGIC_NUMBER "glTF"

+ 10 - 192
code/glTF/glTFAsset.inl

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -52,6 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endif
 
 using namespace Assimp;
+using namespace glTFCommon;
 
 namespace glTF {
 
@@ -301,7 +301,7 @@ inline void Buffer::Read(Value& obj, Asset& r)
 
     const char* uri = it->GetString();
 
-    Util::DataURI dataURI;
+    glTFCommon::Util::DataURI dataURI;
     if (ParseDataURI(uri, it->GetStringLength(), dataURI)) {
         if (dataURI.base64) {
             uint8_t* data = 0;
@@ -654,12 +654,12 @@ inline void Image::Read(Value& obj, Asset& r)
         if (Value* uri = FindString(obj, "uri")) {
             const char* uristr = uri->GetString();
 
-            Util::DataURI dataURI;
+            glTFCommon::Util::DataURI dataURI;
             if (ParseDataURI(uristr, uri->GetStringLength(), dataURI)) {
                 mimeType = dataURI.mediaType;
                 if (dataURI.base64) {
                     uint8_t *ptr = nullptr;
-                    mDataLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, ptr);
+                    mDataLength = glTFCommon::Util::DecodeBase64(dataURI.data, dataURI.dataLength, ptr);
                     mData.reset(ptr);
                 }
             }
@@ -1180,8 +1180,12 @@ inline void Light::SetDefaults()
     falloffExponent = 0.f;
 }
 
-inline void Node::Read(Value& obj, Asset& r)
-{
+inline 
+void Node::Read(Value& obj, Asset& r) {
+    if (name.empty()) {
+        name = id;
+    }
+
     if (Value* children = FindArray(obj, "children")) {
         this->children.reserve(children->Size());
         for (unsigned int i = 0; i < children->Size(); ++i) {
@@ -1474,190 +1478,4 @@ inline std::string Asset::FindUniqueID(const std::string& str, const char* suffi
     return id;
 }
 
-namespace Util {
-
-    inline
-    bool ParseDataURI(const char* const_uri, size_t uriLen, DataURI& out) {
-        if ( NULL == const_uri ) {
-            return false;
-        }
-
-        if (const_uri[0] != 0x10) { // we already parsed this uri?
-            if (strncmp(const_uri, "data:", 5) != 0) // not a data uri?
-                return false;
-        }
-
-        // set defaults
-        out.mediaType = "text/plain";
-        out.charset = "US-ASCII";
-        out.base64 = false;
-
-        char* uri = const_cast<char*>(const_uri);
-        if (uri[0] != 0x10) {
-            uri[0] = 0x10;
-            uri[1] = uri[2] = uri[3] = uri[4] = 0;
-
-            size_t i = 5, j;
-            if (uri[i] != ';' && uri[i] != ',') { // has media type?
-                uri[1] = char(i);
-                for (; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {
-                    // nothing to do!
-                }
-            }
-            while (uri[i] == ';' && i < uriLen) {
-                uri[i++] = '\0';
-                for (j = i; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {
-                    // nothing to do!
-                }
-
-                if ( strncmp( uri + j, "charset=", 8 ) == 0 ) {
-                    uri[2] = char(j + 8);
-                } else if ( strncmp( uri + j, "base64", 6 ) == 0 ) {
-                    uri[3] = char(j);
-                }
-            }
-            if (i < uriLen) {
-                uri[i++] = '\0';
-                uri[4] = char(i);
-            } else {
-                uri[1] = uri[2] = uri[3] = 0;
-                uri[4] = 5;
-            }
-        }
-
-        if ( uri[ 1 ] != 0 ) {
-            out.mediaType = uri + uri[ 1 ];
-        }
-        if ( uri[ 2 ] != 0 ) {
-            out.charset = uri + uri[ 2 ];
-        }
-        if ( uri[ 3 ] != 0 ) {
-            out.base64 = true;
-        }
-        out.data = uri + uri[4];
-        out.dataLength = (uri + uriLen) - out.data;
-
-        return true;
-    }
-
-    template<bool B>
-    struct DATA
-    {
-        static const uint8_t tableDecodeBase64[128];
-    };
-
-    template<bool B>
-    const uint8_t DATA<B>::tableDecodeBase64[128] = {
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62,  0,  0,  0, 63,
-        52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  0,  0,  0, 64,  0,  0,
-         0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
-         0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  0,  0,  0,  0,  0
-    };
-
-    inline char EncodeCharBase64(uint8_t b)
-    {
-        return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="[size_t(b)];
-    }
-
-    inline uint8_t DecodeCharBase64(char c)
-    {
-        return DATA<true>::tableDecodeBase64[size_t(c)]; // TODO faster with lookup table or ifs?
-        /*if (c >= 'A' && c <= 'Z') return c - 'A';
-        if (c >= 'a' && c <= 'z') return c - 'a' + 26;
-        if (c >= '0' && c <= '9') return c - '0' + 52;
-        if (c == '+') return 62;
-        if (c == '/') return 63;
-        return 64; // '-' */
-    }
-
-    inline size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out)
-    {
-        ai_assert(inLength % 4 == 0);
-
-        if (inLength < 4) {
-            out = 0;
-            return 0;
-        }
-
-        int nEquals = int(in[inLength - 1] == '=') +
-                      int(in[inLength - 2] == '=');
-
-        size_t outLength = (inLength * 3) / 4 - nEquals;
-        out = new uint8_t[outLength];
-        memset(out, 0, outLength);
-
-        size_t i, j = 0;
-
-        for (i = 0; i + 4 < inLength; i += 4) {
-            uint8_t b0 = DecodeCharBase64(in[i]);
-            uint8_t b1 = DecodeCharBase64(in[i + 1]);
-            uint8_t b2 = DecodeCharBase64(in[i + 2]);
-            uint8_t b3 = DecodeCharBase64(in[i + 3]);
-
-            out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
-            out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
-            out[j++] = (uint8_t)((b2 << 6) | b3);
-        }
-
-        {
-            uint8_t b0 = DecodeCharBase64(in[i]);
-            uint8_t b1 = DecodeCharBase64(in[i + 1]);
-            uint8_t b2 = DecodeCharBase64(in[i + 2]);
-            uint8_t b3 = DecodeCharBase64(in[i + 3]);
-
-            out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
-            if (b2 < 64) out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
-            if (b3 < 64) out[j++] = (uint8_t)((b2 << 6) | b3);
-        }
-
-        return outLength;
-    }
-
-
-
-    inline void EncodeBase64(
-        const uint8_t* in, size_t inLength,
-        std::string& out)
-    {
-        size_t outLength = ((inLength + 2) / 3) * 4;
-
-        size_t j = out.size();
-        out.resize(j + outLength);
-
-        for (size_t i = 0; i <  inLength; i += 3) {
-            uint8_t b = (in[i] & 0xFC) >> 2;
-            out[j++] = EncodeCharBase64(b);
-
-            b = (in[i] & 0x03) << 4;
-            if (i + 1 < inLength) {
-                b |= (in[i + 1] & 0xF0) >> 4;
-                out[j++] = EncodeCharBase64(b);
-
-                b = (in[i + 1] & 0x0F) << 2;
-                if (i + 2 < inLength) {
-                    b |= (in[i + 2] & 0xC0) >> 6;
-                    out[j++] = EncodeCharBase64(b);
-
-                    b = in[i + 2] & 0x3F;
-                    out[j++] = EncodeCharBase64(b);
-                }
-                else {
-                    out[j++] = EncodeCharBase64(b);
-                    out[j++] = '=';
-                }
-            }
-            else {
-                out[j++] = EncodeCharBase64(b);
-                out[j++] = '=';
-                out[j++] = '=';
-            }
-        }
-    }
-
-}
-
 } // ns glTF

+ 5 - 3
code/glTF/glTFAssetWriter.inl

@@ -55,7 +55,8 @@ namespace glTF {
     namespace {
 
         template<size_t N>
-        inline Value& MakeValue(Value& val, float(&r)[N], MemoryPoolAllocator<>& al) {
+        inline 
+        Value& MakeValue(Value& val, float(&r)[N], MemoryPoolAllocator<>& al) {
             val.SetArray();
             val.Reserve(N, al);
             for (decltype(N) i = 0; i < N; ++i) {
@@ -64,7 +65,8 @@ namespace glTF {
             return val;
         }
 
-        inline Value& MakeValue(Value& val, const std::vector<float> & r, MemoryPoolAllocator<>& al) {
+        inline 
+        Value& MakeValue(Value& val, const std::vector<float> & r, MemoryPoolAllocator<>& al) {
             val.SetArray();
             val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al);
             for (unsigned int i = 0; i < r.size(); ++i) {
@@ -213,7 +215,7 @@ namespace glTF {
         else if (img.HasData()) {
             uri = "data:" + (img.mimeType.empty() ? "application/octet-stream" : img.mimeType);
             uri += ";base64,";
-            Util::EncodeBase64(img.GetData(), img.GetDataLength(), uri);
+            glTFCommon::Util::EncodeBase64(img.GetData(), img.GetDataLength(), uri);
         }
         else {
             uri = img.uri;

+ 193 - 0
code/glTF/glTFCommon.cpp

@@ -0,0 +1,193 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#include "glTF/glTFCommon.h"
+
+namespace glTFCommon {
+
+using namespace glTFCommon::Util;
+
+namespace Util {
+
+size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out) {
+    ai_assert(inLength % 4 == 0);
+
+    if (inLength < 4) {
+        out = 0;
+        return 0;
+    }
+
+    int nEquals = int(in[inLength - 1] == '=') +
+        int(in[inLength - 2] == '=');
+
+    size_t outLength = (inLength * 3) / 4 - nEquals;
+    out = new uint8_t[outLength];
+    memset(out, 0, outLength);
+
+    size_t i, j = 0;
+
+    for (i = 0; i + 4 < inLength; i += 4) {
+        uint8_t b0 = DecodeCharBase64(in[i]);
+        uint8_t b1 = DecodeCharBase64(in[i + 1]);
+        uint8_t b2 = DecodeCharBase64(in[i + 2]);
+        uint8_t b3 = DecodeCharBase64(in[i + 3]);
+
+        out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
+        out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
+        out[j++] = (uint8_t)((b2 << 6) | b3);
+    }
+
+    {
+        uint8_t b0 = DecodeCharBase64(in[i]);
+        uint8_t b1 = DecodeCharBase64(in[i + 1]);
+        uint8_t b2 = DecodeCharBase64(in[i + 2]);
+        uint8_t b3 = DecodeCharBase64(in[i + 3]);
+
+        out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
+        if (b2 < 64) out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
+        if (b3 < 64) out[j++] = (uint8_t)((b2 << 6) | b3);
+    }
+
+    return outLength;
+}
+
+void EncodeBase64(const uint8_t* in, size_t inLength, std::string& out) {
+    size_t outLength = ((inLength + 2) / 3) * 4;
+
+    size_t j = out.size();
+    out.resize(j + outLength);
+
+    for (size_t i = 0; i < inLength; i += 3) {
+        uint8_t b = (in[i] & 0xFC) >> 2;
+        out[j++] = EncodeCharBase64(b);
+
+        b = (in[i] & 0x03) << 4;
+        if (i + 1 < inLength) {
+            b |= (in[i + 1] & 0xF0) >> 4;
+            out[j++] = EncodeCharBase64(b);
+
+            b = (in[i + 1] & 0x0F) << 2;
+            if (i + 2 < inLength) {
+                b |= (in[i + 2] & 0xC0) >> 6;
+                out[j++] = EncodeCharBase64(b);
+
+                b = in[i + 2] & 0x3F;
+                out[j++] = EncodeCharBase64(b);
+            }
+            else {
+                out[j++] = EncodeCharBase64(b);
+                out[j++] = '=';
+            }
+        }
+        else {
+            out[j++] = EncodeCharBase64(b);
+            out[j++] = '=';
+            out[j++] = '=';
+        }
+    }
+}
+
+bool ParseDataURI(const char* const_uri, size_t uriLen, DataURI& out) {
+    if (nullptr == const_uri) {
+        return false;
+    }
+
+    if (const_uri[0] != 0x10) { // we already parsed this uri?
+        if (strncmp(const_uri, "data:", 5) != 0) // not a data uri?
+            return false;
+    }
+
+    // set defaults
+    out.mediaType = "text/plain";
+    out.charset = "US-ASCII";
+    out.base64 = false;
+
+    char* uri = const_cast<char*>(const_uri);
+    if (uri[0] != 0x10) {
+        uri[0] = 0x10;
+        uri[1] = uri[2] = uri[3] = uri[4] = 0;
+
+        size_t i = 5, j;
+        if (uri[i] != ';' && uri[i] != ',') { // has media type?
+            uri[1] = char(i);
+            for (; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {
+                // nothing to do!
+            }
+        }
+        while (uri[i] == ';' && i < uriLen) {
+            uri[i++] = '\0';
+            for (j = i; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {
+                // nothing to do!
+            }
+
+            if (strncmp(uri + j, "charset=", 8) == 0) {
+                uri[2] = char(j + 8);
+            }
+            else if (strncmp(uri + j, "base64", 6) == 0) {
+                uri[3] = char(j);
+            }
+        }
+        if (i < uriLen) {
+            uri[i++] = '\0';
+            uri[4] = char(i);
+        }
+        else {
+            uri[1] = uri[2] = uri[3] = 0;
+            uri[4] = 5;
+        }
+    }
+
+    if (uri[1] != 0) {
+        out.mediaType = uri + uri[1];
+    }
+    if (uri[2] != 0) {
+        out.charset = uri + uri[2];
+    }
+    if (uri[3] != 0) {
+        out.base64 = true;
+    }
+    out.data = uri + uri[4];
+    out.dataLength = (uri + uriLen) - out.data;
+
+    return true;
+}
+
+}
+}

+ 248 - 0
code/glTF/glTFCommon.h

@@ -0,0 +1,248 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#ifndef AI_GLFTCOMMON_H_INC
+#define AI_GLFTCOMMON_H_INC
+
+#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+
+#include <assimp/Exceptional.h>
+
+#include <map>
+#include <string>
+#include <list>
+#include <vector>
+#include <algorithm>
+#include <stdexcept>
+
+#define RAPIDJSON_HAS_STDSTRING 1
+#include <rapidjson/rapidjson.h>
+#include <rapidjson/document.h>
+#include <rapidjson/error/en.h>
+
+#ifdef ASSIMP_API
+#   include <memory>
+#   include <assimp/DefaultIOSystem.h>
+#   include <assimp/ByteSwapper.h>
+#else
+#   include <memory>
+#   define AI_SWAP4(p)
+#   define ai_assert
+#endif
+
+
+#if _MSC_VER > 1500 || (defined __GNUC___)
+#       define ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
+#   else
+#       define gltf_unordered_map map
+#endif
+
+#ifdef ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
+#   include <unordered_map>
+#   if _MSC_VER > 1600
+#       define gltf_unordered_map unordered_map
+#   else
+#       define gltf_unordered_map tr1::unordered_map
+#   endif
+#endif
+
+namespace glTFCommon {
+
+#ifdef ASSIMP_API
+    using Assimp::IOStream;
+    using Assimp::IOSystem;
+    using std::shared_ptr;
+#else
+    using std::shared_ptr;
+
+    typedef std::runtime_error DeadlyImportError;
+    typedef std::runtime_error DeadlyExportError;
+
+    enum aiOrigin {
+        aiOrigin_SET = 0,
+        aiOrigin_CUR = 1,
+        aiOrigin_END = 2
+    };
+
+    class IOSystem;
+
+    class IOStream {
+    public:
+        IOStream(FILE* file) : f(file) {}
+        ~IOStream() { fclose(f); f = 0; }
+
+        size_t Read(void* b, size_t sz, size_t n) { return fread(b, sz, n, f); }
+        size_t Write(const void* b, size_t sz, size_t n) { return fwrite(b, sz, n, f); }
+        int    Seek(size_t off, aiOrigin orig) { return fseek(f, off, int(orig)); }
+        size_t Tell() const { return ftell(f); }
+
+        size_t FileSize() {
+            long p = Tell(), len = (Seek(0, aiOrigin_END), Tell());
+            return size_t((Seek(p, aiOrigin_SET), len));
+        }
+
+    private:
+        FILE* f;
+    };
+#endif
+
+    // Vec/matrix types, as raw float arrays
+    typedef float(vec3)[3];
+    typedef float(vec4)[4];
+    typedef float(mat4)[16];
+
+    inline
+    void CopyValue(const glTFCommon::vec3& v, aiColor4D& out) {
+        out.r = v[0];
+        out.g = v[1];
+        out.b = v[2];
+        out.a = 1.0;
+    }
+
+    inline
+    void CopyValue(const glTFCommon::vec4& v, aiColor4D& out) {
+        out.r = v[0];
+        out.g = v[1];
+        out.b = v[2];
+        out.a = v[3];
+    }
+
+    inline
+    void CopyValue(const glTFCommon::vec4& v, aiColor3D& out) {
+        out.r = v[0];
+        out.g = v[1];
+        out.b = v[2];
+    }
+
+    inline
+    void CopyValue(const glTFCommon::vec3& v, aiColor3D& out) {
+        out.r = v[0];
+        out.g = v[1];
+        out.b = v[2];
+    }
+
+    inline
+    void CopyValue(const glTFCommon::vec3& v, aiVector3D& out) {
+        out.x = v[0];
+        out.y = v[1];
+        out.z = v[2];
+    }
+
+    inline
+    void CopyValue(const glTFCommon::vec4& v, aiQuaternion& out) {
+        out.x = v[0];
+        out.y = v[1];
+        out.z = v[2];
+        out.w = v[3];
+    }
+
+    inline
+    void CopyValue(const glTFCommon::mat4& v, aiMatrix4x4& o) {
+        o.a1 = v[0]; o.b1 = v[1]; o.c1 = v[2]; o.d1 = v[3];
+        o.a2 = v[4]; o.b2 = v[5]; o.c2 = v[6]; o.d2 = v[7];
+        o.a3 = v[8]; o.b3 = v[9]; o.c3 = v[10]; o.d3 = v[11];
+        o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15];
+    }
+
+    namespace Util {
+
+        void EncodeBase64(const uint8_t* in, size_t inLength, std::string& out);
+
+        size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out);
+
+        inline
+            size_t DecodeBase64(const char* in, uint8_t*& out) {
+            return DecodeBase64(in, strlen(in), out);
+        }
+
+        struct DataURI {
+            const char* mediaType;
+            const char* charset;
+            bool base64;
+            const char* data;
+            size_t dataLength;
+        };
+
+        //! Check if a uri is a data URI
+        bool ParseDataURI(const char* const_uri, size_t uriLen, DataURI& out);
+
+        template<bool B>
+        struct DATA {
+            static const uint8_t tableDecodeBase64[128];
+        };
+
+        template<bool B>
+        const uint8_t DATA<B>::tableDecodeBase64[128] = {
+                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62,  0,  0,  0, 63,
+            52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  0,  0,  0, 64,  0,  0,
+                0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
+                0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  0,  0,  0,  0,  0
+        };
+
+        inline
+            char EncodeCharBase64(uint8_t b) {
+            return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="[size_t(b)];
+        }
+
+        inline
+            uint8_t DecodeCharBase64(char c) {
+            return DATA<true>::tableDecodeBase64[size_t(c)]; // TODO faster with lookup table or ifs?
+            /*if (c >= 'A' && c <= 'Z') return c - 'A';
+            if (c >= 'a' && c <= 'z') return c - 'a' + 26;
+            if (c >= '0' && c <= '9') return c - '0' + 52;
+            if (c == '+') return 62;
+            if (c == '/') return 63;
+            return 64; // '-' */
+        }
+
+        size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out);
+
+        void EncodeBase64(const uint8_t* in, size_t inLength, std::string& out);
+    }
+
+}
+
+#endif  // ASSIMP_BUILD_NO_GLTF_IMPORTER
+
+#endif // AI_GLFTCOMMON_H_INC

+ 4 - 1
code/glTF/glTFExporter.cpp

@@ -242,7 +242,10 @@ inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& bu
 
 namespace {
     void GetMatScalar(const aiMaterial* mat, float& val, const char* propName, int type, int idx) {
-        ai_assert(mat->Get(propName, type, idx, val) == AI_SUCCESS);
+        ai_assert( nullptr != mat );
+        if ( nullptr != mat ) {
+            mat->Get(propName, type, idx, val);
+        }
     }
 }
 

+ 24 - 58
code/glTF/glTFImporter.cpp

@@ -82,7 +82,7 @@ glTFImporter::glTFImporter()
 : BaseImporter()
 , meshOffsets()
 , embeddedTexIdxs()
-, mScene( NULL ) {
+, mScene( nullptr ) {
     // empty
 }
 
@@ -90,17 +90,16 @@ glTFImporter::~glTFImporter() {
     // empty
 }
 
-const aiImporterDesc* glTFImporter::GetInfo() const
-{
+const aiImporterDesc* glTFImporter::GetInfo() const {
     return &desc;
 }
 
-bool glTFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool /* checkSig */) const
-{
+bool glTFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool /* checkSig */) const {
     const std::string &extension = GetExtension(pFile);
 
-    if (extension != "gltf" && extension != "glb")
+    if (extension != "gltf" && extension != "glb") {
         return false;
+    }
 
     if (pIOHandler) {
         glTF::Asset asset(pIOHandler);
@@ -116,44 +115,9 @@ bool glTFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool
     return false;
 }
 
-
-
-//static void CopyValue(const glTF::vec3& v, aiColor3D& out)
-//{
-//    out.r = v[0]; out.g = v[1]; out.b = v[2];
-//}
-
-static void CopyValue(const glTF::vec4& v, aiColor4D& out)
-{
-    out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = v[3];
-}
-
-static void CopyValue(const glTF::vec4& v, aiColor3D& out)
-{
-    out.r = v[0]; out.g = v[1]; out.b = v[2];
-}
-
-static void CopyValue(const glTF::vec3& v, aiVector3D& out)
-{
-    out.x = v[0]; out.y = v[1]; out.z = v[2];
-}
-
-static void CopyValue(const glTF::vec4& v, aiQuaternion& out)
-{
-    out.x = v[0]; out.y = v[1]; out.z = v[2]; out.w = v[3];
-}
-
-static void CopyValue(const glTF::mat4& v, aiMatrix4x4& o)
-{
-    o.a1 = v[ 0]; o.b1 = v[ 1]; o.c1 = v[ 2]; o.d1 = v[ 3];
-    o.a2 = v[ 4]; o.b2 = v[ 5]; o.c2 = v[ 6]; o.d2 = v[ 7];
-    o.a3 = v[ 8]; o.b3 = v[ 9]; o.c3 = v[10]; o.d3 = v[11];
-    o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15];
-}
-
-inline void SetMaterialColorProperty(std::vector<int>& embeddedTexIdxs, Asset& /*r*/, glTF::TexProperty prop, aiMaterial* mat,
-    aiTextureType texType, const char* pKey, unsigned int type, unsigned int idx)
-{
+inline
+void SetMaterialColorProperty(std::vector<int>& embeddedTexIdxs, Asset& /*r*/, glTF::TexProperty prop, aiMaterial* mat,
+        aiTextureType texType, const char* pKey, unsigned int type, unsigned int idx) {
     if (prop.texture) {
         if (prop.texture->source) {
             aiString uri(prop.texture->source->uri);
@@ -167,16 +131,14 @@ inline void SetMaterialColorProperty(std::vector<int>& embeddedTexIdxs, Asset& /
 
             mat->AddProperty(&uri, _AI_MATKEY_TEXTURE_BASE, texType, 0);
         }
-    }
-    else {
+    } else {
         aiColor4D col;
         CopyValue(prop.color, col);
         mat->AddProperty(&col, 1, pKey, type, idx);
     }
 }
 
-void glTFImporter::ImportMaterials(glTF::Asset& r)
-{
+void glTFImporter::ImportMaterials(glTF::Asset& r) {
     mScene->mNumMaterials = unsigned(r.materials.Size());
     mScene->mMaterials = new aiMaterial*[mScene->mNumMaterials];
 
@@ -304,7 +266,7 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
 
             aim->mName = mesh.id;
             if (mesh.primitives.size() > 1) {
-                size_t& len = aim->mName.length;
+                ai_uint32& len = aim->mName.length;
                 aim->mName.data[len] = '-';
                 len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p);
             }
@@ -499,27 +461,31 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
     CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
 }
 
-void glTFImporter::ImportCameras(glTF::Asset& r)
-{
-    if (!r.cameras.Size()) return;
+void glTFImporter::ImportCameras(glTF::Asset& r) {
+    if (!r.cameras.Size()) {
+        return;
+    }
 
     mScene->mNumCameras = r.cameras.Size();
     mScene->mCameras = new aiCamera*[r.cameras.Size()];
-
     for (size_t i = 0; i < r.cameras.Size(); ++i) {
         Camera& cam = r.cameras[i];
 
         aiCamera* aicam = mScene->mCameras[i] = new aiCamera();
 
         if (cam.type == Camera::Perspective) {
-
             aicam->mAspect        = cam.perspective.aspectRatio;
-            aicam->mHorizontalFOV = cam.perspective.yfov * aicam->mAspect;
+            aicam->mHorizontalFOV = cam.perspective.yfov * ((aicam->mAspect == 0.f) ? 1.f : aicam->mAspect);
             aicam->mClipPlaneFar  = cam.perspective.zfar;
             aicam->mClipPlaneNear = cam.perspective.znear;
-        }
-        else {
-            // assimp does not support orthographic cameras
+        } else {
+            aicam->mClipPlaneFar = cam.ortographic.zfar;
+            aicam->mClipPlaneNear = cam.ortographic.znear;
+            aicam->mHorizontalFOV = 0.0;
+            aicam->mAspect = 1.0f;
+            if (0.f != cam.ortographic.ymag) {
+                aicam->mAspect = cam.ortographic.xmag / cam.ortographic.ymag;
+            }
         }
     }
 }

+ 9 - 60
code/glTF2/glTF2Asset.h

@@ -95,38 +95,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <assimp/StringUtils.h>
 
+#include "glTF/glTFCommon.h"
+
 namespace glTF2
 {
-#ifdef ASSIMP_API
-    using Assimp::IOStream;
-    using Assimp::IOSystem;
-    using std::shared_ptr;
-#else
-    using std::shared_ptr;
-
-    typedef std::runtime_error DeadlyImportError;
-    typedef std::runtime_error DeadlyExportError;
-
-    enum aiOrigin { aiOrigin_SET = 0, aiOrigin_CUR = 1, aiOrigin_END = 2 };
-    class IOSystem;
-    class IOStream
-    {
-        FILE* f;
-    public:
-        IOStream(FILE* file) : f(file) {}
-        ~IOStream() { fclose(f); f = 0; }
-
-        size_t Read(void* b, size_t sz, size_t n) { return fread(b, sz, n, f); }
-        size_t Write(const void* b, size_t sz, size_t n) { return fwrite(b, sz, n, f); }
-        int    Seek(size_t off, aiOrigin orig) { return fseek(f, off, int(orig)); }
-        size_t Tell() const { return ftell(f); }
-
-        size_t FileSize() {
-            long p = Tell(), len = (Seek(0, aiOrigin_END), Tell());
-            return size_t((Seek(p, aiOrigin_SET), len));
-        }
-    };
-#endif
+    using glTFCommon::shared_ptr;
+    using glTFCommon::IOSystem;
+    using glTFCommon::IOStream;
 
     using rapidjson::Value;
     using rapidjson::Document;
@@ -138,35 +113,9 @@ namespace glTF2
     struct Texture;
     struct Skin;
 
-    // Vec/matrix types, as raw float arrays
-    typedef float (vec3)[3];
-    typedef float (vec4)[4];
-    typedef float (mat4)[16];
-
-    namespace Util
-    {
-        void EncodeBase64(const uint8_t* in, size_t inLength, std::string& out);
-
-        size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out);
-
-        inline size_t DecodeBase64(const char* in, uint8_t*& out)
-        {
-            return DecodeBase64(in, strlen(in), out);
-        }
-
-        struct DataURI
-        {
-            const char* mediaType;
-            const char* charset;
-            bool base64;
-            const char* data;
-            size_t dataLength;
-        };
-
-        //! Check if a uri is a data URI
-        inline bool ParseDataURI(const char* uri, size_t uriLen, DataURI& out);
-    }
-
+    using glTFCommon::vec3;
+    using glTFCommon::vec4;
+    using glTFCommon::mat4;
 
     //! Magic number for GLB files
 	#define AI_GLB_MAGIC_NUMBER "glTF"
@@ -552,7 +501,7 @@ namespace glTF2
 		/// but in real life you'll get:
 		/// "accessor_0" : { byteOffset: 0, byteLength: 4}, "accessor_1" : { byteOffset: 2, byteLength: 4}
 		/// Yes, accessor of next mesh has offset and length which mean: current mesh data is decoded, all other data is encoded.
-		/// And when before you start to read data of current mesh (with encoded data ofcourse) you must decode region of "bufferView", after read finished
+		/// And when before you start to read data of current mesh (with encoded data of course) you must decode region of "bufferView", after read finished
 		/// delete encoding mark. And after that you can repeat process: decode data of mesh, read, delete decoded data.
 		///
 		/// Remark. Encoding all data at once is good in world with computers which do not has RAM limitation. So, you must use step by step encoding in

+ 9 - 194
code/glTF2/glTF2Asset.inl

@@ -282,9 +282,7 @@ Ref<T> LazyDict<T>::Retrieve(unsigned int i)
 template<class T>
 Ref<T> LazyDict<T>::Get(unsigned int i)
 {
-
     return Ref<T>(mObjs, i);
-
 }
 
 template<class T>
@@ -361,11 +359,11 @@ inline void Buffer::Read(Value& obj, Asset& r)
 
     const char* uri = it->GetString();
 
-    Util::DataURI dataURI;
+    glTFCommon::Util::DataURI dataURI;
     if (ParseDataURI(uri, it->GetStringLength(), dataURI)) {
         if (dataURI.base64) {
             uint8_t* data = 0;
-            this->byteLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, data);
+            this->byteLength = glTFCommon::Util::DecodeBase64(dataURI.data, dataURI.dataLength, data);
             this->mData.reset(data, std::default_delete<uint8_t[]>());
 
             if (statedLength > 0 && this->byteLength != statedLength) {
@@ -717,12 +715,12 @@ inline void Image::Read(Value& obj, Asset& r)
         if (Value* uri = FindString(obj, "uri")) {
             const char* uristr = uri->GetString();
 
-            Util::DataURI dataURI;
+            glTFCommon::Util::DataURI dataURI;
             if (ParseDataURI(uristr, uri->GetStringLength(), dataURI)) {
                 mimeType = dataURI.mediaType;
                 if (dataURI.base64) {
                     uint8_t *ptr = nullptr;
-                    mDataLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, ptr);
+                    mDataLength = glTFCommon::Util::DecodeBase64(dataURI.data, dataURI.dataLength, ptr);
                     mData.reset(ptr);
                 }
             }
@@ -1100,8 +1098,11 @@ inline void Light::Read(Value& obj, Asset& /*r*/)
     }
 }
 
-inline void Node::Read(Value& obj, Asset& r)
-{
+inline 
+void Node::Read(Value& obj, Asset& r) {
+    if (name.empty()) {
+        name = id;
+    }
 
     if (Value* children = FindArray(obj, "children")) {
         this->children.reserve(children->Size());
@@ -1515,190 +1516,4 @@ inline std::string Asset::FindUniqueID(const std::string& str, const char* suffi
     return id;
 }
 
-namespace Util {
-
-    inline
-    bool ParseDataURI(const char* const_uri, size_t uriLen, DataURI& out) {
-        if ( NULL == const_uri ) {
-            return false;
-        }
-
-        if (const_uri[0] != 0x10) { // we already parsed this uri?
-            if (strncmp(const_uri, "data:", 5) != 0) // not a data uri?
-                return false;
-        }
-
-        // set defaults
-        out.mediaType = "text/plain";
-        out.charset = "US-ASCII";
-        out.base64 = false;
-
-        char* uri = const_cast<char*>(const_uri);
-        if (uri[0] != 0x10) {
-            uri[0] = 0x10;
-            uri[1] = uri[2] = uri[3] = uri[4] = 0;
-
-            size_t i = 5, j;
-            if (uri[i] != ';' && uri[i] != ',') { // has media type?
-                uri[1] = char(i);
-                for (; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {
-                    // nothing to do!
-                }
-            }
-            while (uri[i] == ';' && i < uriLen) {
-                uri[i++] = '\0';
-                for (j = i; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {
-                    // nothing to do!
-                }
-
-                if ( strncmp( uri + j, "charset=", 8 ) == 0 ) {
-                    uri[2] = char(j + 8);
-                } else if ( strncmp( uri + j, "base64", 6 ) == 0 ) {
-                    uri[3] = char(j);
-                }
-            }
-            if (i < uriLen) {
-                uri[i++] = '\0';
-                uri[4] = char(i);
-            } else {
-                uri[1] = uri[2] = uri[3] = 0;
-                uri[4] = 5;
-            }
-        }
-
-        if ( uri[ 1 ] != 0 ) {
-            out.mediaType = uri + uri[ 1 ];
-        }
-        if ( uri[ 2 ] != 0 ) {
-            out.charset = uri + uri[ 2 ];
-        }
-        if ( uri[ 3 ] != 0 ) {
-            out.base64 = true;
-        }
-        out.data = uri + uri[4];
-        out.dataLength = (uri + uriLen) - out.data;
-
-        return true;
-    }
-
-    template<bool B>
-    struct DATA
-    {
-        static const uint8_t tableDecodeBase64[128];
-    };
-
-    template<bool B>
-    const uint8_t DATA<B>::tableDecodeBase64[128] = {
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62,  0,  0,  0, 63,
-        52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  0,  0,  0, 64,  0,  0,
-         0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
-         0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
-        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  0,  0,  0,  0,  0
-    };
-
-    inline char EncodeCharBase64(uint8_t b)
-    {
-        return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="[size_t(b)];
-    }
-
-    inline uint8_t DecodeCharBase64(char c)
-    {
-        return DATA<true>::tableDecodeBase64[size_t(c)]; // TODO faster with lookup table or ifs?
-        /*if (c >= 'A' && c <= 'Z') return c - 'A';
-        if (c >= 'a' && c <= 'z') return c - 'a' + 26;
-        if (c >= '0' && c <= '9') return c - '0' + 52;
-        if (c == '+') return 62;
-        if (c == '/') return 63;
-        return 64; // '-' */
-    }
-
-    inline size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out)
-    {
-        ai_assert(inLength % 4 == 0);
-
-        if (inLength < 4) {
-            out = 0;
-            return 0;
-        }
-
-        int nEquals = int(in[inLength - 1] == '=') +
-                      int(in[inLength - 2] == '=');
-
-        size_t outLength = (inLength * 3) / 4 - nEquals;
-        out = new uint8_t[outLength];
-        memset(out, 0, outLength);
-
-        size_t i, j = 0;
-
-        for (i = 0; i + 4 < inLength; i += 4) {
-            uint8_t b0 = DecodeCharBase64(in[i]);
-            uint8_t b1 = DecodeCharBase64(in[i + 1]);
-            uint8_t b2 = DecodeCharBase64(in[i + 2]);
-            uint8_t b3 = DecodeCharBase64(in[i + 3]);
-
-            out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
-            out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
-            out[j++] = (uint8_t)((b2 << 6) | b3);
-        }
-
-        {
-            uint8_t b0 = DecodeCharBase64(in[i]);
-            uint8_t b1 = DecodeCharBase64(in[i + 1]);
-            uint8_t b2 = DecodeCharBase64(in[i + 2]);
-            uint8_t b3 = DecodeCharBase64(in[i + 3]);
-
-            out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
-            if (b2 < 64) out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
-            if (b3 < 64) out[j++] = (uint8_t)((b2 << 6) | b3);
-        }
-
-        return outLength;
-    }
-
-
-
-    inline void EncodeBase64(
-        const uint8_t* in, size_t inLength,
-        std::string& out)
-    {
-        size_t outLength = ((inLength + 2) / 3) * 4;
-
-        size_t j = out.size();
-        out.resize(j + outLength);
-
-        for (size_t i = 0; i <  inLength; i += 3) {
-            uint8_t b = (in[i] & 0xFC) >> 2;
-            out[j++] = EncodeCharBase64(b);
-
-            b = (in[i] & 0x03) << 4;
-            if (i + 1 < inLength) {
-                b |= (in[i + 1] & 0xF0) >> 4;
-                out[j++] = EncodeCharBase64(b);
-
-                b = (in[i + 1] & 0x0F) << 2;
-                if (i + 2 < inLength) {
-                    b |= (in[i + 2] & 0xC0) >> 6;
-                    out[j++] = EncodeCharBase64(b);
-
-                    b = in[i + 2] & 0x3F;
-                    out[j++] = EncodeCharBase64(b);
-                }
-                else {
-                    out[j++] = EncodeCharBase64(b);
-                    out[j++] = '=';
-                }
-            }
-            else {
-                out[j++] = EncodeCharBase64(b);
-                out[j++] = '=';
-                out[j++] = '=';
-            }
-        }
-    }
-
-}
-
 } // ns glTF

+ 1 - 1
code/glTF2/glTF2AssetWriter.inl

@@ -218,7 +218,7 @@ namespace glTF2 {
             if (img.HasData()) {
                 uri = "data:" + (img.mimeType.empty() ? "application/octet-stream" : img.mimeType);
                 uri += ";base64,";
-                Util::EncodeBase64(img.GetData(), img.GetDataLength(), uri);
+                glTFCommon::Util::EncodeBase64(img.GetData(), img.GetDataLength(), uri);
             }
             else {
                 uri = img.uri;

+ 42 - 17
code/glTF2/glTF2Importer.cpp

@@ -64,6 +64,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 using namespace Assimp;
 using namespace glTF2;
+using namespace glTFCommon;
 
 namespace {
     // generate bi-tangents from normals and tangents according to spec
@@ -140,22 +141,23 @@ static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode)
     }
 }
 
-static void CopyValue(const glTF2::vec3& v, aiColor3D& out)
+/*static void CopyValue(const glTF2::vec3& v, aiColor3D& out)
 {
     out.r = v[0]; out.g = v[1]; out.b = v[2];
 }
 
+
 static void CopyValue(const glTF2::vec4& v, aiColor4D& out)
 {
     out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = v[3];
-}
+}*/
 
 /*static void CopyValue(const glTF2::vec4& v, aiColor3D& out)
 {
     out.r = v[0]; out.g = v[1]; out.b = v[2];
 }*/
 
-static void CopyValue(const glTF2::vec3& v, aiColor4D& out)
+/*static void CopyValue(const glTF2::vec3& v, aiColor4D& out)
 {
     out.r = v[0]; out.g = v[1]; out.b = v[2]; out.a = 1.0;
 }
@@ -168,15 +170,15 @@ static void CopyValue(const glTF2::vec3& v, aiVector3D& out)
 static void CopyValue(const glTF2::vec4& v, aiQuaternion& out)
 {
     out.x = v[0]; out.y = v[1]; out.z = v[2]; out.w = v[3];
-}
+}*/
 
-static void CopyValue(const glTF2::mat4& v, aiMatrix4x4& o)
+/*static void CopyValue(const glTF2::mat4& v, aiMatrix4x4& o)
 {
     o.a1 = v[ 0]; o.b1 = v[ 1]; o.c1 = v[ 2]; o.d1 = v[ 3];
     o.a2 = v[ 4]; o.b2 = v[ 5]; o.c2 = v[ 6]; o.d2 = v[ 7];
     o.a3 = v[ 8]; o.b3 = v[ 9]; o.c3 = v[10]; o.d3 = v[11];
     o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15];
-}
+}*/
 
 inline void SetMaterialColorProperty(Asset& /*r*/, vec4& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx)
 {
@@ -188,7 +190,7 @@ inline void SetMaterialColorProperty(Asset& /*r*/, vec4& prop, aiMaterial* mat,
 inline void SetMaterialColorProperty(Asset& /*r*/, vec3& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx)
 {
     aiColor4D col;
-    CopyValue(prop, col);
+    glTFCommon::CopyValue(prop, col);
     mat->AddProperty(&col, 1, pKey, type, idx);
 }
 
@@ -383,7 +385,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
             aim->mName = mesh.name.empty() ? mesh.id : mesh.name;
 
             if (mesh.primitives.size() > 1) {
-                size_t& len = aim->mName.length;
+                ai_uint32& len = aim->mName.length;
                 aim->mName.data[len] = '-';
                 len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p);
             }
@@ -442,7 +444,6 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
                         "\" does not match the vertex count");
                     continue;
                 }
-                aim->mColors[c] = new aiColor4D[attr.color[c]->count];
                 attr.color[c]->ExtractData(aim->mColors[c]);
             }
             for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) {
@@ -700,12 +701,17 @@ void glTF2Importer::ImportCameras(glTF2::Asset& r)
         if (cam.type == Camera::Perspective) {
 
             aicam->mAspect        = cam.cameraProperties.perspective.aspectRatio;
-            aicam->mHorizontalFOV = cam.cameraProperties.perspective.yfov * aicam->mAspect;
+            aicam->mHorizontalFOV = cam.cameraProperties.perspective.yfov * ((aicam->mAspect == 0.f) ? 1.f : aicam->mAspect);
             aicam->mClipPlaneFar  = cam.cameraProperties.perspective.zfar;
             aicam->mClipPlaneNear = cam.cameraProperties.perspective.znear;
-        }
-        else {
-            // assimp does not support orthographic cameras
+        } else {
+            aicam->mClipPlaneFar = cam.cameraProperties.ortographic.zfar;
+            aicam->mClipPlaneNear = cam.cameraProperties.ortographic.znear;
+            aicam->mHorizontalFOV = 0.0;
+            aicam->mAspect = 1.0f;
+            if (0.f != cam.cameraProperties.ortographic.ymag ) {
+                aicam->mAspect = cam.cameraProperties.ortographic.xmag / cam.cameraProperties.ortographic.ymag;
+            }
         }
     }
 }
@@ -901,6 +907,9 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
                 std::vector<std::vector<aiVertexWeight>> weighting(mesh->mNumBones);
                 BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting);
 
+                mat4* pbindMatrices = nullptr;
+                node.skin->inverseBindMatrices->ExtractData(pbindMatrices);
+
                 for (uint32_t i = 0; i < mesh->mNumBones; ++i) {
                     aiBone* bone = new aiBone();
 
@@ -916,6 +925,8 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
                     }
                     GetNodeTransform(bone->mOffsetMatrix, *joint);
 
+                    CopyValue(pbindMatrices[i], bone->mOffsetMatrix);
+
                     std::vector<aiVertexWeight>& weights = weighting[i];
 
                     bone->mNumWeights = static_cast<uint32_t>(weights.size());
@@ -931,6 +942,10 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
                     }
                     mesh->mBones[i] = bone;
                 }
+
+                if (pbindMatrices) {
+                    delete[] pbindMatrices;
+                }
             }
         }
 
@@ -987,7 +1002,12 @@ void glTF2Importer::ImportNodes(glTF2::Asset& r)
 }
 
 struct AnimationSamplers {
-    AnimationSamplers() : translation(nullptr), rotation(nullptr), scale(nullptr) {}
+    AnimationSamplers()
+    : translation(nullptr)
+    , rotation(nullptr)
+    , scale(nullptr) {
+        // empty
+    }
 
     Animation::Sampler* translation;
     Animation::Sampler* rotation;
@@ -1016,7 +1036,7 @@ aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& sampl
         delete[] values;
     } else if (node.translation.isPresent) {
         anim->mNumPositionKeys = 1;
-        anim->mPositionKeys = new aiVectorKey();
+        anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
         anim->mPositionKeys->mTime = 0.f;
         anim->mPositionKeys->mValue.x = node.translation.value[0];
         anim->mPositionKeys->mValue.y = node.translation.value[1];
@@ -1041,7 +1061,7 @@ aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& sampl
         delete[] values;
     } else if (node.rotation.isPresent) {
         anim->mNumRotationKeys = 1;
-        anim->mRotationKeys = new aiQuatKey();
+        anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
         anim->mRotationKeys->mTime = 0.f;
         anim->mRotationKeys->mValue.x = node.rotation.value[0];
         anim->mRotationKeys->mValue.y = node.rotation.value[1];
@@ -1064,7 +1084,7 @@ aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& sampl
         delete[] values;
     } else if (node.scale.isPresent) {
         anim->mNumScalingKeys = 1;
-        anim->mScalingKeys = new aiVectorKey();
+        anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
         anim->mScalingKeys->mTime = 0.f;
         anim->mScalingKeys->mValue.x = node.scale.value[0];
         anim->mScalingKeys->mValue.y = node.scale.value[1];
@@ -1130,6 +1150,7 @@ void glTF2Importer::ImportAnimations(glTF2::Asset& r)
 
         // Use the latest keyframe for the duration of the animation
         double maxDuration = 0;
+        unsigned int maxNumberOfKeys = 0;
         for (unsigned int j = 0; j < ai_anim->mNumChannels; ++j) {
             auto chan = ai_anim->mChannels[j];
             if (chan->mNumPositionKeys) {
@@ -1137,21 +1158,25 @@ void glTF2Importer::ImportAnimations(glTF2::Asset& r)
                 if (lastPosKey.mTime > maxDuration) {
                     maxDuration = lastPosKey.mTime;
                 }
+                maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumPositionKeys);
             }
             if (chan->mNumRotationKeys) {
                 auto lastRotKey = chan->mRotationKeys[chan->mNumRotationKeys - 1];
                 if (lastRotKey.mTime > maxDuration) {
                     maxDuration = lastRotKey.mTime;
                 }
+                maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumRotationKeys);
             }
             if (chan->mNumScalingKeys) {
                 auto lastScaleKey = chan->mScalingKeys[chan->mNumScalingKeys - 1];
                 if (lastScaleKey.mTime > maxDuration) {
                     maxDuration = lastScaleKey.mTime;
                 }
+                maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumScalingKeys);
             }
         }
         ai_anim->mDuration = maxDuration;
+        ai_anim->mTicksPerSecond = (maxNumberOfKeys > 0 && maxDuration > 0) ? (maxNumberOfKeys / (maxDuration/1000)) : 30;
 
         mScene->mAnimations[i] = ai_anim;
     }

+ 2 - 2
code/res/assimp.rc

@@ -52,8 +52,8 @@ BEGIN
             VALUE "FileDescription", "Open Asset Import Library"
             VALUE "FileVersion", VER_FILEVERSION
             VALUE "InternalName", "assimp "
-            VALUE "LegalCopyright", "Copyright (C) 2006-2010"
-            VALUE "OriginalFilename", "assimpNN.dll"
+            VALUE "LegalCopyright", "Copyright (C) 2006-2019"
+            VALUE "OriginalFilename", VER_ORIGINAL_FILENAME_STR
             VALUE "ProductName", "Open Asset Import Library"
             VALUE "ProductVersion", VER_FILEVERSION_STR
 		,0

+ 4 - 4
contrib/gtest/test/gtest-param-test_test.cc

@@ -141,7 +141,7 @@ void VerifyGenerator(const ParamGenerator<T>& generator,
         << ", expected_values[i] is " << PrintValue(expected_values[i])
         << ", *it is " << PrintValue(*it)
         << ", and 'it' is an iterator created with the copy constructor.\n";
-    it++;
+    ++it;
   }
   EXPECT_TRUE(it == generator.end())
         << "At the presumed end of sequence when accessing via an iterator "
@@ -161,7 +161,7 @@ void VerifyGenerator(const ParamGenerator<T>& generator,
         << ", expected_values[i] is " << PrintValue(expected_values[i])
         << ", *it is " << PrintValue(*it)
         << ", and 'it' is an iterator created with the copy constructor.\n";
-    it++;
+    ++it;
   }
   EXPECT_TRUE(it == generator.end())
         << "At the presumed end of sequence when accessing via an iterator "
@@ -196,7 +196,7 @@ TEST(IteratorTest, ParamIteratorConformsToForwardIteratorConcept) {
                            << "element same as its source points to";
 
   // Verifies that iterator assignment works as expected.
-  it++;
+  ++it;
   EXPECT_FALSE(*it == *it2);
   it2 = it;
   EXPECT_TRUE(*it == *it2) << "Assigned iterators must point to the "
@@ -215,7 +215,7 @@ TEST(IteratorTest, ParamIteratorConformsToForwardIteratorConcept) {
   // Verifies that prefix and postfix operator++() advance an iterator
   // all the same.
   it2 = it;
-  it++;
+  ++it;
   ++it2;
   EXPECT_TRUE(*it == *it2);
 }

+ 1 - 1
contrib/irrXML/irrXML.h

@@ -215,7 +215,7 @@ namespace io
 	two methods to read your data and give a pointer to an instance of
 	your implementation when calling createIrrXMLReader(), 
 	createIrrXMLReaderUTF16() or createIrrXMLReaderUTF32() */
-	class IFileReadCallBack
+	class IRRXML_API IFileReadCallBack
 	{
 	public:
 

+ 59 - 2
include/assimp/BaseImporter.h

@@ -48,8 +48,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <vector>
 #include <set>
+#include <map>
 #include <assimp/types.h>
 #include <assimp/ProgressHandler.hpp>
+#include <assimp/ai_assert.h>
 
 struct aiScene;
 struct aiImporterDesc;
@@ -80,6 +82,10 @@ class IOStream;
 class ASSIMP_API BaseImporter {
     friend class Importer;
 
+private:
+    /* Pushes state into importer for the importer scale */
+    virtual void UpdateImporterScale( Importer* pImp );
+
 public:
 
     /** Constructor to be privately used by #Importer */
@@ -132,7 +138,7 @@ public:
      *  a suitable response to the caller.
      */
     aiScene* ReadFile(
-        const Importer* pImp,
+        Importer* pImp,
         const std::string& pFile,
         IOSystem* pIOHandler
         );
@@ -161,14 +167,65 @@ public:
      *  some loader features. Importers must provide this information. */
     virtual const aiImporterDesc* GetInfo() const = 0;
 
+    /**
+     * Will be called only by scale process when scaling is requested.
+     */
+    virtual void SetFileScale(double scale)
+    {
+        fileScale = scale;
+    }
+
+    virtual double GetFileScale() const
+    {
+        return fileScale;
+    }
+
+    enum ImporterUnits {
+        M,
+        MM,
+        CM,
+        INCHES,
+        FEET
+    };
+
+    /**
+     * Assimp Importer
+     * unit conversions available 
+     * if you need another measurment unit add it below.
+     * it's currently defined in assimp that we prefer meters.
+     * */
+    std::map<ImporterUnits, double> importerUnits = {
+        {ImporterUnits::M, 1},
+        {ImporterUnits::CM, 0.01},
+        {ImporterUnits::MM, 0.001},
+        {ImporterUnits::INCHES, 0.0254},
+        {ImporterUnits::FEET, 0.3048}
+    };
+
+    virtual void SetApplicationUnits( const ImporterUnits& unit )
+    {
+        importerScale = importerUnits[unit];
+        applicationUnits = unit;
+    }
+
+    virtual const ImporterUnits& GetApplicationUnits()
+    {
+        return applicationUnits;
+    }
+
     // -------------------------------------------------------------------
     /** Called by #Importer::GetExtensionList for each loaded importer.
      *  Take the extension list contained in the structure returned by
      *  #GetInfo and insert all file extensions into the given set.
      *  @param extension set to collect file extensions in*/
     void GetExtensionList(std::set<std::string>& extensions);
+    
+protected:    
+    ImporterUnits applicationUnits = ImporterUnits::M;
+    double importerScale = 1.0;
+    double fileScale = 1.0;
+
 
-protected:
 
     // -------------------------------------------------------------------
     /** Imports the given file into the given scene structure. The

+ 19 - 10
include/assimp/MathFunctions.h

@@ -39,22 +39,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ---------------------------------------------------------------------------
 */
 
+#pragma once
+
 /** @file  MathFunctions.h
- *  @brief Implementation of the math functions (gcd and lcm)
+*  @brief Implementation of math utility functions.
  *
- *  Copied from BoostWorkaround/math
- */
+*/
+
+#include <limits>
 
 namespace Assimp {
 namespace Math {
 
 // TODO: use binary GCD for unsigned integers ....
 template < typename IntegerType >
-IntegerType  gcd( IntegerType a, IntegerType b )
-{
+inline
+IntegerType gcd( IntegerType a, IntegerType b ) {
 	const IntegerType zero = (IntegerType)0;
-	while ( true )
-	{
+	while ( true ) {
 		if ( a == zero )
 			return b;
 		b %= a;
@@ -66,12 +68,19 @@ IntegerType  gcd( IntegerType a, IntegerType b )
 }
 
 template < typename IntegerType >
-IntegerType  lcm( IntegerType a, IntegerType b )
-{
+inline
+IntegerType lcm( IntegerType a, IntegerType b ) {
 	const IntegerType t = gcd (a,b);
-	if (!t)return t;
+	if (!t)
+        return t;
 	return a / t * b;
 }
 
+template<class T>
+inline
+T getEpsilon() {
+    return std::numeric_limits<T>::epsilon();
+}
+
 }
 }

+ 2 - 0
include/assimp/SceneCombiner.h

@@ -65,6 +65,7 @@ struct aiLight;
 struct aiMetadata;
 struct aiBone;
 struct aiMesh;
+struct aiAnimMesh;
 struct aiAnimation;
 struct aiNodeAnim;
 
@@ -363,6 +364,7 @@ public:
     static void Copy     (aiMesh** dest, const aiMesh* src);
 
     // similar to Copy():
+    static void Copy  (aiAnimMesh** dest, const aiAnimMesh* src);
     static void Copy  (aiMaterial** dest, const aiMaterial* src);
     static void Copy  (aiTexture** dest, const aiTexture* src);
     static void Copy  (aiAnimation** dest, const aiAnimation* src);

+ 87 - 0
include/assimp/ZipArchiveIOSystem.h

@@ -0,0 +1,87 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file ZipArchiveIOSystem.h
+ *  @brief Implementation of IOSystem to read a ZIP file from another IOSystem
+*/
+
+#ifndef AI_ZIPARCHIVEIOSYSTEM_H_INC
+#define AI_ZIPARCHIVEIOSYSTEM_H_INC
+
+#include <assimp/IOStream.hpp>
+#include <assimp/IOSystem.hpp>
+
+namespace Assimp {
+    class ZipArchiveIOSystem : public IOSystem {
+    public:
+        //! Open a Zip using the proffered IOSystem
+        ZipArchiveIOSystem(IOSystem* pIOHandler, const char *pFilename, const char* pMode = "r");
+        ZipArchiveIOSystem(IOSystem* pIOHandler, const std::string& rFilename, const char* pMode = "r");
+        virtual ~ZipArchiveIOSystem();
+        bool Exists(const char* pFilename) const override;
+        char getOsSeparator() const override;
+        IOStream* Open(const char* pFilename, const char* pMode = "rb") override;
+        void Close(IOStream* pFile) override;
+
+        // Specific to ZIP
+        //! The file was opened and is a ZIP
+        bool isOpen() const;
+
+        //! Get the list of all files with their simplified paths
+        //! Intended for use within Assimp library boundaries
+        void getFileList(std::vector<std::string>& rFileList) const;
+
+        //! Get the list of all files with extension (must be lowercase)
+        //! Intended for use within Assimp library boundaries
+        void getFileListExtension(std::vector<std::string>& rFileList, const std::string& extension) const;
+
+        static bool isZipArchive(IOSystem* pIOHandler, const char *pFilename);
+        static bool isZipArchive(IOSystem* pIOHandler, const std::string& rFilename);
+
+    private:
+        class Implement;
+        Implement *pImpl = nullptr;
+    };
+} // Namespace Assimp
+
+#endif // AI_ZIPARCHIVEIOSYSTEM_H_INC

+ 0 - 2
include/assimp/camera.h

@@ -113,7 +113,6 @@ struct aiCamera
      */
     C_STRUCT aiVector3D mPosition;
 
-
     /** 'Up' - vector of the camera coordinate system relative to
      *  the coordinate space defined by the corresponding node.
      *
@@ -134,7 +133,6 @@ struct aiCamera
      */
     C_STRUCT aiVector3D mLookAt;
 
-
     /** Half horizontal field of view angle, in radians.
      *
      *  The field of view angle is the angle between the center

+ 7 - 0
include/assimp/config.h.in

@@ -999,6 +999,13 @@ enum aiComponent
 #   define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT  1.0f
 #endif // !! AI_DEBONE_THRESHOLD
 
+#define AI_CONFIG_APP_SCALE_KEY "APP_SCALE_FACTOR"
+
+#if (!defined AI_CONFIG_APP_SCALE_KEY)
+#   define AI_CONFIG_APP_SCALE_KEY 1.0
+#endif // AI_CONFIG_APP_SCALE_KEY
+
+
 // ---------- All the Build/Compile-time defines ------------
 
 /** @brief Specifies if double precision is supported inside assimp

+ 33 - 13
include/assimp/material.h

@@ -196,34 +196,40 @@ enum aiTextureType
      *  (#aiMaterialProperty::mSemantic) for all material properties
      *  *not* related to textures.
      */
-    aiTextureType_NONE = 0x0,
+    aiTextureType_NONE = 0,
+
+    /** LEGACY API MATERIALS 
+     * Legacy refers to materials which 
+     * Were originally implemented in the specifications around 2000.
+     * These must never be removed, as most engines support them.
+     */
 
     /** The texture is combined with the result of the diffuse
      *  lighting equation.
      */
-    aiTextureType_DIFFUSE = 0x1,
+    aiTextureType_DIFFUSE = 1,
 
     /** The texture is combined with the result of the specular
      *  lighting equation.
      */
-    aiTextureType_SPECULAR = 0x2,
+    aiTextureType_SPECULAR = 2,
 
     /** The texture is combined with the result of the ambient
      *  lighting equation.
      */
-    aiTextureType_AMBIENT = 0x3,
+    aiTextureType_AMBIENT = 3,
 
     /** The texture is added to the result of the lighting
      *  calculation. It isn't influenced by incoming light.
      */
-    aiTextureType_EMISSIVE = 0x4,
+    aiTextureType_EMISSIVE = 4,
 
     /** The texture is a height map.
      *
      *  By convention, higher gray-scale values stand for
      *  higher elevations from the base height.
      */
-    aiTextureType_HEIGHT = 0x5,
+    aiTextureType_HEIGHT = 5,
 
     /** The texture is a (tangent space) normal-map.
      *
@@ -231,7 +237,7 @@ enum aiTextureType
      *  normal maps. Assimp does (intentionally) not
      *  distinguish here.
      */
-    aiTextureType_NORMALS = 0x6,
+    aiTextureType_NORMALS = 6,
 
     /** The texture defines the glossiness of the material.
      *
@@ -240,21 +246,21 @@ enum aiTextureType
      *  function defined to map the linear color values in the
      *  texture to a suitable exponent. Have fun.
     */
-    aiTextureType_SHININESS = 0x7,
+    aiTextureType_SHININESS = 7,
 
     /** The texture defines per-pixel opacity.
      *
      *  Usually 'white' means opaque and 'black' means
      *  'transparency'. Or quite the opposite. Have fun.
     */
-    aiTextureType_OPACITY = 0x8,
+    aiTextureType_OPACITY = 8,
 
     /** Displacement texture
      *
      *  The exact purpose and format is application-dependent.
      *  Higher color values stand for higher vertex displacements.
     */
-    aiTextureType_DISPLACEMENT = 0x9,
+    aiTextureType_DISPLACEMENT = 9,
 
     /** Lightmap texture (aka Ambient Occlusion)
      *
@@ -263,14 +269,28 @@ enum aiTextureType
      *  scaling value for the final color value of a pixel. Its
      *  intensity is not affected by incoming light.
     */
-    aiTextureType_LIGHTMAP = 0xA,
+    aiTextureType_LIGHTMAP = 10,
 
     /** Reflection texture
      *
      * Contains the color of a perfect mirror reflection.
      * Rarely used, almost never for real-time applications.
     */
-    aiTextureType_REFLECTION = 0xB,
+    aiTextureType_REFLECTION = 11,
+
+    /** PBR Materials
+     * PBR definitions from maya and other modelling packages now use this standard.
+     * This was originally introduced around 2012.
+     * Support for this is in game engines like Godot, Unreal or Unity3D.
+     * Modelling packages which use this are very common now.
+     */
+
+    aiTextureType_BASE_COLOR = 12,
+    aiTextureType_NORMAL_CAMERA = 13,
+    aiTextureType_EMISSION_COLOR = 14,
+    aiTextureType_METALNESS = 15,
+    aiTextureType_DIFFUSE_ROUGHNESS = 16,
+    aiTextureType_AMBIENT_OCCLUSION = 17,
 
     /** Unknown texture
      *
@@ -278,7 +298,7 @@ enum aiTextureType
      *  above is considered to be 'unknown'. It is still imported,
      *  but is excluded from any further post-processing.
     */
-    aiTextureType_UNKNOWN = 0xC,
+    aiTextureType_UNKNOWN = 18,
 
 
 #ifndef SWIG

+ 4 - 5
include/assimp/matrix4x4.inl

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2019, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -53,6 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "matrix4x4.h"
 #include "matrix3x3.h"
 #include "quaternion.h"
+#include "MathFunctions.h"
 
 #include <algorithm>
 #include <limits>
@@ -420,8 +419,8 @@ inline void aiMatrix4x4t<TReal>::Decompose (aiVector3t<TReal>& pScaling, aiQuate
 }
 
 template <typename TReal>
-inline void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const
-{
+inline
+void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const {
 	ASSIMP_MATRIX4_4_DECOMPOSE_PART;
 
     /*
@@ -442,7 +441,7 @@ inline void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector
 	*/
 
 	// Use a small epsilon to solve floating-point inaccuracies
-    const TReal epsilon = 10e-3f;
+    const TReal epsilon = Assimp::Math::getEpsilon<TReal>();
 
 	pRotation.y  = std::asin(-vCols[0].z);// D. Angle around oY.
 

+ 1 - 0
include/assimp/scene.h

@@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "metadata.h"
 
 #ifdef __cplusplus
+#  include <cstdlib>
 extern "C" {
 #endif
 

+ 13 - 8
include/assimp/types.h

@@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <stddef.h>
 #include <string.h>
 #include <limits.h>
+#include <stdint.h>
 
 // Our compile configuration
 #include "defs.h"
@@ -65,11 +66,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "matrix4x4.h"
 #include "quaternion.h"
 
+typedef int32_t ai_int32;
+typedef uint32_t ai_uint32 ;
+
 #ifdef __cplusplus
 #include <cstring>
 #include <new>      // for std::nothrow_t
 #include <string>   // for aiString::Set(const std::string&)
 
+
 namespace Assimp    {
     //! @cond never
 namespace Intern        {
@@ -269,8 +274,8 @@ struct aiString
     }
 
     /** Copy constructor */
-    aiString(const aiString& rOther) :
-        length(rOther.length)
+    aiString(const aiString& rOther)
+    : length(rOther.length)
     {
         // Crop the string to the maximum length
         length = length>=MAXLEN?MAXLEN-1:length;
@@ -280,7 +285,7 @@ struct aiString
 
     /** Constructor from std::string */
     explicit aiString(const std::string& pString) :
-        length(pString.length())
+        length( (ai_uint32) pString.length())
     {
         length = length>=MAXLEN?MAXLEN-1:length;
         memcpy( data, pString.c_str(), length);
@@ -292,15 +297,15 @@ struct aiString
         if( pString.length() > MAXLEN - 1) {
             return;
         }
-        length = pString.length();
+        length = (ai_uint32)pString.length();
         memcpy( data, pString.c_str(), length);
         data[length] = 0;
     }
 
     /** Copy a const char* to the aiString */
     void Set( const char* sz) {
-        const size_t len = ::strlen(sz);
-        if( len > MAXLEN - 1) {
+        const ai_int32 len = (ai_uint32) ::strlen(sz);
+        if( len > (ai_int32)MAXLEN - 1) {
             return;
         }
         length = len;
@@ -346,7 +351,7 @@ struct aiString
 
     /** Append a string to the string */
     void Append (const char* app)   {
-        const size_t len = ::strlen(app);
+        const ai_uint32 len = (ai_uint32) ::strlen(app);
         if (!len) {
             return;
         }
@@ -379,7 +384,7 @@ struct aiString
     /** Binary length of the string excluding the terminal 0. This is NOT the
      *  logical length of strings containing UTF-8 multi-byte sequences! It's
      *  the number of bytes from the beginning of the string to its end.*/
-    size_t length;
+    ai_uint32 length;
 
     /** String buffer. Size limit is MAXLEN */
     char data[MAXLEN];

+ 2 - 2
port/PyAssimp/pyassimp/helper.py

@@ -274,8 +274,8 @@ def hasattr_silent(object, name):
     """
 
     try:
-	    if not object:
-		    return False
+        if not object:
+            return False
         return hasattr(object, name)
     except AttributeError:
         return False

+ 10 - 0
revision.h.in

@@ -13,6 +13,16 @@
 #define STR(x) STR_HELP(x)
 
 #define VER_FILEVERSION             VER_MAJOR,VER_MINOR,VER_PATCH,VER_BUILD
+#if (GitVersion == 0)
 #define VER_FILEVERSION_STR         STR(VER_MAJOR) "." STR(VER_MINOR) "." STR(VER_PATCH) "." STR(VER_BUILD)
+#else
+#define VER_FILEVERSION_STR         STR(VER_MAJOR) "." STR(VER_MINOR) "." STR(VER_PATCH) "." STR(VER_BUILD) " (Commit @GIT_COMMIT_HASH@)"
+#endif
+
+#ifdef  NDEBUG
+#define VER_ORIGINAL_FILENAME_STR   "assimp@[email protected]"
+#else
+#define VER_ORIGINAL_FILENAME_STR   "assimp@LIBRARY_SUFFIX@@[email protected]"
+#endif //  NDEBUG
 
 #endif // ASSIMP_REVISION_H_INC

+ 1 - 0
samples/SimpleOpenGL/CMakeLists.txt

@@ -25,6 +25,7 @@ INCLUDE_DIRECTORIES(
   ${Assimp_SOURCE_DIR}/include
   ${Assimp_SOURCE_DIR}/code
   ${OPENGL_INCLUDE_DIR}
+  ${GLUT_INCLUDE_DIR}
   ${Assimp_SOURCE_DIR}/samples/freeglut/include
 )
 

+ 22 - 22
samples/SimpleOpenGL/Sample_SimpleOpenGL.c

@@ -26,9 +26,9 @@
 #include <assimp/postprocess.h>
 
 /* the global Assimp scene object */
-const struct aiScene* scene = NULL;
+const C_STRUCT aiScene* scene = NULL;
 GLuint scene_list = 0;
-struct aiVector3D scene_min, scene_max, scene_center;
+C_STRUCT aiVector3D scene_min, scene_max, scene_center;
 
 /* current rotation angle */
 static float angle = 0.f;
@@ -49,22 +49,22 @@ void reshape(int width, int height)
 }
 
 /* ---------------------------------------------------------------------------- */
-void get_bounding_box_for_node (const struct aiNode* nd,
-	struct aiVector3D* min,
-	struct aiVector3D* max,
-	struct aiMatrix4x4* trafo
+void get_bounding_box_for_node (const C_STRUCT aiNode* nd,
+	C_STRUCT aiVector3D* min,
+	C_STRUCT aiVector3D* max,
+	C_STRUCT aiMatrix4x4* trafo
 ){
-	struct aiMatrix4x4 prev;
+	C_STRUCT aiMatrix4x4 prev;
 	unsigned int n = 0, t;
 
 	prev = *trafo;
 	aiMultiplyMatrix4(trafo,&nd->mTransformation);
 
 	for (; n < nd->mNumMeshes; ++n) {
-		const struct aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]];
+		const C_STRUCT aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]];
 		for (t = 0; t < mesh->mNumVertices; ++t) {
 
-			struct aiVector3D tmp = mesh->mVertices[t];
+			C_STRUCT aiVector3D tmp = mesh->mVertices[t];
 			aiTransformVecByMatrix4(&tmp,trafo);
 
 			min->x = aisgl_min(min->x,tmp.x);
@@ -84,9 +84,9 @@ void get_bounding_box_for_node (const struct aiNode* nd,
 }
 
 /* ---------------------------------------------------------------------------- */
-void get_bounding_box (struct aiVector3D* min, struct aiVector3D* max)
+void get_bounding_box(C_STRUCT aiVector3D* min, C_STRUCT aiVector3D* max)
 {
-	struct aiMatrix4x4 trafo;
+	C_STRUCT aiMatrix4x4 trafo;
 	aiIdentityMatrix4(&trafo);
 
 	min->x = min->y = min->z =  1e10f;
@@ -95,7 +95,7 @@ void get_bounding_box (struct aiVector3D* min, struct aiVector3D* max)
 }
 
 /* ---------------------------------------------------------------------------- */
-void color4_to_float4(const struct aiColor4D *c, float f[4])
+void color4_to_float4(const C_STRUCT aiColor4D *c, float f[4])
 {
 	f[0] = c->r;
 	f[1] = c->g;
@@ -113,16 +113,16 @@ void set_float4(float f[4], float a, float b, float c, float d)
 }
 
 /* ---------------------------------------------------------------------------- */
-void apply_material(const struct aiMaterial *mtl)
+void apply_material(const C_STRUCT aiMaterial *mtl)
 {
 	float c[4];
 
 	GLenum fill_mode;
 	int ret1, ret2;
-	struct aiColor4D diffuse;
-	struct aiColor4D specular;
-	struct aiColor4D ambient;
-	struct aiColor4D emission;
+	C_STRUCT aiColor4D diffuse;
+	C_STRUCT aiColor4D specular;
+	C_STRUCT aiColor4D ambient;
+	C_STRUCT aiColor4D emission;
 	ai_real shininess, strength;
 	int two_sided;
 	int wireframe;
@@ -179,11 +179,11 @@ void apply_material(const struct aiMaterial *mtl)
 }
 
 /* ---------------------------------------------------------------------------- */
-void recursive_render (const struct aiScene *sc, const struct aiNode* nd)
+void recursive_render (const C_STRUCT aiScene *sc, const C_STRUCT aiNode* nd)
 {
 	unsigned int i;
 	unsigned int n = 0, t;
-	struct aiMatrix4x4 m = nd->mTransformation;
+	C_STRUCT aiMatrix4x4 m = nd->mTransformation;
 
 	/* update transform */
 	aiTransposeMatrix4(&m);
@@ -192,7 +192,7 @@ void recursive_render (const struct aiScene *sc, const struct aiNode* nd)
 
 	/* draw all meshes assigned to this node */
 	for (; n < nd->mNumMeshes; ++n) {
-		const struct aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]];
+		const C_STRUCT aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]];
 
 		apply_material(sc->mMaterials[mesh->mMaterialIndex]);
 
@@ -203,7 +203,7 @@ void recursive_render (const struct aiScene *sc, const struct aiNode* nd)
 		}
 
 		for (t = 0; t < mesh->mNumFaces; ++t) {
-			const struct aiFace* face = &mesh->mFaces[t];
+			const C_STRUCT aiFace* face = &mesh->mFaces[t];
 			GLenum face_mode;
 
 			switch(face->mNumIndices) {
@@ -324,7 +324,7 @@ int loadasset (const char* path)
 /* ---------------------------------------------------------------------------- */
 int main(int argc, char **argv)
 {
-	struct aiLogStream stream;
+	C_STRUCT aiLogStream stream;
 
 	glutInitWindowSize(900,600);
 	glutInitWindowPosition(100,100);

BIN
test/models/Collada/duck.zae


BIN
test/models/Collada/duck_nomanifest.zae


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 571 - 0
test/models/FBX/close_to_identity_transforms.fbx


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1 - 0
test/models/FBX/transparentTest.fbx


+ 98 - 0
test/models/glTF2/cameras/Cameras.gltf

@@ -0,0 +1,98 @@
+{
+  "scenes" : [
+    {
+      "nodes" : [ 0, 1, 2 ]
+    }
+  ],
+  "nodes" : [
+    {
+      "rotation" : [ -0.383, 0.0, 0.0, 0.92375 ],
+      "mesh" : 0
+    },
+    {
+      "translation" : [ 0.5, 0.5, 3.0 ],
+      "camera" : 0
+    },
+    {
+      "translation" : [ 0.5, 0.5, 3.0 ],
+      "camera" : 1
+    }
+  ],
+  
+  "cameras" : [
+    {
+      "type": "perspective",
+      "perspective": {
+        "aspectRatio": 1.0,
+        "yfov": 0.7,
+        "zfar": 100,
+        "znear": 0.01
+      }
+    },
+    {
+      "type": "orthographic",
+      "orthographic": {
+        "xmag": 1.0,
+        "ymag": 1.0,
+        "zfar": 100,
+        "znear": 0.01
+      }
+    }
+  ],
+  
+  "meshes" : [
+    {
+      "primitives" : [ {
+        "attributes" : {
+          "POSITION" : 1
+        },
+        "indices" : 0
+      } ]
+    }
+  ],
+
+  "buffers" : [
+    {
+      "uri" : "simpleSquare.bin",
+      "byteLength" : 60
+    }
+  ],
+  "bufferViews" : [
+    {
+      "buffer" : 0,
+      "byteOffset" : 0,
+      "byteLength" : 12,
+      "target" : 34963
+    },
+    {
+      "buffer" : 0,
+      "byteOffset" : 12,
+      "byteLength" : 48,
+      "target" : 34962
+    }
+  ],
+  "accessors" : [
+    {
+      "bufferView" : 0,
+      "byteOffset" : 0,
+      "componentType" : 5123,
+      "count" : 6,
+      "type" : "SCALAR",
+      "max" : [ 3 ],
+      "min" : [ 0 ]
+    },
+    {
+      "bufferView" : 1,
+      "byteOffset" : 0,
+      "componentType" : 5126,
+      "count" : 4,
+      "type" : "VEC3",
+      "max" : [ 1.0, 1.0, 0.0 ],
+      "min" : [ 0.0, 0.0, 0.0 ]
+    }
+  ],
+  
+  "asset" : {
+    "version" : "2.0"
+  }
+}

BIN
test/models/glTF2/cameras/simpleSquare.bin


BIN
test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube.bin


+ 282 - 0
test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube.gltf

@@ -0,0 +1,282 @@
+{
+  "accessors": [
+    {
+      "bufferView": 0,
+      "componentType": 5126,
+      "count": 24,
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 1,
+      "componentType": 5126,
+      "count": 24,
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 2,
+      "componentType": 5126,
+      "count": 24,
+      "type": "VEC3",
+      "max": [
+        0.0100000035,
+        0.0100000035,
+        0.01
+      ],
+      "min": [
+        -0.0100000044,
+        -0.0100000054,
+        -0.01
+      ]
+    },
+    {
+      "bufferView": 3,
+      "componentType": 5126,
+      "count": 24,
+      "type": "VEC3",
+      "name": "thin"
+    },
+    {
+      "bufferView": 4,
+      "componentType": 5126,
+      "count": 24,
+      "type": "VEC3",
+      "max": [
+        0.0,
+        0.01893253,
+        0.0
+      ],
+      "min": [
+        0.0,
+        0.0,
+        0.0
+      ],
+      "name": "thin"
+    },
+    {
+      "bufferView": 5,
+      "componentType": 5126,
+      "count": 24,
+      "type": "VEC3",
+      "name": "thin"
+    },
+    {
+      "bufferView": 6,
+      "componentType": 5126,
+      "count": 24,
+      "type": "VEC3",
+      "name": "angle"
+    },
+    {
+      "bufferView": 7,
+      "componentType": 5126,
+      "count": 24,
+      "type": "VEC3",
+      "max": [
+        0.0,
+        0.0198908355,
+        0.0
+      ],
+      "min": [
+        0.0,
+        0.0,
+        0.0
+      ],
+      "name": "angle"
+    },
+    {
+      "bufferView": 8,
+      "componentType": 5126,
+      "count": 24,
+      "type": "VEC3",
+      "name": "angle"
+    },
+    {
+      "bufferView": 9,
+      "componentType": 5123,
+      "count": 36,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 10,
+      "componentType": 5126,
+      "count": 127,
+      "type": "SCALAR",
+      "max": [
+        4.19999743
+      ],
+      "min": [
+        0.0
+      ]
+    },
+    {
+      "bufferView": 11,
+      "componentType": 5126,
+      "count": 254,
+      "type": "SCALAR"
+    }
+  ],
+  "animations": [
+    {
+      "channels": [
+        {
+          "sampler": 0,
+          "target": {
+            "node": 0,
+            "path": "weights"
+          }
+        }
+      ],
+      "samplers": [
+        {
+          "input": 10,
+          "interpolation": "LINEAR",
+          "output": 11
+        }
+      ],
+      "name": "Square"
+    }
+  ],
+  "asset": {
+    "generator": "glTF Tools for Unity",
+    "version": "2.0"
+  },
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 288
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 288,
+      "byteLength": 384
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 672,
+      "byteLength": 288
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 960,
+      "byteLength": 288
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 1248,
+      "byteLength": 288
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 1536,
+      "byteLength": 288
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 1824,
+      "byteLength": 288
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 2112,
+      "byteLength": 288
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 2400,
+      "byteLength": 288
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 2688,
+      "byteLength": 72
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 2760,
+      "byteLength": 508
+    },
+    {
+      "buffer": 0,
+      "byteOffset": 3268,
+      "byteLength": 1016
+    }
+  ],
+  "buffers": [
+    {
+      "uri": "AnimatedMorphCube.bin",
+      "byteLength": 4284
+    }
+  ],
+  "meshes": [
+    {
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 0,
+            "TANGENT": 1,
+            "POSITION": 2
+          },
+          "indices": 9,
+          "material": 0,
+          "targets": [
+            {
+              "NORMAL": 3,
+              "POSITION": 4,
+              "TANGENT": 5
+            },
+            {
+              "NORMAL": 6,
+              "POSITION": 7,
+              "TANGENT": 8
+            }
+          ]
+        }
+      ],
+      "weights": [
+        0.0,
+        0.0
+      ],
+      "name": "Cube"
+    }
+  ],
+  "materials": [
+    {
+      "pbrMetallicRoughness": {
+        "baseColorFactor": [
+          0.6038274,
+          0.6038274,
+          0.6038274,
+          1.0
+        ],
+        "metallicFactor": 0.0,
+        "roughnessFactor": 0.5
+      },
+      "name": "Material"
+    }
+  ],
+  "nodes": [
+    {
+      "mesh": 0,
+      "rotation": [
+        0.0,
+        0.7071067,
+        -0.7071068,
+        0.0
+      ],
+      "scale": [
+        100.0,
+        100.0,
+        100.0
+      ],
+      "name": "AnimatedMorphCube"
+    }
+  ],
+  "scene": 0,
+  "scenes": [
+    {
+      "nodes": [
+        0
+      ]
+    }
+  ]
+}

+ 1 - 1
test/unit/SceneDiffer.cpp

@@ -123,7 +123,7 @@ void SceneDiffer::showReport() {
         return;
     }
 
-    for ( std::vector<std::string>::iterator it = m_diffs.begin(); it != m_diffs.end(); it++ ) {
+    for ( std::vector<std::string>::iterator it = m_diffs.begin(); it != m_diffs.end(); ++it ) {
         std::cout << *it << "\n";
     }
 

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio