Ver código fonte

Merge branch 'master' into pugi_xml

Kim Kulling 5 anos atrás
pai
commit
d6892b3f58
100 arquivos alterados com 1648 adições e 1123 exclusões
  1. 73 9
      .github/workflows/ccpp.yml
  2. 54 47
      CMakeLists.txt
  3. 1 3
      Readme.md
  4. 15 16
      cmake/HunterGate.cmake
  5. 1 1
      cmake/assimp-hunter-config.cmake.in
  6. 7 2
      code/AssetLib/3DS/3DSExporter.cpp
  7. 7 2
      code/AssetLib/3DS/3DSHelper.h
  8. 2 2
      code/AssetLib/3DS/3DSLoader.cpp
  9. 2 2
      code/AssetLib/3MF/D3MFOpcPackage.cpp
  10. 78 13
      code/AssetLib/AC/ACLoader.cpp
  11. 31 3
      code/AssetLib/AC/ACLoader.h
  12. 6 8
      code/AssetLib/AMF/AMFImporter.cpp
  13. 1 1
      code/AssetLib/ASE/ASELoader.cpp
  14. 4 4
      code/AssetLib/Assbin/AssbinFileWriter.cpp
  15. 2 2
      code/AssetLib/Assjson/cencode.h
  16. 2 2
      code/AssetLib/B3D/B3DImporter.cpp
  17. 19 18
      code/AssetLib/BVH/BVHLoader.cpp
  18. 2 1
      code/AssetLib/BVH/BVHLoader.h
  19. 1 1
      code/AssetLib/Blender/BlenderCustomData.cpp
  20. 4 12
      code/AssetLib/Blender/BlenderDNA.cpp
  21. 4 3
      code/AssetLib/Blender/BlenderDNA.h
  22. 22 34
      code/AssetLib/Blender/BlenderDNA.inl
  23. 2 3
      code/AssetLib/Blender/BlenderLoader.cpp
  24. 7 1
      code/AssetLib/Blender/BlenderTessellator.cpp
  25. 1 1
      code/AssetLib/COB/COBLoader.cpp
  26. 1 1
      code/AssetLib/CSM/CSMLoader.cpp
  27. 2 2
      code/AssetLib/Collada/ColladaLoader.cpp
  28. 64 60
      code/AssetLib/Collada/ColladaParser.cpp
  29. 3 4
      code/AssetLib/Collada/ColladaParser.h
  30. 1 1
      code/AssetLib/DXF/DXFLoader.cpp
  31. 15 4
      code/AssetLib/FBX/FBXBinaryTokenizer.cpp
  32. 1 0
      code/AssetLib/FBX/FBXConverter.cpp
  33. 3 3
      code/AssetLib/FBX/FBXDocumentUtil.cpp
  34. 76 17
      code/AssetLib/FBX/FBXExporter.cpp
  35. 4 1
      code/AssetLib/FBX/FBXImporter.cpp
  36. 2 2
      code/AssetLib/FBX/FBXParser.cpp
  37. 1 1
      code/AssetLib/FBX/FBXTokenizer.cpp
  38. 9 11
      code/AssetLib/FBX/FBXUtil.cpp
  39. 7 14
      code/AssetLib/FBX/FBXUtil.h
  40. 3 3
      code/AssetLib/HMP/HMPLoader.cpp
  41. 9 0
      code/AssetLib/IFC/IFCReaderGen1_2x3.cpp
  42. 9 0
      code/AssetLib/IFC/IFCReaderGen2_2x3.cpp
  43. 7 2
      code/AssetLib/IFC/IFCReaderGen_2x3.h
  44. 3 3
      code/AssetLib/LWO/LWOLoader.cpp
  45. 1 1
      code/AssetLib/LWS/LWSLoader.cpp
  46. 5 5
      code/AssetLib/M3D/M3DImporter.cpp
  47. 423 421
      code/AssetLib/M3D/m3d.h
  48. 1 1
      code/AssetLib/MD2/MD2Loader.cpp
  49. 1 1
      code/AssetLib/MD3/MD3Loader.cpp
  50. 1 1
      code/AssetLib/MD5/MD5Loader.cpp
  51. 1 1
      code/AssetLib/MDC/MDCLoader.cpp
  52. 3 3
      code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp
  53. 2 2
      code/AssetLib/MDL/HalfLife/HL1MDLLoader.h
  54. 3 3
      code/AssetLib/MDL/MDLLoader.cpp
  55. 2 2
      code/AssetLib/MMD/MMDImporter.cpp
  56. 2 2
      code/AssetLib/MMD/MMDPmxParser.cpp
  57. 1 1
      code/AssetLib/MS3D/MS3DLoader.cpp
  58. 7 7
      code/AssetLib/NFF/NFFLoader.cpp
  59. 3 3
      code/AssetLib/NFF/NFFLoader.h
  60. 1 1
      code/AssetLib/OFF/OFFLoader.cpp
  61. 5 2
      code/AssetLib/Obj/ObjFileImporter.cpp
  62. 9 9
      code/AssetLib/Ogre/OgreBinarySerializer.cpp
  63. 1 1
      code/AssetLib/Ogre/OgreImporter.cpp
  64. 5 5
      code/AssetLib/Ogre/OgreStructs.cpp
  65. 14 14
      code/AssetLib/Ogre/OgreXmlSerializer.cpp
  66. 1 1
      code/AssetLib/OpenGEX/OpenGEXImporter.cpp
  67. 2 2
      code/AssetLib/Ply/PlyLoader.cpp
  68. 1 1
      code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp
  69. 2 3
      code/AssetLib/Q3D/Q3DLoader.cpp
  70. 1 1
      code/AssetLib/Raw/RawLoader.cpp
  71. 2 2
      code/AssetLib/SIB/SIBImporter.cpp
  72. 1 1
      code/AssetLib/SMD/SMDLoader.cpp
  73. 1 1
      code/AssetLib/STEPParser/STEPFileEncoding.cpp
  74. 2 2
      code/AssetLib/STL/STLLoader.cpp
  75. 9 9
      code/AssetLib/Step/STEPFile.h
  76. 1 1
      code/AssetLib/Terragen/TerragenLoader.cpp
  77. 1 1
      code/AssetLib/X/XFileImporter.cpp
  78. 14 13
      code/AssetLib/X/XFileParser.cpp
  79. 2 1
      code/AssetLib/X/XFileParser.h
  80. 19 81
      code/AssetLib/X3D/X3DImporter.cpp
  81. 2 10
      code/AssetLib/glTF/glTFAsset.h
  82. 37 37
      code/AssetLib/glTF/glTFAsset.inl
  83. 1 1
      code/AssetLib/glTF/glTFAssetWriter.h
  84. 5 5
      code/AssetLib/glTF/glTFAssetWriter.inl
  85. 4 0
      code/AssetLib/glTF/glTFCommon.cpp
  86. 4 6
      code/AssetLib/glTF/glTFCommon.h
  87. 22 2
      code/AssetLib/glTF/glTFExporter.cpp
  88. 1 1
      code/AssetLib/glTF/glTFExporter.h
  89. 5 5
      code/AssetLib/glTF/glTFImporter.cpp
  90. 7 12
      code/AssetLib/glTF2/glTF2Asset.h
  91. 59 25
      code/AssetLib/glTF2/glTF2Asset.inl
  92. 1 1
      code/AssetLib/glTF2/glTF2AssetWriter.h
  93. 47 14
      code/AssetLib/glTF2/glTF2AssetWriter.inl
  94. 186 10
      code/AssetLib/glTF2/glTF2Exporter.cpp
  95. 1 1
      code/AssetLib/glTF2/glTF2Exporter.h
  96. 74 53
      code/AssetLib/glTF2/glTF2Importer.cpp
  97. 8 3
      code/CMakeLists.txt
  98. 4 3
      code/Common/BaseImporter.cpp
  99. 10 8
      code/Common/DefaultIOStream.cpp
  100. 52 0
      code/Common/Exceptional.cpp

+ 73 - 9
.github/workflows/ccpp.yml

@@ -8,30 +8,39 @@ on:
 
 
 jobs:
 jobs:
   job:
   job:
-    name: ${{ matrix.os }}-${{ matrix.cxx }}-build-and-test
+    name: ${{ matrix.name }}-build-and-test
     runs-on: ${{ matrix.os }}
     runs-on: ${{ matrix.os }}
     strategy:
     strategy:
       fail-fast: false
       fail-fast: false
       matrix:
       matrix:
-        name: [ubuntu-gcc, macos-clang, windows-msvc, ubuntu-clang]
+        name: [ubuntu-latest-g++, macos-latest-clang++, windows-latest-cl.exe, ubuntu-latest-clang++, ubuntu-gcc-hunter, macos-clang-hunter, windows-msvc-hunter]
         # For Windows msvc, for Linux and macOS let's use the clang compiler, use gcc for Linux.
         # For Windows msvc, for Linux and macOS let's use the clang compiler, use gcc for Linux.
         include:
         include:
-          - name: windows-msvc
+          - name: windows-latest-cl.exe
             os: windows-latest
             os: windows-latest
             cxx: cl.exe
             cxx: cl.exe
             cc: cl.exe
             cc: cl.exe
-          - name: ubuntu-clang
+          - name: ubuntu-latest-clang++
             os: ubuntu-latest
             os: ubuntu-latest
             cxx: clang++
             cxx: clang++
             cc: clang
             cc: clang
-          - name: macos-clang
+          - name: macos-latest-clang++
             os: macos-latest
             os: macos-latest
             cxx: clang++
             cxx: clang++
             cc: clang
             cc: clang
-          - name: ubuntu-gcc
+          - name: ubuntu-latest-g++
             os: ubuntu-latest
             os: ubuntu-latest
             cxx: g++
             cxx: g++
             cc: gcc
             cc: gcc
+          - name: ubuntu-gcc-hunter
+            os: ubuntu-latest
+            toolchain: ninja-gcc-cxx17-fpic
+          - name: macos-clang-hunter
+            os: macos-latest
+            toolchain: ninja-clang-cxx17-fpic
+          - name: windows-msvc-hunter
+            os: windows-latest
+            toolchain: ninja-vs-win64-cxx17
 
 
     steps:
     steps:
     - uses: actions/checkout@v2
     - uses: actions/checkout@v2
@@ -40,20 +49,75 @@ jobs:
     
     
     - uses: ilammy/msvc-dev-cmd@v1
     - uses: ilammy/msvc-dev-cmd@v1
     
     
-    - uses: lukka/set-shell-env@v1
+    - name: Set Compiler Environment
+      if: "!endsWith(matrix.name, 'hunter')"
+      uses: lukka/set-shell-env@v1
       with:
       with:
         CXX: ${{ matrix.cxx }}
         CXX: ${{ matrix.cxx }}
         CC: ${{ matrix.cc }}
         CC: ${{ matrix.cc }}
+
+    - name: Set Compiler Environment for Hunter on Windows
+      if: startsWith(matrix.name, 'windows') && endsWith(matrix.name, 'hunter')
+      uses: lukka/set-shell-env@v1
+      with:
+        VS160COMNTOOLS: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools
+
+    - name: Checkout Hunter toolchains
+      if: endsWith(matrix.name, 'hunter')
+      uses: actions/checkout@v2
+      with:
+        repository: cpp-pm/polly
+        path: cmake/polly     
+
+    - name: Cache DX SDK
+      id: dxcache
+      if: contains(matrix.name, 'windows')
+      uses: actions/cache@v2
+      with:
+        path: '${{ github.workspace }}/DX_SDK'
+        key: ${{ runner.os }}-DX_SDK
+        restore-keys: |
+          ${{ runner.os }}-DX_SDK
+
+    - name: Download DXSetup
+      if: contains(matrix.name, 'windows') && steps.dxcache.outputs.cache-hit != 'true'
+      run: |
+        curl -s -o DXSDK_Jun10.exe --location https://download.microsoft.com/download/A/E/7/AE743F1F-632B-4809-87A9-AA1BB3458E31/DXSDK_Jun10.exe
+        cmd.exe /c start /wait .\DXSDK_Jun10.exe /U /O /F /S /P "${{ github.workspace }}\DX_SDK"
+
+    - name: Set Windows specific CMake arguments
+      if: contains(matrix.name, 'windows')
+      id: windows_extra_cmake_args
+      run: echo "::set-output name=args::-DASSIMP_BUILD_ASSIMP_TOOLS=1 -DASSIMP_BUILD_ASSIMP_VIEW=1"
     
     
+    - name: Set Hunter specific CMake arguments
+      if: contains(matrix.name, 'hunter')
+      id: hunter_extra_cmake_args
+      run: echo "::set-output name=args::-DBUILD_SHARED_LIBS=OFF -DASSIMP_HUNTER_ENABLED=ON -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/cmake/polly/${{ matrix.toolchain }}.cmake"
+
     - name: configure and build
     - name: configure and build
       uses: lukka/run-cmake@v2
       uses: lukka/run-cmake@v2
+      env:
+        DXSDK_DIR: '${{ github.workspace }}/DX_SDK'
+
       with:
       with:
         cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
         cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
         cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
         cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
-        cmakeAppendedArgs: '-GNinja -DCMAKE_BUILD_TYPE=Release'
+        cmakeAppendedArgs: '-GNinja -DCMAKE_BUILD_TYPE=Release ${{ steps.windows_extra_cmake_args.outputs.args }} ${{ steps.hunter_extra_cmake_args.outputs.args }}'
         buildWithCMakeArgs: '-- -v'
         buildWithCMakeArgs: '-- -v'
         buildDirectory: '${{ github.workspace }}/build/'
         buildDirectory: '${{ github.workspace }}/build/'
         
         
+    - name: Exclude certain tests in Hunter specific builds
+      if: contains(matrix.name, 'hunter')
+      id: hunter_extra_test_args
+      run: echo "::set-output name=args::--gtest_filter=-utOpenGEXImportExport.Importissue1340_EmptyCameraObject:utColladaZaeImportExport.importBlenFromFileTest"
+
     - name: test
     - name: test
-      run: cd build/bin && ./unit
+      run: cd build/bin && ./unit ${{ steps.hunter_extra_test_args.outputs.args }}
       shell: bash
       shell: bash
+
+    - uses: actions/upload-artifact@v2
+      if: matrix.name == 'windows-msvc'
+      with:
+        name: 'assimp-bins-${{ matrix.name }}-${{ github.sha }}'
+        path: build/bin

+ 54 - 47
CMakeLists.txt

@@ -35,6 +35,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #----------------------------------------------------------------------
 #----------------------------------------------------------------------
 SET(CMAKE_POLICY_DEFAULT_CMP0074 NEW)
 SET(CMAKE_POLICY_DEFAULT_CMP0074 NEW)
+SET(CMAKE_POLICY_DEFAULT_CMP0092 NEW)
 
 
 CMAKE_MINIMUM_REQUIRED( VERSION 3.0 )
 CMAKE_MINIMUM_REQUIRED( VERSION 3.0 )
 
 
@@ -44,8 +45,8 @@ option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
 IF(ASSIMP_HUNTER_ENABLED)
 IF(ASSIMP_HUNTER_ENABLED)
   include("cmake/HunterGate.cmake")
   include("cmake/HunterGate.cmake")
   HunterGate(
   HunterGate(
-    URL "https://github.com/ruslo/hunter/archive/v0.23.176.tar.gz"
-    SHA1 "2e9ae973d028660b735ac4c6142725ca36a0048a"
+    URL "https://github.com/cpp-pm/hunter/archive/v0.23.269.tar.gz"
+    SHA1 "64024b7b95b4c86d50ae05b926814448c93a70a0"
   )
   )
 
 
   add_definitions(-DASSIMP_USE_HUNTER)
   add_definitions(-DASSIMP_USE_HUNTER)
@@ -254,7 +255,11 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT CMAKE_COMPILER_IS_MINGW)
   SET(LIBSTDC++_LIBRARIES -lstdc++)
   SET(LIBSTDC++_LIBRARIES -lstdc++)
 ELSEIF(MSVC)
 ELSEIF(MSVC)
   # enable multi-core compilation with MSVC
   # enable multi-core compilation with MSVC
-  ADD_COMPILE_OPTIONS(/MP /bigobj /W4 /WX ) 
+  IF( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) # clang-cl
+    ADD_COMPILE_OPTIONS(/bigobj /W4 /WX )
+  ELSE() # msvc
+    ADD_COMPILE_OPTIONS(/MP /bigobj /W4 /WX)
+  ENDIF()
   # disable "elements of array '' will be default initialized" warning on MSVC2013
   # disable "elements of array '' will be default initialized" warning on MSVC2013
   IF(MSVC12)
   IF(MSVC12)
     ADD_COMPILE_OPTIONS(/wd4351)
     ADD_COMPILE_OPTIONS(/wd4351)
@@ -318,26 +323,27 @@ ENDIF()
 
 
 IF (ASSIMP_UBSAN)
 IF (ASSIMP_UBSAN)
   MESSAGE(STATUS "Undefined Behavior sanitizer enabled")
   MESSAGE(STATUS "Undefined Behavior sanitizer enabled")
-  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all")
-  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all")
+  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin -fno-sanitize-recover=all")
+  SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin -fno-sanitize-recover=all")
 ENDIF()
 ENDIF()
 
 
 INCLUDE (FindPkgMacros)
 INCLUDE (FindPkgMacros)
 INCLUDE (PrecompiledHeader)
 INCLUDE (PrecompiledHeader)
 
 
-# If this is an in-source build (CMAKE_SOURCE_DIR == CMAKE_BINARY_DIR),
-# write the library/executable files to the respective directories in the
-# source tree. During an out-of-source build, however, do not litter this
-# directory, since that is probably what the user wanted to avoid.
-IF ( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR )
-  SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/bin" )
-  SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/lib" )
-  SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/bin" )
-ELSE()
-  SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib")
-  SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
-  SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
-ENDIF ()
+# Set Assimp project output directory variables.
+SET(ASSIMP_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
+SET(ASSIMP_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
+SET(ASSIMP_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib")
+
+# Macro used to set the output directories of a target to the
+# respective Assimp output directories.
+MACRO(TARGET_USE_COMMON_OUTPUT_DIRECTORY target)
+  set_target_properties(${target} PROPERTIES
+      RUNTIME_OUTPUT_DIRECTORY ${ASSIMP_RUNTIME_OUTPUT_DIRECTORY}
+      LIBRARY_OUTPUT_DIRECTORY ${ASSIMP_LIBRARY_OUTPUT_DIRECTORY}
+      ARCHIVE_OUTPUT_DIRECTORY ${ASSIMP_ARCHIVE_OUTPUT_DIRECTORY}
+  )
+ENDMACRO()
 
 
 get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
 get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
 
 
@@ -354,6 +360,34 @@ IF (NOT TARGET uninstall)
   ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
   ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
 ENDIF()
 ENDIF()
 
 
+# cmake configuration files
+if(${BUILD_SHARED_LIBS})
+  set(BUILD_LIB_TYPE SHARED)
+else()
+  set(BUILD_LIB_TYPE STATIC)
+endif()
+
+IF( UNIX )
+  # Use GNUInstallDirs for Unix predefined directories
+  INCLUDE(GNUInstallDirs)
+
+  SET( ASSIMP_LIB_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR})
+  SET( ASSIMP_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
+  SET( ASSIMP_BIN_INSTALL_DIR ${CMAKE_INSTALL_BINDIR})
+ELSE()
+  # Cache these to allow the user to override them on non-Unix platforms
+  SET( ASSIMP_LIB_INSTALL_DIR "lib" CACHE STRING
+    "Path the built library files are installed to." )
+  SET( ASSIMP_INCLUDE_INSTALL_DIR "include" CACHE STRING
+    "Path the header files are installed to." )
+  SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE STRING
+    "Path the tool executables are installed to." )
+
+  SET(CMAKE_INSTALL_FULL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX}/${ASSIMP_INCLUDE_INSTALL_DIR})
+  SET(CMAKE_INSTALL_FULL_LIBDIR ${CMAKE_INSTALL_PREFIX}/${ASSIMP_LIB_INSTALL_DIR})
+  SET(CMAKE_INSTALL_FULL_BINDIR ${CMAKE_INSTALL_PREFIX}/${ASSIMP_BIN_INSTALL_DIR})
+ENDIF()
+
 IF(ASSIMP_HUNTER_ENABLED)
 IF(ASSIMP_HUNTER_ENABLED)
   set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
   set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
   set(INCLUDE_INSTALL_DIR "include")
   set(INCLUDE_INSTALL_DIR "include")
@@ -392,34 +426,6 @@ IF(ASSIMP_HUNTER_ENABLED)
       DESTINATION "${CONFIG_INSTALL_DIR}"
       DESTINATION "${CONFIG_INSTALL_DIR}"
   )
   )
 ELSE()
 ELSE()
-  # cmake configuration files
-  if(${BUILD_SHARED_LIBS})
-    set(BUILD_LIB_TYPE SHARED)
-  else()
-    set(BUILD_LIB_TYPE STATIC)
-  endif()
-
-  IF( UNIX )
-    # Use GNUInstallDirs for Unix predefined directories
-    INCLUDE(GNUInstallDirs)
-
-    SET( ASSIMP_LIB_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR})
-    SET( ASSIMP_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
-    SET( ASSIMP_BIN_INSTALL_DIR ${CMAKE_INSTALL_BINDIR})
-  ELSE()
-    # Cache these to allow the user to override them on non-Unix platforms
-    SET( ASSIMP_LIB_INSTALL_DIR "lib" CACHE STRING
-      "Path the built library files are installed to." )
-    SET( ASSIMP_INCLUDE_INSTALL_DIR "include" CACHE STRING
-      "Path the header files are installed to." )
-    SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE STRING
-      "Path the tool executables are installed to." )
-
-    SET(CMAKE_INSTALL_FULL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX}/${ASSIMP_INCLUDE_INSTALL_DIR})
-    SET(CMAKE_INSTALL_FULL_LIBDIR ${CMAKE_INSTALL_PREFIX}/${ASSIMP_LIB_INSTALL_DIR})
-    SET(CMAKE_INSTALL_FULL_BINDIR ${CMAKE_INSTALL_PREFIX}/${ASSIMP_BIN_INSTALL_DIR})
-  ENDIF()
-
   CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE)
   CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE)
   CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake" @ONLY IMMEDIATE)
   CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake" @ONLY IMMEDIATE)
   IF (is_multi_config)
   IF (is_multi_config)
@@ -671,7 +677,8 @@ if(WIN32)
   ENDIF()
   ENDIF()
 
 
   IF(MSVC_TOOLSET_VERSION)
   IF(MSVC_TOOLSET_VERSION)
-    set(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
+    SET(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
+    SET(ASSIMP_MSVC_VERSION ${MCVS_PREFIX})
   ELSE()
   ELSE()
     IF(MSVC12)
     IF(MSVC12)
       SET(ASSIMP_MSVC_VERSION "vc120")
       SET(ASSIMP_MSVC_VERSION "vc120")

+ 1 - 3
Readme.md

@@ -4,8 +4,6 @@ A library to import and export various 3d-model-formats including scene-post-pro
 ### Current project status ###
 ### Current project status ###
 [![Financial Contributors on Open Collective](https://opencollective.com/assimp/all/badge.svg?label=financial+contributors)](https://opencollective.com/assimp) 
 [![Financial Contributors on Open Collective](https://opencollective.com/assimp/all/badge.svg?label=financial+contributors)](https://opencollective.com/assimp) 
 ![C/C++ CI](https://github.com/assimp/assimp/workflows/C/C++%20CI/badge.svg)
 ![C/C++ CI](https://github.com/assimp/assimp/workflows/C/C++%20CI/badge.svg)
-[![Linux Build Status](https://travis-ci.org/assimp/assimp.svg)](https://travis-ci.org/assimp/assimp)
-[![Windows Build Status](https://ci.appveyor.com/api/projects/status/tmo433wax6u6cjp4?svg=true)](https://ci.appveyor.com/project/kimkulling/assimp)
 <a href="https://scan.coverity.com/projects/5607">
 <a href="https://scan.coverity.com/projects/5607">
   <img alt="Coverity Scan Build Status"
   <img alt="Coverity Scan Build Status"
        src="https://scan.coverity.com/projects/5607/badge.svg"/>
        src="https://scan.coverity.com/projects/5607/badge.svg"/>
@@ -72,7 +70,7 @@ The source code is organized in the following way:
 For more information, visit [our website](http://assimp.org/). Or check out the `./doc`- folder, which contains the official documentation in HTML format.
 For more information, visit [our website](http://assimp.org/). Or check out the `./doc`- folder, which contains the official documentation in HTML format.
 (CHMs for Windows are included in some release packages and should be located right here in the root folder).
 (CHMs for Windows are included in some release packages and should be located right here in the root folder).
 
 
-If the docs don't solve your problem, ask on [StackOverflow](http://stackoverflow.com/questions/tagged/assimp?sort=newest). If you think you found a bug, please open an issue on Github.
+If the docs don't solve your problem, ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest). If you think you found a bug, please open an issue on Github.
 
 
 For development discussions, there is also a (very low-volume) mailing list, _assimp-discussions_
 For development discussions, there is also a (very low-volume) mailing list, _assimp-discussions_
   [(subscribe here)]( https://lists.sourceforge.net/lists/listinfo/assimp-discussions)
   [(subscribe here)]( https://lists.sourceforge.net/lists/listinfo/assimp-discussions)

+ 15 - 16
cmake/HunterGate.cmake

@@ -1,4 +1,4 @@
-# Copyright (c) 2013-2018, Ruslan Baratov
+# Copyright (c) 2013-2019, Ruslan Baratov
 # All rights reserved.
 # All rights reserved.
 #
 #
 # Redistribution and use in source and binary forms, with or without
 # Redistribution and use in source and binary forms, with or without
@@ -60,7 +60,7 @@ option(HUNTER_STATUS_PRINT "Print working status" ON)
 option(HUNTER_STATUS_DEBUG "Print a lot info" OFF)
 option(HUNTER_STATUS_DEBUG "Print a lot info" OFF)
 option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON)
 option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON)
 
 
-set(HUNTER_WIKI "https://github.com/ruslo/hunter/wiki")
+set(HUNTER_ERROR_PAGE "https://docs.hunter.sh/en/latest/reference/errors")
 
 
 function(hunter_gate_status_print)
 function(hunter_gate_status_print)
   if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG)
   if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG)
@@ -79,9 +79,9 @@ function(hunter_gate_status_debug)
   endif()
   endif()
 endfunction()
 endfunction()
 
 
-function(hunter_gate_wiki wiki_page)
-  message("------------------------------ WIKI -------------------------------")
-  message("    ${HUNTER_WIKI}/${wiki_page}")
+function(hunter_gate_error_page error_page)
+  message("------------------------------ ERROR ------------------------------")
+  message("    ${HUNTER_ERROR_PAGE}/${error_page}.html")
   message("-------------------------------------------------------------------")
   message("-------------------------------------------------------------------")
   message("")
   message("")
   message(FATAL_ERROR "")
   message(FATAL_ERROR "")
@@ -94,14 +94,13 @@ function(hunter_gate_internal_error)
   endforeach()
   endforeach()
   message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
   message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
   message("")
   message("")
-  hunter_gate_wiki("error.internal")
+  hunter_gate_error_page("error.internal")
 endfunction()
 endfunction()
 
 
 function(hunter_gate_fatal_error)
 function(hunter_gate_fatal_error)
-  cmake_parse_arguments(hunter "" "WIKI" "" "${ARGV}")
-  string(COMPARE EQUAL "${hunter_WIKI}" "" have_no_wiki)
-  if(have_no_wiki)
-    hunter_gate_internal_error("Expected wiki")
+  cmake_parse_arguments(hunter "" "ERROR_PAGE" "" "${ARGV}")
+  if("${hunter_ERROR_PAGE}" STREQUAL "")
+    hunter_gate_internal_error("Expected ERROR_PAGE")
   endif()
   endif()
   message("")
   message("")
   foreach(x ${hunter_UNPARSED_ARGUMENTS})
   foreach(x ${hunter_UNPARSED_ARGUMENTS})
@@ -109,11 +108,11 @@ function(hunter_gate_fatal_error)
   endforeach()
   endforeach()
   message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
   message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
   message("")
   message("")
-  hunter_gate_wiki("${hunter_WIKI}")
+  hunter_gate_error_page("${hunter_ERROR_PAGE}")
 endfunction()
 endfunction()
 
 
 function(hunter_gate_user_error)
 function(hunter_gate_user_error)
-  hunter_gate_fatal_error(${ARGV} WIKI "error.incorrect.input.data")
+  hunter_gate_fatal_error(${ARGV} ERROR_PAGE "error.incorrect.input.data")
 endfunction()
 endfunction()
 
 
 function(hunter_gate_self root version sha1 result)
 function(hunter_gate_self root version sha1 result)
@@ -195,7 +194,7 @@ function(hunter_gate_detect_root)
 
 
   hunter_gate_fatal_error(
   hunter_gate_fatal_error(
       "Can't detect HUNTER_ROOT"
       "Can't detect HUNTER_ROOT"
-      WIKI "error.detect.hunter.root"
+      ERROR_PAGE "error.detect.hunter.root"
   )
   )
 endfunction()
 endfunction()
 
 
@@ -214,7 +213,7 @@ function(hunter_gate_download dir)
         "Settings:"
         "Settings:"
         "  HUNTER_ROOT: ${HUNTER_GATE_ROOT}"
         "  HUNTER_ROOT: ${HUNTER_GATE_ROOT}"
         "  HUNTER_SHA1: ${HUNTER_GATE_SHA1}"
         "  HUNTER_SHA1: ${HUNTER_GATE_SHA1}"
-        WIKI "error.run.install"
+        ERROR_PAGE "error.run.install"
     )
     )
   endif()
   endif()
   string(COMPARE EQUAL "${dir}" "" is_bad)
   string(COMPARE EQUAL "${dir}" "" is_bad)
@@ -400,7 +399,7 @@ macro(HunterGate)
       hunter_gate_fatal_error(
       hunter_gate_fatal_error(
           "Please set HunterGate *before* 'project' command. "
           "Please set HunterGate *before* 'project' command. "
           "Detected project: ${PROJECT_NAME}"
           "Detected project: ${PROJECT_NAME}"
-          WIKI "error.huntergate.before.project"
+          ERROR_PAGE "error.huntergate.before.project"
       )
       )
     endif()
     endif()
 
 
@@ -470,7 +469,7 @@ macro(HunterGate)
             "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces."
             "HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces."
             "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error"
             "Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error"
             "(Use at your own risk!)"
             "(Use at your own risk!)"
-            WIKI "error.spaces.in.hunter.root"
+            ERROR_PAGE "error.spaces.in.hunter.root"
         )
         )
       endif()
       endif()
     endif()
     endif()

+ 1 - 1
cmake/assimp-hunter-config.cmake.in

@@ -2,7 +2,7 @@
 
 
 find_package(RapidJSON CONFIG REQUIRED)
 find_package(RapidJSON CONFIG REQUIRED)
 find_package(ZLIB CONFIG REQUIRED)
 find_package(ZLIB CONFIG REQUIRED)
-find_package(utf8 CONFIG REQUIRED)
+find_package(utf8cpp CONFIG REQUIRED)
 find_package(irrXML CONFIG REQUIRED)
 find_package(irrXML CONFIG REQUIRED)
 find_package(minizip CONFIG REQUIRED)
 find_package(minizip CONFIG REQUIRED)
 find_package(openddlparser CONFIG REQUIRED)
 find_package(openddlparser CONFIG REQUIRED)

+ 7 - 2
code/AssetLib/3DS/3DSExporter.cpp

@@ -290,12 +290,18 @@ void Discreet3DSExporter::WriteMaterials() {
             ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR);
             ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR);
             WriteColor(color);
             WriteColor(color);
         }
         }
-
+                
         if (mat.Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS) {
         if (mat.Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS) {
             ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT);
             ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT);
             WriteColor(color);
             WriteColor(color);
         }
         }
 
 
+        float f;
+        if (mat.Get(AI_MATKEY_OPACITY, f) == AI_SUCCESS) {
+            ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_TRANSPARENCY);
+            WritePercentChunk(1.0f - f);
+        }
+
         if (mat.Get(AI_MATKEY_COLOR_EMISSIVE, color) == AI_SUCCESS) {
         if (mat.Get(AI_MATKEY_COLOR_EMISSIVE, color) == AI_SUCCESS) {
             ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SELF_ILLUM);
             ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SELF_ILLUM);
             WriteColor(color);
             WriteColor(color);
@@ -333,7 +339,6 @@ void Discreet3DSExporter::WriteMaterials() {
             writer.PutU2(static_cast<uint16_t>(shading_mode_out));
             writer.PutU2(static_cast<uint16_t>(shading_mode_out));
         }
         }
 
 
-        float f;
         if (mat.Get(AI_MATKEY_SHININESS, f) == AI_SUCCESS) {
         if (mat.Get(AI_MATKEY_SHININESS, f) == AI_SUCCESS) {
             ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHININESS);
             ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHININESS);
             WritePercentChunk(f);
             WritePercentChunk(f);

+ 7 - 2
code/AssetLib/3DS/3DSHelper.h

@@ -321,9 +321,10 @@ public:
 struct Face : public FaceWithSmoothingGroup {
 struct Face : public FaceWithSmoothingGroup {
 };
 };
 
 
-#ifdef _WIN32
+#ifdef _MSC_VER
+#pragma warning(push)
 #pragma warning(disable : 4315)
 #pragma warning(disable : 4315)
-#endif
+#endif // _MSC_VER
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Helper structure representing a texture */
 /** Helper structure representing a texture */
@@ -412,6 +413,10 @@ struct Texture {
 
 
 #include <assimp/Compiler/poppack1.h>
 #include <assimp/Compiler/poppack1.h>
 
 
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif // _MSC_VER
+
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** Helper structure representing a 3ds material */
 /** Helper structure representing a 3ds material */
 struct Material {
 struct Material {

+ 2 - 2
code/AssetLib/3DS/3DSLoader.cpp

@@ -147,7 +147,7 @@ void Discreet3DSImporter::InternReadFile(const std::string &pFile,
 
 
     // We should have at least one chunk
     // We should have at least one chunk
     if (theStream.GetRemainingSize() < 16) {
     if (theStream.GetRemainingSize() < 16) {
-        throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile);
+        throw DeadlyImportError("3DS file is either empty or corrupt: ", pFile);
     }
     }
     this->stream = &theStream;
     this->stream = &theStream;
 
 
@@ -178,7 +178,7 @@ void Discreet3DSImporter::InternReadFile(const std::string &pFile,
     // file.
     // file.
     for (auto &mesh : mScene->mMeshes) {
     for (auto &mesh : mScene->mMeshes) {
         if (mesh.mFaces.size() > 0 && mesh.mPositions.size() == 0) {
         if (mesh.mFaces.size() > 0 && mesh.mPositions.size() == 0) {
-            throw DeadlyImportError("3DS file contains faces but no vertices: " + pFile);
+            throw DeadlyImportError("3DS file contains faces but no vertices: ", pFile);
         }
         }
         CheckIndices(mesh);
         CheckIndices(mesh);
         MakeUnique(mesh);
         MakeUnique(mesh);

+ 2 - 2
code/AssetLib/3MF/D3MFOpcPackage.cpp

@@ -122,7 +122,7 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
         mZipArchive() {
         mZipArchive() {
     mZipArchive.reset(new ZipArchiveIOSystem(pIOHandler, rFile));
     mZipArchive.reset(new ZipArchiveIOSystem(pIOHandler, rFile));
     if (!mZipArchive->isOpen()) {
     if (!mZipArchive->isOpen()) {
-        throw DeadlyImportError("Failed to open file " + rFile + ".");
+        throw DeadlyImportError("Failed to open file ", rFile, ".");
     }
     }
 
 
     std::vector<std::string> fileList;
     std::vector<std::string> fileList;
@@ -198,7 +198,7 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) {
     });
     });
 
 
     if (itr == reader.m_relationShips.end()) {
     if (itr == reader.m_relationShips.end()) {
-        throw DeadlyImportError("Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE);
+        throw DeadlyImportError("Cannot find ", XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE);
     }
     }
 
 
     return (*itr)->target;
     return (*itr)->target;

+ 78 - 13
code/AssetLib/AC/ACLoader.cpp

@@ -471,26 +471,33 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
                     ++node->mNumMeshes;
                     ++node->mNumMeshes;
                 }
                 }
 
 
-                switch ((*it).flags & 0xf) {
+                switch ((*it).GetType()) {
                     // closed line
                     // closed line
-                case 0x1:
+                case Surface::ClosedLine:
                     needMat[idx].first += (unsigned int)(*it).entries.size();
                     needMat[idx].first += (unsigned int)(*it).entries.size();
                     needMat[idx].second += (unsigned int)(*it).entries.size() << 1u;
                     needMat[idx].second += (unsigned int)(*it).entries.size() << 1u;
                     break;
                     break;
 
 
                     // unclosed line
                     // unclosed line
-                case 0x2:
+                case Surface::OpenLine:
                     needMat[idx].first += (unsigned int)(*it).entries.size() - 1;
                     needMat[idx].first += (unsigned int)(*it).entries.size() - 1;
                     needMat[idx].second += ((unsigned int)(*it).entries.size() - 1) << 1u;
                     needMat[idx].second += ((unsigned int)(*it).entries.size() - 1) << 1u;
                     break;
                     break;
 
 
-                    // 0 == polygon, else unknown
+                    // triangle strip
+                case Surface::TriangleStrip:
+                    needMat[idx].first += (unsigned int)(*it).entries.size() - 2;
+                    needMat[idx].second += ((unsigned int)(*it).entries.size() - 2) * 3;
+                    break;
+
                 default:
                 default:
-                    if ((*it).flags & 0xf) {
-                        ASSIMP_LOG_WARN("AC3D: The type flag of a surface is unknown");
-                        (*it).flags &= ~(0xf);
-                    }
+                    // Coerce unknowns to a polygon and warn
+                    ASSIMP_LOG_WARN_F("AC3D: The type flag of a surface is unknown: ", (*it).flags);
+                    (*it).flags &= ~(Surface::Mask);
+                    // fallthrough
 
 
+                    // polygon
+                case Surface::Polygon:
                     // the number of faces increments by one, the number
                     // the number of faces increments by one, the number
                     // of vertices by surface.numref.
                     // of vertices by surface.numref.
                     needMat[idx].first++;
                     needMat[idx].first++;
@@ -546,8 +553,8 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
                         const Surface &src = *it;
                         const Surface &src = *it;
 
 
                         // closed polygon
                         // closed polygon
-                        unsigned int type = (*it).flags & 0xf;
-                        if (!type) {
+                        uint8_t type = (*it).GetType();
+                        if (type == Surface::Polygon) {
                             aiFace &face = *faces++;
                             aiFace &face = *faces++;
                             face.mNumIndices = (unsigned int)src.entries.size();
                             face.mNumIndices = (unsigned int)src.entries.size();
                             if (0 != face.mNumIndices) {
                             if (0 != face.mNumIndices) {
@@ -570,13 +577,71 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
                                     }
                                     }
                                 }
                                 }
                             }
                             }
+                        } else if (type == Surface::TriangleStrip) {
+                            for (unsigned int i = 0; i < (unsigned int)src.entries.size() - 2; ++i) {
+                                const Surface::SurfaceEntry &entry1 = src.entries[i];
+                                const Surface::SurfaceEntry &entry2 = src.entries[i + 1];
+                                const Surface::SurfaceEntry &entry3 = src.entries[i + 2];
+
+                                // skip degenerate triangles
+                                if (object.vertices[entry1.first] == object.vertices[entry2.first] ||
+                                        object.vertices[entry1.first] == object.vertices[entry3.first] ||
+                                        object.vertices[entry2.first] == object.vertices[entry3.first]) {
+                                    mesh->mNumFaces--;
+                                    mesh->mNumVertices -= 3;
+                                    continue;
+                                }
+
+                                aiFace &face = *faces++;
+                                face.mNumIndices = 3;
+                                face.mIndices = new unsigned int[face.mNumIndices];
+                                face.mIndices[0] = cur++;
+                                face.mIndices[1] = cur++;
+                                face.mIndices[2] = cur++;
+                                if (!(i & 1)) {
+                                    *vertices++ = object.vertices[entry1.first] + object.translation;
+                                    if (uv) {
+                                        uv->x = entry1.second.x;
+                                        uv->y = entry1.second.y;
+                                        ++uv;
+                                    }
+                                    *vertices++ = object.vertices[entry2.first] + object.translation;
+                                    if (uv) {
+                                        uv->x = entry2.second.x;
+                                        uv->y = entry2.second.y;
+                                        ++uv;
+                                    }
+                                } else {
+                                    *vertices++ = object.vertices[entry2.first] + object.translation;
+                                    if (uv) {
+                                        uv->x = entry2.second.x;
+                                        uv->y = entry2.second.y;
+                                        ++uv;
+                                    }
+                                    *vertices++ = object.vertices[entry1.first] + object.translation;
+                                    if (uv) {
+                                        uv->x = entry1.second.x;
+                                        uv->y = entry1.second.y;
+                                        ++uv;
+                                    }
+                                }
+                                if (static_cast<unsigned>(vertices - mesh->mVertices) >= mesh->mNumVertices) {
+                                    throw DeadlyImportError("AC3D: Invalid number of vertices");
+                                }
+                                *vertices++ = object.vertices[entry3.first] + object.translation;
+                                if (uv) {
+                                    uv->x = entry3.second.x;
+                                    uv->y = entry3.second.y;
+                                    ++uv;
+                                }
+                            }
                         } else {
                         } else {
 
 
                             it2 = (*it).entries.begin();
                             it2 = (*it).entries.begin();
 
 
                             // either a closed or an unclosed line
                             // either a closed or an unclosed line
                             unsigned int tmp = (unsigned int)(*it).entries.size();
                             unsigned int tmp = (unsigned int)(*it).entries.size();
-                            if (0x2 == type) --tmp;
+                            if (Surface::OpenLine == type) --tmp;
                             for (unsigned int m = 0; m < tmp; ++m) {
                             for (unsigned int m = 0; m < tmp; ++m) {
                                 aiFace &face = *faces++;
                                 aiFace &face = *faces++;
 
 
@@ -599,7 +664,7 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object,
                                     ++uv;
                                     ++uv;
                                 }
                                 }
 
 
-                                if (0x1 == type && tmp - 1 == m) {
+                                if (Surface::ClosedLine == type && tmp - 1 == m) {
                                     // if this is a closed line repeat its beginning now
                                     // if this is a closed line repeat its beginning now
                                     it2 = (*it).entries.begin();
                                     it2 = (*it).entries.begin();
                                 } else
                                 } else
@@ -697,7 +762,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile,
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open AC3D file " + pFile + ".");
+        throw DeadlyImportError("Failed to open AC3D file ", pFile, ".");
     }
     }
 
 
     // allocate storage and copy the contents of the file to a memory buffer
     // allocate storage and copy the contents of the file to a memory buffer

+ 31 - 3
code/AssetLib/AC/ACLoader.h

@@ -69,7 +69,10 @@ public:
     // Represents an AC3D material
     // Represents an AC3D material
     struct Material {
     struct Material {
         Material() :
         Material() :
-                rgb(0.6f, 0.6f, 0.6f), spec(1.f, 1.f, 1.f), shin(0.f), trans(0.f) {}
+                rgb(0.6f, 0.6f, 0.6f),
+                spec(1.f, 1.f, 1.f),
+                shin(0.f),
+                trans(0.f) {}
 
 
         // base color of the material
         // base color of the material
         aiColor3D rgb;
         aiColor3D rgb;
@@ -96,18 +99,43 @@ public:
     // Represents an AC3D surface
     // Represents an AC3D surface
     struct Surface {
     struct Surface {
         Surface() :
         Surface() :
-                mat(0), flags(0) {}
+                mat(0),
+                flags(0) {}
 
 
         unsigned int mat, flags;
         unsigned int mat, flags;
 
 
         typedef std::pair<unsigned int, aiVector2D> SurfaceEntry;
         typedef std::pair<unsigned int, aiVector2D> SurfaceEntry;
         std::vector<SurfaceEntry> entries;
         std::vector<SurfaceEntry> entries;
+
+        // Type is low nibble of flags
+        enum Type : uint8_t {
+            Polygon = 0x0,
+            ClosedLine = 0x1,
+            OpenLine = 0x2,
+            TriangleStrip = 0x4, // ACC extension (TORCS and Speed Dreams)
+
+            Mask = 0xf,
+        };
+
+        inline constexpr uint8_t GetType() const { return (flags & Mask); }
     };
     };
 
 
     // Represents an AC3D object
     // Represents an AC3D object
     struct Object {
     struct Object {
         Object() :
         Object() :
-                type(World), name(""), children(), texture(""), texRepeat(1.f, 1.f), texOffset(0.0f, 0.0f), rotation(), translation(), vertices(), surfaces(), numRefs(0), subDiv(0), crease() {}
+                type(World),
+                name(""),
+                children(),
+                texture(""),
+                texRepeat(1.f, 1.f),
+                texOffset(0.0f, 0.0f),
+                rotation(),
+                translation(),
+                vertices(),
+                surfaces(),
+                numRefs(0),
+                subDiv(0),
+                crease() {}
 
 
         // Type description
         // Type description
         enum Type {
         enum Type {

+ 6 - 8
code/AssetLib/AMF/AMFImporter.cpp

@@ -170,7 +170,7 @@ void AMFImporter::Throw_MoreThanOnceDefined(const std::string &nodeName, const s
 }
 }
 
 
 void AMFImporter::Throw_ID_NotFound(const std::string &pID) const {
 void AMFImporter::Throw_ID_NotFound(const std::string &pID) const {
-    throw DeadlyImportError("Not found node with name \"" + pID + "\".");
+    throw DeadlyImportError("Not found node with name \"", pID, "\".");
 }
 }
 
 
 /*********************************************************************************************************************************************/
 /*********************************************************************************************************************************************/
@@ -267,7 +267,7 @@ void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) {
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
+        throw DeadlyImportError("Failed to open AMF file ", pFile, ".");
     }
     }
 
 
     mXmlParser = new XmlParser();
     mXmlParser = new XmlParser();
@@ -409,7 +409,7 @@ void AMFImporter::ParseNode_Instance(XmlNode &node) {
         ParseHelper_Node_Enter(ne);
         ParseHelper_Node_Enter(ne);
         for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
         for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
             bool read_flag[6] = { false, false, false, false, false, false };
             bool read_flag[6] = { false, false, false, false, false, false };
-            std::string currentName = currentNode.name();
+            const std::string &currentName = currentNode.name();
             if (currentName == "deltax") {
             if (currentName == "deltax") {
                 read_flag[0] = true;
                 read_flag[0] = true;
                 als.Delta.x = (ai_real)std::atof(currentNode.value());
                 als.Delta.x = (ai_real)std::atof(currentNode.value());
@@ -446,8 +446,7 @@ void AMFImporter::ParseNode_Instance(XmlNode &node) {
 // Multi elements - Yes.
 // Multi elements - Yes.
 // Parent element - <amf>.
 // Parent element - <amf>.
 void AMFImporter::ParseNode_Object(XmlNode &node) {
 void AMFImporter::ParseNode_Object(XmlNode &node) {
-
-    AMFNodeElementBase *ne(nullptr);
+    AMFNodeElementBase *ne = nullptr;
 
 
     // Read attributes for node <object>.
     // Read attributes for node <object>.
     std::string id = node.attribute("id").as_string();
     std::string id = node.attribute("id").as_string();
@@ -466,7 +465,7 @@ void AMFImporter::ParseNode_Object(XmlNode &node) {
     if (!node.empty()) {
     if (!node.empty()) {
         ParseHelper_Node_Enter(ne);
         ParseHelper_Node_Enter(ne);
         for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
         for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
-            const std::string currentName = currentNode.name();
+            const std::string &currentName = currentNode.name();
             if (currentName == "color") {
             if (currentName == "color") {
                 ParseNode_Color(currentNode);
                 ParseNode_Color(currentNode);
                 col_read = true;
                 col_read = true;
@@ -523,9 +522,8 @@ bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool p
         return true;
         return true;
     }
     }
 
 
-    if (!extension.length() || pCheckSig) {
+    if (extension.empty() || pCheckSig) {
         const char *tokens[] = { "<amf" };
         const char *tokens[] = { "<amf" };
-
         return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
         return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
     }
     }
 
 

+ 1 - 1
code/AssetLib/ASE/ASELoader.cpp

@@ -137,7 +137,7 @@ void ASEImporter::InternReadFile(const std::string &pFile,
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open ASE file " + pFile + ".");
+        throw DeadlyImportError("Failed to open ASE file ", pFile, ".");
     }
     }
 
 
     // Allocate storage and copy the contents of the file to a memory buffer
     // Allocate storage and copy the contents of the file to a memory buffer

+ 4 - 4
code/AssetLib/Assbin/AssbinFileWriter.cpp

@@ -60,10 +60,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #include <time.h>
 #include <time.h>
 
 
-#ifdef _WIN32
+#if _MSC_VER
 #pragma warning(push)
 #pragma warning(push)
 #pragma warning(disable : 4706)
 #pragma warning(disable : 4706)
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 namespace Assimp {
 namespace Assimp {
 
 
@@ -825,8 +825,8 @@ void DumpSceneToAssbin(
     AssbinFileWriter fileWriter(shortened, compressed);
     AssbinFileWriter fileWriter(shortened, compressed);
     fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene);
     fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene);
 }
 }
-#ifdef _WIN32
+#if _MSC_VER
 #pragma warning(pop)
 #pragma warning(pop)
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 } // end of namespace Assimp
 } // end of namespace Assimp

+ 2 - 2
code/AssetLib/Assjson/cencode.h

@@ -8,9 +8,9 @@ For details, see http://sourceforge.net/projects/libb64
 #ifndef BASE64_CENCODE_H
 #ifndef BASE64_CENCODE_H
 #define BASE64_CENCODE_H
 #define BASE64_CENCODE_H
 
 
-#ifdef _WIN32
+#ifdef _MSC_VER
 #pragma warning(disable : 4127 )
 #pragma warning(disable : 4127 )
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 typedef enum
 typedef enum
 {
 {

+ 2 - 2
code/AssetLib/B3D/B3DImporter.cpp

@@ -119,7 +119,7 @@ void B3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open B3D file " + pFile + ".");
+        throw DeadlyImportError("Failed to open B3D file ", pFile, ".");
     }
     }
 
 
     // check whether the .b3d file is large enough to contain
     // check whether the .b3d file is large enough to contain
@@ -147,7 +147,7 @@ AI_WONT_RETURN void B3DImporter::Fail(string str) {
 #ifdef DEBUG_B3D
 #ifdef DEBUG_B3D
     ASSIMP_LOG_ERROR_F("Error in B3D file data: ", str);
     ASSIMP_LOG_ERROR_F("Error in B3D file data: ", str);
 #endif
 #endif
-    throw DeadlyImportError("B3D Importer - error in B3D file data: " + str);
+    throw DeadlyImportError("B3D Importer - error in B3D file data: ", str);
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 19 - 18
code/AssetLib/BVH/BVHLoader.cpp

@@ -71,6 +71,13 @@ static const aiImporterDesc desc = {
     "bvh"
     "bvh"
 };
 };
 
 
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+template<typename... T>
+AI_WONT_RETURN void BVHLoader::ThrowException(T&&... args) {
+    throw DeadlyImportError(mFileName, ":", mLine, " - ", args...);
+}
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 // Constructor to be privately used by Importer
 BVHLoader::BVHLoader() :
 BVHLoader::BVHLoader() :
@@ -118,7 +125,7 @@ void BVHLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IOSyst
     // read file into memory
     // read file into memory
     std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
     std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open file " + pFile + ".");
+        throw DeadlyImportError("Failed to open file ", pFile, ".");
     }
     }
 
 
     size_t fileSize = file->FileSize();
     size_t fileSize = file->FileSize();
@@ -176,12 +183,12 @@ aiNode *BVHLoader::ReadNode() {
     // first token is name
     // first token is name
     std::string nodeName = GetNextToken();
     std::string nodeName = GetNextToken();
     if (nodeName.empty() || nodeName == "{")
     if (nodeName.empty() || nodeName == "{")
-        ThrowException(format() << "Expected node name, but found \"" << nodeName << "\".");
+        ThrowException("Expected node name, but found \"", nodeName, "\".");
 
 
     // then an opening brace should follow
     // then an opening brace should follow
     std::string openBrace = GetNextToken();
     std::string openBrace = GetNextToken();
     if (openBrace != "{")
     if (openBrace != "{")
-        ThrowException(format() << "Expected opening brace \"{\", but found \"" << openBrace << "\".");
+        ThrowException("Expected opening brace \"{\", but found \"", openBrace, "\".");
 
 
     // Create a node
     // Create a node
     aiNode *node = new aiNode(nodeName);
     aiNode *node = new aiNode(nodeName);
@@ -211,7 +218,7 @@ aiNode *BVHLoader::ReadNode() {
             siteToken.clear();
             siteToken.clear();
             siteToken = GetNextToken();
             siteToken = GetNextToken();
             if (siteToken != "Site")
             if (siteToken != "Site")
-                ThrowException(format() << "Expected \"End Site\" keyword, but found \"" << token << " " << siteToken << "\".");
+                ThrowException("Expected \"End Site\" keyword, but found \"", token, " ", siteToken, "\".");
 
 
             aiNode *child = ReadEndSite(nodeName);
             aiNode *child = ReadEndSite(nodeName);
             child->mParent = node;
             child->mParent = node;
@@ -221,7 +228,7 @@ aiNode *BVHLoader::ReadNode() {
             break;
             break;
         } else {
         } else {
             // everything else is a parse error
             // everything else is a parse error
-            ThrowException(format() << "Unknown keyword \"" << token << "\".");
+            ThrowException("Unknown keyword \"", token, "\".");
         }
         }
     }
     }
 
 
@@ -242,7 +249,7 @@ aiNode *BVHLoader::ReadEndSite(const std::string &pParentName) {
     // check opening brace
     // check opening brace
     std::string openBrace = GetNextToken();
     std::string openBrace = GetNextToken();
     if (openBrace != "{")
     if (openBrace != "{")
-        ThrowException(format() << "Expected opening brace \"{\", but found \"" << openBrace << "\".");
+        ThrowException("Expected opening brace \"{\", but found \"", openBrace, "\".");
 
 
     // Create a node
     // Create a node
     aiNode *node = new aiNode("EndSite_" + pParentName);
     aiNode *node = new aiNode("EndSite_" + pParentName);
@@ -261,7 +268,7 @@ aiNode *BVHLoader::ReadEndSite(const std::string &pParentName) {
             break;
             break;
         } else {
         } else {
             // everything else is a parse error
             // everything else is a parse error
-            ThrowException(format() << "Unknown keyword \"" << token << "\".");
+            ThrowException("Unknown keyword \"", token, "\".");
         }
         }
     }
     }
 
 
@@ -307,7 +314,7 @@ void BVHLoader::ReadNodeChannels(BVHLoader::Node &pNode) {
         else if (channelToken == "Zrotation")
         else if (channelToken == "Zrotation")
             pNode.mChannels.push_back(Channel_RotationZ);
             pNode.mChannels.push_back(Channel_RotationZ);
         else
         else
-            ThrowException(format() << "Invalid channel specifier \"" << channelToken << "\".");
+            ThrowException("Invalid channel specifier \"", channelToken, "\".");
     }
     }
 }
 }
 
 
@@ -317,7 +324,7 @@ void BVHLoader::ReadMotion(aiScene * /*pScene*/) {
     // Read number of frames
     // Read number of frames
     std::string tokenFrames = GetNextToken();
     std::string tokenFrames = GetNextToken();
     if (tokenFrames != "Frames:")
     if (tokenFrames != "Frames:")
-        ThrowException(format() << "Expected frame count \"Frames:\", but found \"" << tokenFrames << "\".");
+        ThrowException("Expected frame count \"Frames:\", but found \"", tokenFrames, "\".");
 
 
     float numFramesFloat = GetNextTokenAsFloat();
     float numFramesFloat = GetNextTokenAsFloat();
     mAnimNumFrames = (unsigned int)numFramesFloat;
     mAnimNumFrames = (unsigned int)numFramesFloat;
@@ -326,7 +333,7 @@ void BVHLoader::ReadMotion(aiScene * /*pScene*/) {
     std::string tokenDuration1 = GetNextToken();
     std::string tokenDuration1 = GetNextToken();
     std::string tokenDuration2 = GetNextToken();
     std::string tokenDuration2 = GetNextToken();
     if (tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
     if (tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
-        ThrowException(format() << "Expected frame duration \"Frame Time:\", but found \"" << tokenDuration1 << " " << tokenDuration2 << "\".");
+        ThrowException("Expected frame duration \"Frame Time:\", but found \"", tokenDuration1, " ", tokenDuration2, "\".");
 
 
     mAnimTickDuration = GetNextTokenAsFloat();
     mAnimTickDuration = GetNextTokenAsFloat();
 
 
@@ -393,17 +400,11 @@ float BVHLoader::GetNextTokenAsFloat() {
     ctoken = fast_atoreal_move<float>(ctoken, result);
     ctoken = fast_atoreal_move<float>(ctoken, result);
 
 
     if (ctoken != token.c_str() + token.length())
     if (ctoken != token.c_str() + token.length())
-        ThrowException(format() << "Expected a floating point number, but found \"" << token << "\".");
+        ThrowException("Expected a floating point number, but found \"", token, "\".");
 
 
     return result;
     return result;
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-// Aborts the file reading with an exception
-AI_WONT_RETURN void BVHLoader::ThrowException(const std::string &pError) {
-    throw DeadlyImportError(format() << mFileName << ":" << mLine << " - " << pError);
-}
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructs an animation for the motion data and stores it in the given scene
 // Constructs an animation for the motion data and stores it in the given scene
 void BVHLoader::CreateAnimation(aiScene *pScene) {
 void BVHLoader::CreateAnimation(aiScene *pScene) {
@@ -453,7 +454,7 @@ void BVHLoader::CreateAnimation(aiScene *pScene) {
                     std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel);
                     std::map<BVHLoader::ChannelType, int>::iterator mapIter = channelMap.find(channel);
 
 
                     if (mapIter == channelMap.end())
                     if (mapIter == channelMap.end())
-                        throw DeadlyImportError("Missing position channel in node " + nodeName);
+                        throw DeadlyImportError("Missing position channel in node ", nodeName);
                     else {
                     else {
                         int channelIdx = mapIter->second;
                         int channelIdx = mapIter->second;
                         switch (channel) {
                         switch (channel) {

+ 2 - 1
code/AssetLib/BVH/BVHLoader.h

@@ -134,7 +134,8 @@ protected:
     float GetNextTokenAsFloat();
     float GetNextTokenAsFloat();
 
 
     /** Aborts the file reading with an exception */
     /** Aborts the file reading with an exception */
-    AI_WONT_RETURN void ThrowException(const std::string &pError) AI_WONT_RETURN_SUFFIX;
+    template<typename... T>
+    AI_WONT_RETURN void ThrowException(T&&... args) AI_WONT_RETURN_SUFFIX;
 
 
     /** Constructs an animation for the motion data and stores it in the given scene */
     /** Constructs an animation for the motion data and stores it in the given scene */
     void CreateAnimation(aiScene *pScene);
     void CreateAnimation(aiScene *pScene);

+ 1 - 1
code/AssetLib/Blender/BlenderCustomData.cpp

@@ -149,7 +149,7 @@ bool isValidCustomDataType(const int cdtype) {
 
 
 bool readCustomData(std::shared_ptr<ElemBase> &out, const int cdtype, const size_t cnt, const FileDatabase &db) {
 bool readCustomData(std::shared_ptr<ElemBase> &out, const int cdtype, const size_t cnt, const FileDatabase &db) {
     if (!isValidCustomDataType(cdtype)) {
     if (!isValidCustomDataType(cdtype)) {
-        throw Error((Formatter::format(), "CustomData.type ", cdtype, " out of index"));
+        throw Error("CustomData.type ", cdtype, " out of index");
     }
     }
 
 
     const CustomDataTypeDescription cdtd = customDataTypeDescriptions[cdtype];
     const CustomDataTypeDescription cdtd = customDataTypeDescriptions[cdtype];

+ 4 - 12
code/AssetLib/Blender/BlenderDNA.cpp

@@ -130,9 +130,7 @@ void DNAParser::Parse() {
 
 
         uint16_t n = stream.GetI2();
         uint16_t n = stream.GetI2();
         if (n >= types.size()) {
         if (n >= types.size()) {
-            throw DeadlyImportError((format(),
-                    "BlenderDNA: Invalid type index in structure name", n,
-                    " (there are only ", types.size(), " entries)"));
+            throw DeadlyImportError("BlenderDNA: Invalid type index in structure name", n, " (there are only ", types.size(), " entries)");
         }
         }
 
 
         // maintain separate indexes
         // maintain separate indexes
@@ -151,9 +149,7 @@ void DNAParser::Parse() {
 
 
             uint16_t j = stream.GetI2();
             uint16_t j = stream.GetI2();
             if (j >= types.size()) {
             if (j >= types.size()) {
-                throw DeadlyImportError((format(),
-                        "BlenderDNA: Invalid type index in structure field ", j,
-                        " (there are only ", types.size(), " entries)"));
+                throw DeadlyImportError("BlenderDNA: Invalid type index in structure field ", j, " (there are only ", types.size(), " entries)");
             }
             }
             s.fields.push_back(Field());
             s.fields.push_back(Field());
             Field &f = s.fields.back();
             Field &f = s.fields.back();
@@ -164,9 +160,7 @@ void DNAParser::Parse() {
 
 
             j = stream.GetI2();
             j = stream.GetI2();
             if (j >= names.size()) {
             if (j >= names.size()) {
-                throw DeadlyImportError((format(),
-                        "BlenderDNA: Invalid name index in structure field ", j,
-                        " (there are only ", names.size(), " entries)"));
+                throw DeadlyImportError("BlenderDNA: Invalid name index in structure field ", j, " (there are only ", names.size(), " entries)");
             }
             }
 
 
             f.name = names[j];
             f.name = names[j];
@@ -188,9 +182,7 @@ void DNAParser::Parse() {
             if (*f.name.rbegin() == ']') {
             if (*f.name.rbegin() == ']') {
                 const std::string::size_type rb = f.name.find('[');
                 const std::string::size_type rb = f.name.find('[');
                 if (rb == std::string::npos) {
                 if (rb == std::string::npos) {
-                    throw DeadlyImportError((format(),
-                            "BlenderDNA: Encountered invalid array declaration ",
-                            f.name));
+                    throw DeadlyImportError("BlenderDNA: Encountered invalid array declaration ", f.name);
                 }
                 }
 
 
                 f.flags |= FieldFlag_Array;
                 f.flags |= FieldFlag_Array;

+ 4 - 3
code/AssetLib/Blender/BlenderDNA.h

@@ -83,9 +83,10 @@ class ObjectCache;
  *  ancestry. */
  *  ancestry. */
 // -------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------
 struct Error : DeadlyImportError {
 struct Error : DeadlyImportError {
-    Error(const std::string &s) :
-            DeadlyImportError(s) {
-        // empty
+    template<typename... T>
+    explicit Error(T&&... args)
+        : DeadlyImportError(args...)
+    {
     }
     }
 };
 };
 
 

+ 22 - 34
code/AssetLib/Blender/BlenderDNA.inl

@@ -57,9 +57,7 @@ const Field& Structure :: operator [] (const std::string& ss) const
 {
 {
     std::map<std::string, size_t>::const_iterator it = indices.find(ss);
     std::map<std::string, size_t>::const_iterator it = indices.find(ss);
     if (it == indices.end()) {
     if (it == indices.end()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`"
-            ));
+        throw Error("BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`");
     }
     }
 
 
     return fields[(*it).second];
     return fields[(*it).second];
@@ -76,9 +74,7 @@ const Field* Structure :: Get (const std::string& ss) const
 const Field& Structure :: operator [] (const size_t i) const
 const Field& Structure :: operator [] (const size_t i) const
 {
 {
     if (i >= fields.size()) {
     if (i >= fields.size()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: There is no field with index `",i,"` in structure `",name,"`"
-            ));
+        throw Error("BlendDNA: There is no field with index `",i,"` in structure `",name,"`");
     }
     }
 
 
     return fields[i];
     return fields[i];
@@ -109,9 +105,7 @@ void Structure :: ReadFieldArray(T (& out)[M], const char* name, const FileDatab
 
 
         // is the input actually an array?
         // is the input actually an array?
         if (!(f.flags & FieldFlag_Array)) {
         if (!(f.flags & FieldFlag_Array)) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
-                this->name,"` ought to be an array of size ",M
-                ));
+            throw Error("Field `",name,"` of structure `",this->name,"` ought to be an array of size ",M);
         }
         }
 
 
         db.reader->IncPtr(f.offset);
         db.reader->IncPtr(f.offset);
@@ -148,9 +142,9 @@ void Structure :: ReadFieldArray2(T (& out)[M][N], const char* name, const FileD
 
 
         // is the input actually an array?
         // is the input actually an array?
         if (!(f.flags & FieldFlag_Array)) {
         if (!(f.flags & FieldFlag_Array)) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
+            throw Error("Field `",name,"` of structure `",
                 this->name,"` ought to be an array of size ",M,"*",N
                 this->name,"` ought to be an array of size ",M,"*",N
-                ));
+                );
         }
         }
 
 
         db.reader->IncPtr(f.offset);
         db.reader->IncPtr(f.offset);
@@ -195,8 +189,8 @@ bool Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabas
 
 
         // sanity check, should never happen if the genblenddna script is right
         // sanity check, should never happen if the genblenddna script is right
         if (!(f->flags & FieldFlag_Pointer)) {
         if (!(f->flags & FieldFlag_Pointer)) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
-                this->name,"` ought to be a pointer"));
+            throw Error("Field `",name,"` of structure `",
+                this->name,"` ought to be a pointer");
         }
         }
 
 
         db.reader->IncPtr(f->offset);
         db.reader->IncPtr(f->offset);
@@ -241,8 +235,8 @@ bool Structure :: ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
 #ifdef _DEBUG
 #ifdef _DEBUG
         // sanity check, should never happen if the genblenddna script is right
         // sanity check, should never happen if the genblenddna script is right
         if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) {
         if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) {
-            throw Error((Formatter::format(),"Field `",name,"` of structure `",
-                this->name,"` ought to be a pointer AND an array"));
+            throw Error("Field `",name,"` of structure `",
+                this->name,"` ought to be a pointer AND an array");
         }
         }
 #endif // _DEBUG
 #endif // _DEBUG
 
 
@@ -322,8 +316,8 @@ bool Structure::ReadCustomDataPtr(std::shared_ptr<ElemBase>&out, int cdtype, con
 
 
 		// sanity check, should never happen if the genblenddna script is right
 		// sanity check, should never happen if the genblenddna script is right
 		if (!(f->flags & FieldFlag_Pointer)) {
 		if (!(f->flags & FieldFlag_Pointer)) {
-			throw Error((Formatter::format(), "Field `", name, "` of structure `",
-				this->name, "` ought to be a pointer"));
+			throw Error("Field `", name, "` of structure `",
+				this->name, "` ought to be a pointer");
 		}
 		}
 
 
 		db.reader->IncPtr(f->offset);
 		db.reader->IncPtr(f->offset);
@@ -369,8 +363,8 @@ bool Structure::ReadFieldPtrVector(vector<TOUT<T>>&out, const char* name, const
 
 
 		// sanity check, should never happen if the genblenddna script is right
 		// sanity check, should never happen if the genblenddna script is right
 		if (!(f->flags & FieldFlag_Pointer)) {
 		if (!(f->flags & FieldFlag_Pointer)) {
-			throw Error((Formatter::format(), "Field `", name, "` of structure `",
-				this->name, "` ought to be a pointer"));
+			throw Error("Field `", name, "` of structure `",
+				this->name, "` ought to be a pointer");
 		}
 		}
 
 
 		db.reader->IncPtr(f->offset);
 		db.reader->IncPtr(f->offset);
@@ -428,9 +422,9 @@ bool Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const Fil
     // and check if it matches the type which we expect.
     // and check if it matches the type which we expect.
     const Structure& ss = db.dna[block->dna_index];
     const Structure& ss = db.dna[block->dna_index];
     if (ss != s) {
     if (ss != s) {
-        throw Error((Formatter::format(),"Expected target to be of type `",s.name,
+        throw Error("Expected target to be of type `",s.name,
             "` but seemingly it is a `",ss.name,"` instead"
             "` but seemingly it is a `",ss.name,"` instead"
-            ));
+            );
     }
     }
 
 
     // try to retrieve the object from the cache
     // try to retrieve the object from the cache
@@ -614,16 +608,14 @@ const FileBlockHead* Structure :: LocateFileBlockForAddress(const Pointer & ptrv
     if (it == db.entries.end()) {
     if (it == db.entries.end()) {
         // this is crucial, pointers may not be invalid.
         // this is crucial, pointers may not be invalid.
         // this is either a corrupted file or an attempted attack.
         // this is either a corrupted file or an attempted attack.
-        throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
-            std::hex,ptrval.val,", no file block falls into this address range"
-            ));
+        throw DeadlyImportError("Failure resolving pointer 0x",
+            std::hex,ptrval.val,", no file block falls into this address range");
     }
     }
     if (ptrval.val >= (*it).address.val + (*it).size) {
     if (ptrval.val >= (*it).address.val + (*it).size) {
-        throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
+        throw DeadlyImportError("Failure resolving pointer 0x",
             std::hex,ptrval.val,", nearest file block starting at 0x",
             std::hex,ptrval.val,", nearest file block starting at 0x",
             (*it).address.val," ends at 0x",
             (*it).address.val," ends at 0x",
-            (*it).address.val + (*it).size
-            ));
+            (*it).address.val + (*it).size);
     }
     }
     return &*it;
     return &*it;
 }
 }
@@ -676,7 +668,7 @@ template <typename T> inline void ConvertDispatcher(T& out, const Structure& in,
         out = static_cast<T>(db.reader->GetF8());
         out = static_cast<T>(db.reader->GetF8());
     }
     }
     else {
     else {
-        throw DeadlyImportError("Unknown source for conversion to primitive data type: "+in.name);
+        throw DeadlyImportError("Unknown source for conversion to primitive data type: ", in.name);
     }
     }
 }
 }
 
 
@@ -784,9 +776,7 @@ const Structure& DNA :: operator [] (const std::string& ss) const
 {
 {
     std::map<std::string, size_t>::const_iterator it = indices.find(ss);
     std::map<std::string, size_t>::const_iterator it = indices.find(ss);
     if (it == indices.end()) {
     if (it == indices.end()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: Did not find a structure named `",ss,"`"
-            ));
+        throw Error("BlendDNA: Did not find a structure named `",ss,"`");
     }
     }
 
 
     return structures[(*it).second];
     return structures[(*it).second];
@@ -803,9 +793,7 @@ const Structure* DNA :: Get (const std::string& ss) const
 const Structure& DNA :: operator [] (const size_t i) const
 const Structure& DNA :: operator [] (const size_t i) const
 {
 {
     if (i >= structures.size()) {
     if (i >= structures.size()) {
-        throw Error((Formatter::format(),
-            "BlendDNA: There is no structure with index `",i,"`"
-            ));
+        throw Error("BlendDNA: There is no structure with index `",i,"`");
     }
     }
 
 
     return structures[i];
     return structures[i];

+ 2 - 3
code/AssetLib/Blender/BlenderLoader.cpp

@@ -748,9 +748,8 @@ void BlenderImporter::BuildMaterials(ConversionData &conv_data) {
 void BlenderImporter::CheckActualType(const ElemBase *dt, const char *check) {
 void BlenderImporter::CheckActualType(const ElemBase *dt, const char *check) {
     ai_assert(dt);
     ai_assert(dt);
     if (strcmp(dt->dna_type, check)) {
     if (strcmp(dt->dna_type, check)) {
-        ThrowException((format(),
-                "Expected object at ", std::hex, dt, " to be of type `", check,
-                "`, but it claims to be a `", dt->dna_type, "`instead"));
+        ThrowException("Expected object at ", std::hex, dt, " to be of type `", check,
+                "`, but it claims to be a `", dt->dna_type, "`instead");
     }
     }
 }
 }
 
 

+ 7 - 1
code/AssetLib/Blender/BlenderTessellator.cpp

@@ -386,7 +386,14 @@ void BlenderTessellatorP2T::ReferencePoints( std::vector< Blender::PointP2T >& p
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 inline PointP2T& BlenderTessellatorP2T::GetActualPointStructure( p2t::Point& point ) const
 inline PointP2T& BlenderTessellatorP2T::GetActualPointStructure( p2t::Point& point ) const
 {
 {
+#if defined __clang__
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Winvalid-offsetof"
+#endif // __clang__
     unsigned int pointOffset = offsetof( PointP2T, point2D );
     unsigned int pointOffset = offsetof( PointP2T, point2D );
+#if defined __clang__
+#    pragma clang diagnostic pop
+#endif
     PointP2T& pointStruct = *reinterpret_cast< PointP2T* >( reinterpret_cast< char* >( &point ) - pointOffset );
     PointP2T& pointStruct = *reinterpret_cast< PointP2T* >( reinterpret_cast< char* >( &point ) - pointOffset );
     if ( pointStruct.magic != static_cast<int>( BLEND_TESS_MAGIC ) )
     if ( pointStruct.magic != static_cast<int>( BLEND_TESS_MAGIC ) )
     {
     {
@@ -394,7 +401,6 @@ inline PointP2T& BlenderTessellatorP2T::GetActualPointStructure( p2t::Point& poi
     }
     }
     return pointStruct;
     return pointStruct;
 }
 }
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void BlenderTessellatorP2T::MakeFacesFromTriangles( std::vector< p2t::Triangle* >& triangles ) const
 void BlenderTessellatorP2T::MakeFacesFromTriangles( std::vector< p2t::Triangle* >& triangles ) const
 {
 {

+ 1 - 1
code/AssetLib/COB/COBLoader.cpp

@@ -125,7 +125,7 @@ void COBImporter::SetupProperties(const Importer * /*pImp*/) {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 /*static*/ AI_WONT_RETURN void COBImporter::ThrowException(const std::string &msg) {
 /*static*/ AI_WONT_RETURN void COBImporter::ThrowException(const std::string &msg) {
-    throw DeadlyImportError("COB: " + msg);
+    throw DeadlyImportError("COB: ", msg);
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 1 - 1
code/AssetLib/CSM/CSMLoader.cpp

@@ -128,7 +128,7 @@ void CSMImporter::InternReadFile( const std::string& pFile,
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if( file.get() == nullptr) {
     if( file.get() == nullptr) {
-        throw DeadlyImportError( "Failed to open CSM file " + pFile + ".");
+        throw DeadlyImportError( "Failed to open CSM file ", pFile, ".");
     }
     }
 
 
     // allocate storage and copy the contents of the file to a memory buffer
     // allocate storage and copy the contents of the file to a memory buffer

+ 2 - 2
code/AssetLib/Collada/ColladaLoader.cpp

@@ -1245,7 +1245,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
 
 
             // time count and value count must match
             // time count and value count must match
             if (e.mTimeAccessor->mCount != e.mValueAccessor->mCount)
             if (e.mTimeAccessor->mCount != e.mValueAccessor->mCount)
-                throw DeadlyImportError(format() << "Time count / value count mismatch in animation channel \"" << e.mChannel->mTarget << "\".");
+                throw DeadlyImportError("Time count / value count mismatch in animation channel \"", e.mChannel->mTarget, "\".");
 
 
             if (e.mTimeAccessor->mCount > 0) {
             if (e.mTimeAccessor->mCount > 0) {
                 // find bounding times
                 // find bounding times
@@ -1462,7 +1462,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse
         for (size_t a = 0; a < morphAnims.size(); ++a) {
         for (size_t a = 0; a < morphAnims.size(); ++a) {
             anim->mDuration = std::max(anim->mDuration, morphAnims[a]->mKeys[morphAnims[a]->mNumKeys - 1].mTime);
             anim->mDuration = std::max(anim->mDuration, morphAnims[a]->mKeys[morphAnims[a]->mNumKeys - 1].mTime);
         }
         }
-        anim->mTicksPerSecond = 1;
+        anim->mTicksPerSecond = 1000.0;
         mAnims.push_back(anim);
         mAnims.push_back(anim);
     }
     }
 }
 }

+ 64 - 60
code/AssetLib/Collada/ColladaParser.cpp

@@ -101,24 +101,24 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) :
         std::string dae_filename = ReadZaeManifest(*zip_archive);
         std::string dae_filename = ReadZaeManifest(*zip_archive);
 
 
         if (dae_filename.empty()) {
         if (dae_filename.empty()) {
-            ThrowException(std::string("Invalid ZAE"));
+            throw DeadlyImportError("Invalid ZAE");
         }
         }
 
 
         daefile.reset(zip_archive->Open(dae_filename.c_str()));
         daefile.reset(zip_archive->Open(dae_filename.c_str()));
         if (daefile == nullptr) {
         if (daefile == nullptr) {
-            ThrowException(std::string("Invalid ZAE manifest: '") + std::string(dae_filename) + std::string("' is missing"));
+            throw DeadlyImportError("Invalid ZAE manifest: '", dae_filename, "' is missing");
         }
         }
     } else {
     } else {
         // attempt to open the file directly
         // attempt to open the file directly
         daefile.reset(pIOHandler->Open(pFile));
         daefile.reset(pIOHandler->Open(pFile));
         if (daefile.get() == nullptr) {
         if (daefile.get() == nullptr) {
-            throw DeadlyImportError("Failed to open file '" + pFile + "'.");
+            throw DeadlyImportError("Failed to open file '", pFile, "'.");
         }
         }
     }
     }
 
 
     // generate a XML reader for it
     // generate a XML reader for it
     if (!mXmlParser.parse(daefile.get())) {
     if (!mXmlParser.parse(daefile.get())) {
-        ThrowException("Unable to read file, malformed XML");
+        throw DeadlyImportError("Unable to read file, malformed XML");
     }
     }
     // start reading
     // start reading
     XmlNode node = mXmlParser.getRootNode();
     XmlNode node = mXmlParser.getRootNode();
@@ -391,7 +391,7 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) {
             if (url) {
             if (url) {
                 const std::string urlName = url.as_string();
                 const std::string urlName = url.as_string();
                 if (urlName[0] != '#') {
                 if (urlName[0] != '#') {
-                    ThrowException("Unknown reference format");
+                    throw DeadlyImportError("Unknown reference format");
                 }
                 }
                 clip.second.push_back(url.as_string());
                 clip.second.push_back(url.as_string());
             }
             }
@@ -430,7 +430,6 @@ void ColladaParser::PostProcessRootAnimations() {
     }
     }
 
 
     Animation temp;
     Animation temp;
-
     for (AnimationClipLibrary::iterator it = mAnimationClipLibrary.begin(); it != mAnimationClipLibrary.end(); ++it) {
     for (AnimationClipLibrary::iterator it = mAnimationClipLibrary.begin(); it != mAnimationClipLibrary.end(); ++it) {
         std::string clipName = it->first;
         std::string clipName = it->first;
 
 
@@ -568,8 +567,9 @@ void ColladaParser::ReadAnimationSampler(XmlNode &node, Collada::AnimationChanne
                 if (XmlParser::hasAttribute(currentNode, "source")) {
                 if (XmlParser::hasAttribute(currentNode, "source")) {
                     XmlParser::getStdStrAttribute(currentNode, "source", sourceAttr);
                     XmlParser::getStdStrAttribute(currentNode, "source", sourceAttr);
                     const char *source = sourceAttr.c_str();
                     const char *source = sourceAttr.c_str();
-                    if (source[0] != '#')
-                        ThrowException("Unsupported URL format");
+                    if (source[0] != '#') {
+                        throw DeadlyImportError("Unsupported URL format");
+                    }
                     source++;
                     source++;
 
 
                     if (semantic == "INPUT")
                     if (semantic == "INPUT")
@@ -671,7 +671,7 @@ void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pCo
             const char *attrSemantic = currentNode.attribute("semantic").as_string();
             const char *attrSemantic = currentNode.attribute("semantic").as_string();
             const char *attrSource = currentNode.attribute("source").as_string();
             const char *attrSource = currentNode.attribute("source").as_string();
             if (attrSource[0] != '#') {
             if (attrSource[0] != '#') {
-                ThrowException(format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <joints> data <input> element");
+                throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of <joints> data <input> element");
             }
             }
             ++attrSource;
             ++attrSource;
             // parse source URL to corresponding source
             // parse source URL to corresponding source
@@ -680,7 +680,7 @@ void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pCo
             } else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0) {
             } else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0) {
                 pController.mJointOffsetMatrixSource = attrSource;
                 pController.mJointOffsetMatrixSource = attrSource;
             } else {
             } else {
-                ThrowException(format() << "Unknown semantic \"" << attrSemantic << "\" in <joints> data <input> element");
+                throw DeadlyImportError("Unknown semantic \"" , attrSemantic , "\" in <joints> data <input> element");
             }
             }
         }
         }
     }
     }
@@ -704,7 +704,7 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC
 
 
             // local URLS always start with a '#'. We don't support global URLs
             // local URLS always start with a '#'. We don't support global URLs
             if (attrSource[0] != '#') {
             if (attrSource[0] != '#') {
-                ThrowException(format() << "Unsupported URL format in \"" << attrSource << "\" in source attribute of <vertex_weights> data <input> element");
+                throw DeadlyImportError( "Unsupported URL format in \"", attrSource, "\" in source attribute of <vertex_weights> data <input> element");
             }
             }
             channel.mAccessor = attrSource + 1;
             channel.mAccessor = attrSource + 1;
 
 
@@ -714,14 +714,14 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC
             } else if (strcmp(attrSemantic, "WEIGHT") == 0) {
             } else if (strcmp(attrSemantic, "WEIGHT") == 0) {
                 pController.mWeightInputWeights = channel;
                 pController.mWeightInputWeights = channel;
             } else {
             } else {
-                ThrowException(format() << "Unknown semantic \"" << attrSemantic << "\" in <vertex_weights> data <input> element");
+                throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in <vertex_weights> data <input> element");
             }
             }
         } else if (currentName == "vcount" && vertexCount > 0) {
         } else if (currentName == "vcount" && vertexCount > 0) {
             const char *text = currentNode.value();
             const char *text = currentNode.value();
             size_t numWeights = 0;
             size_t numWeights = 0;
             for (std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) {
             for (std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it) {
                 if (*text == 0) {
                 if (*text == 0) {
-                    ThrowException("Out of data while reading <vcount>");
+                    throw DeadlyImportError("Out of data while reading <vcount>");
                 }
                 }
 
 
                 *it = strtoul10(text, &text);
                 *it = strtoul10(text, &text);
@@ -737,12 +737,13 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC
             const char *text = stdText.c_str();
             const char *text = stdText.c_str();
             for (std::vector<std::pair<size_t, size_t>>::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) {
             for (std::vector<std::pair<size_t, size_t>>::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it) {
                 if (text == 0) {
                 if (text == 0) {
-                    ThrowException("Out of data while reading <vertex_weights>");
+                    throw DeadlyImportError("Out of data while reading <vertex_weights>");
                 }
                 }
                 it->first = strtoul10(text, &text);
                 it->first = strtoul10(text, &text);
                 SkipSpacesAndLineEnd(&text);
                 SkipSpacesAndLineEnd(&text);
-                if (*text == 0)
-                    ThrowException("Out of data while reading <vertex_weights>");
+                if (*text == 0) {
+                    throw DeadlyImportError("Out of data while reading <vertex_weights>");
+                }
                 it->second = strtoul10(text, &text);
                 it->second = strtoul10(text, &text);
                 SkipSpacesAndLineEnd(&text);
                 SkipSpacesAndLineEnd(&text);
             }
             }
@@ -925,7 +926,7 @@ void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) {
         if (currentName == "instance_effect") {
         if (currentName == "instance_effect") {
             const char *url = currentNode.attribute("url").as_string();
             const char *url = currentNode.attribute("url").as_string();
             if (url[0] != '#') {
             if (url[0] != '#') {
-                ThrowException("Unknown reference format");
+                throw DeadlyImportError("Unknown reference format");
             }
             }
             pMaterial.mEffect = url + 1;
             pMaterial.mEffect = url + 1;
         }
         }
@@ -1300,8 +1301,9 @@ void ColladaParser::ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam)
             // surface ID is given inside <instance_image> tags
             // surface ID is given inside <instance_image> tags
             std::string url;
             std::string url;
             XmlParser::getStdStrAttribute(currentNode, "url", url);
             XmlParser::getStdStrAttribute(currentNode, "url", url);
-            if (url[0] != '#')
-                ThrowException("Unsupported URL format in instance_image");
+            if (url[0] != '#') {
+                throw DeadlyImportError("Unsupported URL format in instance_image");
+            }
             pParam.mType = Param_Sampler;
             pParam.mType = Param_Sampler;
             pParam.mReference = url.c_str() + 1;
             pParam.mReference = url.c_str() + 1;
         }
         }
@@ -1426,8 +1428,9 @@ void ColladaParser::ReadDataArray(XmlNode &node) {
             std::string s;
             std::string s;
 
 
             for (unsigned int a = 0; a < count; a++) {
             for (unsigned int a = 0; a < count; a++) {
-                if (*content == 0)
-                    ThrowException("Expected more values while reading IDREF_array contents.");
+                if (*content == 0) {
+                    throw DeadlyImportError("Expected more values while reading IDREF_array contents.");
+                }
 
 
                 s.clear();
                 s.clear();
                 while (!IsSpaceOrNewLine(*content))
                 while (!IsSpaceOrNewLine(*content))
@@ -1440,8 +1443,9 @@ void ColladaParser::ReadDataArray(XmlNode &node) {
             data.mValues.reserve(count);
             data.mValues.reserve(count);
 
 
             for (unsigned int a = 0; a < count; a++) {
             for (unsigned int a = 0; a < count; a++) {
-                if (*content == 0)
-                    ThrowException("Expected more values while reading float_array contents.");
+                if (*content == 0) {
+                    throw DeadlyImportError("Expected more values while reading float_array contents.");
+                }
 
 
                 ai_real value;
                 ai_real value;
                 // read a number
                 // read a number
@@ -1461,9 +1465,10 @@ void ColladaParser::ReadAccessor(XmlNode &node, const std::string &pID) {
     // read accessor attributes
     // read accessor attributes
     std::string source;
     std::string source;
     XmlParser::getStdStrAttribute(node, "source", source);
     XmlParser::getStdStrAttribute(node, "source", source);
-    if (source[0] != '#')
-        ThrowException(format() << "Unknown reference format in url \"" << source << "\" in source attribute of <accessor> element.");
-    int count;
+    if (source[0] != '#') {
+        throw DeadlyImportError("Unknown reference format in url \"", source, "\" in source attribute of <accessor> element.");
+    }
+    int count = 0;
     XmlParser::getIntAttribute(node, "count", count);
     XmlParser::getIntAttribute(node, "count", count);
 
 
     unsigned int offset = 0;
     unsigned int offset = 0;
@@ -1561,7 +1566,7 @@ void ColladaParser::ReadVertexData(XmlNode &node, Mesh &pMesh) {
         if (currentName == "input") {
         if (currentName == "input") {
             ReadInputChannel(currentNode, pMesh.mPerVertexData);
             ReadInputChannel(currentNode, pMesh.mPerVertexData);
         } else {
         } else {
-            ThrowException(format() << "Unexpected sub element <" << currentName << "> in tag <vertices>");
+            throw DeadlyImportError("Unexpected sub element <", currentName, "> in tag <vertices>");
         }
         }
     }
     }
 }
 }
@@ -1628,8 +1633,9 @@ void ColladaParser::ReadIndexData(XmlNode &node, Mesh &pMesh) {
                     const char *content = v.c_str();
                     const char *content = v.c_str();
                     vcount.reserve(numPrimitives);
                     vcount.reserve(numPrimitives);
                     for (unsigned int a = 0; a < numPrimitives; a++) {
                     for (unsigned int a = 0; a < numPrimitives; a++) {
-                        if (*content == 0)
-                            ThrowException("Expected more values while reading <vcount> contents.");
+                        if (*content == 0) {
+                            throw DeadlyImportError("Expected more values while reading <vcount> contents.");
+                        }
                         // read a number
                         // read a number
                         vcount.push_back((size_t)strtoul10(content, &content));
                         vcount.push_back((size_t)strtoul10(content, &content));
                         // skip whitespace after it
                         // skip whitespace after it
@@ -1647,7 +1653,7 @@ void ColladaParser::ReadIndexData(XmlNode &node, Mesh &pMesh) {
         } else if (currentName == "ph") {
         } else if (currentName == "ph") {
             // skip
             // skip
         } else {
         } else {
-            ThrowException(format() << "Unexpected sub element <" << currentName << "> in tag <" << elementName << ">");
+            throw DeadlyImportError("Unexpected sub element <", currentName, "> in tag <", elementName, ">");
         }
         }
     }
     }
 
 
@@ -1671,17 +1677,14 @@ void ColladaParser::ReadInputChannel(XmlNode &node, std::vector<InputChannel> &p
     // read semantic
     // read semantic
     std::string semantic;
     std::string semantic;
     XmlParser::getStdStrAttribute(node, "semantic", semantic);
     XmlParser::getStdStrAttribute(node, "semantic", semantic);
-    //int attrSemantic = GetAttribute("semantic");
-    //std::string semantic = mReader->getAttributeValue(attrSemantic);
     channel.mType = GetTypeForSemantic(semantic);
     channel.mType = GetTypeForSemantic(semantic);
 
 
     // read source
     // read source
     std::string source;
     std::string source;
     XmlParser::getStdStrAttribute(node, "source", source);
     XmlParser::getStdStrAttribute(node, "source", source);
-    //int attrSource = GetAttribute("source");
-    //const char *source = mReader->getAttributeValue(attrSource);
-    if (source[0] != '#')
-        ThrowException(format() << "Unknown reference format in url \"" << source << "\" in source attribute of <input> element.");
+    if (source[0] != '#') {
+        throw DeadlyImportError("Unknown reference format in url \"", source, "\" in source attribute of <input> element.");
+    }
     channel.mAccessor = source.c_str() + 1; // skipping the leading #, hopefully the remaining text is the accessor ID only
     channel.mAccessor = source.c_str() + 1; // skipping the leading #, hopefully the remaining text is the accessor ID only
 
 
     // read index offset, if per-index <input>
     // read index offset, if per-index <input>
@@ -1763,11 +1766,13 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
             // HACK: We just fix this number since SketchUp 15.3.331 writes the wrong 'count' for 'lines'
             // HACK: We just fix this number since SketchUp 15.3.331 writes the wrong 'count' for 'lines'
             ReportWarning("Expected different index count in <p> element, %zu instead of %zu.", indices.size(), expectedPointCount * numOffsets);
             ReportWarning("Expected different index count in <p> element, %zu instead of %zu.", indices.size(), expectedPointCount * numOffsets);
             pNumPrimitives = (indices.size() / numOffsets) / 2;
             pNumPrimitives = (indices.size() / numOffsets) / 2;
-        } else
-            ThrowException("Expected different index count in <p> element.");
+        } else {
+            throw DeadlyImportError("Expected different index count in <p> element.");
+        }
 
 
-    } else if (expectedPointCount == 0 && (indices.size() % numOffsets) != 0)
-        ThrowException("Expected different index count in <p> element.");
+    } else if (expectedPointCount == 0 && (indices.size() % numOffsets) != 0) {
+        throw DeadlyImportError("Expected different index count in <p> element.");
+    }
 
 
     // find the data for all sources
     // find the data for all sources
     for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
     for (std::vector<InputChannel>::iterator it = pMesh.mPerVertexData.begin(); it != pMesh.mPerVertexData.end(); ++it) {
@@ -1791,8 +1796,9 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
         // ignore vertex pointer, it doesn't refer to an accessor
         // ignore vertex pointer, it doesn't refer to an accessor
         if (input.mType == IT_Vertex) {
         if (input.mType == IT_Vertex) {
             // warn if the vertex channel does not refer to the <vertices> element in the same mesh
             // warn if the vertex channel does not refer to the <vertices> element in the same mesh
-            if (input.mAccessor != pMesh.mVertexID)
-                ThrowException("Unsupported vertex referencing scheme.");
+            if (input.mAccessor != pMesh.mVertexID) {
+                throw DeadlyImportError("Unsupported vertex referencing scheme.");
+            }
             continue;
             continue;
         }
         }
 
 
@@ -1859,7 +1865,7 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector<Inp
             break;
             break;
         default:
         default:
             // LineStrip is not supported due to expected index unmangling
             // LineStrip is not supported due to expected index unmangling
-            ThrowException("Unsupported primitive type.");
+            throw DeadlyImportError("Unsupported primitive type.");
             break;
             break;
         }
         }
 
 
@@ -1915,8 +1921,9 @@ void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, siz
         return;
         return;
 
 
     const Accessor &acc = *pInput.mResolved;
     const Accessor &acc = *pInput.mResolved;
-    if (pLocalIndex >= acc.mCount)
-        ThrowException(format() << "Invalid data index (" << pLocalIndex << "/" << acc.mCount << ") in primitive specification");
+    if (pLocalIndex >= acc.mCount) {
+        throw DeadlyImportError("Invalid data index (", pLocalIndex, "/", acc.mCount, ") in primitive specification");
+    }
 
 
     // get a pointer to the start of the data object referred to by the accessor and the local index
     // get a pointer to the start of the data object referred to by the accessor and the local index
     const ai_real *dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex * acc.mStride;
     const ai_real *dataObject = &(acc.mData->mValues[0]) + acc.mOffset + pLocalIndex * acc.mStride;
@@ -2121,8 +2128,9 @@ void ColladaParser::ReadSceneNode(XmlNode &node, Node *pNode) {
             if (XmlParser::hasAttribute(currentNode, "url")) {
             if (XmlParser::hasAttribute(currentNode, "url")) {
                 std::string url;
                 std::string url;
                 XmlParser::getStdStrAttribute(currentNode, "url", url);
                 XmlParser::getStdStrAttribute(currentNode, "url", url);
-                if (url[0] != '#')
-                    ThrowException("Unknown reference format in <instance_light> element");
+                if (url[0] != '#') {
+                    throw DeadlyImportError("Unknown reference format in <instance_light> element");
+                }
 
 
                 pNode->mLights.push_back(LightInstance());
                 pNode->mLights.push_back(LightInstance());
                 pNode->mLights.back().mLight = url.c_str() + 1;
                 pNode->mLights.back().mLight = url.c_str() + 1;
@@ -2133,7 +2141,7 @@ void ColladaParser::ReadSceneNode(XmlNode &node, Node *pNode) {
                 std::string url;
                 std::string url;
                 XmlParser::getStdStrAttribute(currentNode, "url", url);
                 XmlParser::getStdStrAttribute(currentNode, "url", url);
                 if (url[0] != '#') {
                 if (url[0] != '#') {
-                    ThrowException("Unknown reference format in <instance_camera> element");
+                    throw DeadlyImportError("Unknown reference format in <instance_camera> element");
                 }
                 }
                 pNode->mCameras.push_back(CameraInstance());
                 pNode->mCameras.push_back(CameraInstance());
                 pNode->mCameras.back().mCamera = url.c_str() + 1;
                 pNode->mCameras.back().mCamera = url.c_str() + 1;
@@ -2237,8 +2245,9 @@ void ColladaParser::ReadNodeGeometry(XmlNode &node, Node *pNode) {
     // referred mesh is given as an attribute of the <instance_geometry> element
     // referred mesh is given as an attribute of the <instance_geometry> element
     std::string url;
     std::string url;
     XmlParser::getStdStrAttribute(node, "url", url);
     XmlParser::getStdStrAttribute(node, "url", url);
-    if (url[0] != '#')
-        ThrowException("Unknown reference format");
+    if (url[0] != '#') {
+        throw DeadlyImportError("Unknown reference format");
+    }
 
 
     Collada::MeshInstance instance;
     Collada::MeshInstance instance;
     instance.mMeshOrController = url.c_str() + 1; // skipping the leading #
     instance.mMeshOrController = url.c_str() + 1; // skipping the leading #
@@ -2277,32 +2286,27 @@ void ColladaParser::ReadScene(XmlNode &node) {
         const std::string currentName = currentNode.name();
         const std::string currentName = currentNode.name();
         if (currentName == "instance_visual_scene") {
         if (currentName == "instance_visual_scene") {
             // should be the first and only occurrence
             // should be the first and only occurrence
-            if (mRootNode)
-                ThrowException("Invalid scene containing multiple root nodes in <instance_visual_scene> element");
+            if (mRootNode) {
+                throw DeadlyImportError("Invalid scene containing multiple root nodes in <instance_visual_scene> element");
+            }
 
 
             // read the url of the scene to instance. Should be of format "#some_name"
             // read the url of the scene to instance. Should be of format "#some_name"
             std::string url;
             std::string url;
             XmlParser::getStdStrAttribute(currentNode, "url", url);
             XmlParser::getStdStrAttribute(currentNode, "url", url);
             if (url[0] != '#') {
             if (url[0] != '#') {
-                ThrowException("Unknown reference format in <instance_visual_scene> element");
+                throw DeadlyImportError("Unknown reference format in <instance_visual_scene> element");
             }
             }
 
 
             // find the referred scene, skip the leading #
             // find the referred scene, skip the leading #
             NodeLibrary::const_iterator sit = mNodeLibrary.find(url.c_str() + 1);
             NodeLibrary::const_iterator sit = mNodeLibrary.find(url.c_str() + 1);
             if (sit == mNodeLibrary.end()) {
             if (sit == mNodeLibrary.end()) {
-                ThrowException("Unable to resolve visual_scene reference \"" + std::string(url) + "\" in <instance_visual_scene> element.");
+                throw DeadlyImportError("Unable to resolve visual_scene reference \"", std::string(url), "\" in <instance_visual_scene> element.");
             }
             }
             mRootNode = sit->second;
             mRootNode = sit->second;
         }
         }
     }
     }
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-// Aborts the file reading with an exception
-AI_WONT_RETURN void ColladaParser::ThrowException(const std::string &pError) const {
-    throw DeadlyImportError(format() << "Collada: " << mFileName << " - " << pError);
-}
-
 void ColladaParser::ReportWarning(const char *msg, ...) {
 void ColladaParser::ReportWarning(const char *msg, ...) {
     ai_assert(nullptr != msg);
     ai_assert(nullptr != msg);
 
 

+ 3 - 4
code/AssetLib/Collada/ColladaParser.h

@@ -243,8 +243,6 @@ protected:
     void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive);
     void ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive);
 
 
 protected:
 protected:
-    /** Aborts the file reading with an exception */
-    AI_WONT_RETURN void ThrowException(const std::string &pError) const AI_WONT_RETURN_SUFFIX;
     void ReportWarning(const char *msg, ...);
     void ReportWarning(const char *msg, ...);
 
 
     /** Calculates the resulting transformation from all the given transform steps */
     /** Calculates the resulting transformation from all the given transform steps */
@@ -348,8 +346,9 @@ protected:
 template <typename Type>
 template <typename Type>
 const Type &ColladaParser::ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const {
 const Type &ColladaParser::ResolveLibraryReference(const std::map<std::string, Type> &pLibrary, const std::string &pURL) const {
     typename std::map<std::string, Type>::const_iterator it = pLibrary.find(pURL);
     typename std::map<std::string, Type>::const_iterator it = pLibrary.find(pURL);
-    if (it == pLibrary.end())
-        ThrowException(Formatter::format() << "Unable to resolve library reference \"" << pURL << "\".");
+    if (it == pLibrary.end()) {
+        throw DeadlyImportError("Unable to resolve library reference \"", pURL, "\".");
+    }
     return it->second;
     return it->second;
 }
 }
 
 

+ 1 - 1
code/AssetLib/DXF/DXFLoader.cpp

@@ -152,7 +152,7 @@ void DXFImporter::InternReadFile( const std::string& filename, aiScene* pScene,
 
 
     // Check whether we can read the file
     // Check whether we can read the file
     if( file.get() == nullptr ) {
     if( file.get() == nullptr ) {
-        throw DeadlyImportError( "Failed to open DXF file " + filename + "");
+        throw DeadlyImportError( "Failed to open DXF file ", filename, "");
     }
     }
 
 
     // Check whether this is a binary DXF file - we can't read binary DXF files :-(
     // Check whether this is a binary DXF file - we can't read binary DXF files :-(

+ 15 - 4
code/AssetLib/FBX/FBXBinaryTokenizer.cpp

@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/Exceptional.h>
 #include <assimp/Exceptional.h>
 #include <assimp/ByteSwapper.h>
 #include <assimp/ByteSwapper.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/DefaultLogger.hpp>
+#include <assimp/StringUtils.h>
 
 
 namespace Assimp {
 namespace Assimp {
 namespace FBX {
 namespace FBX {
@@ -126,7 +127,7 @@ namespace {
 AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset) AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset) AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset)
 AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset)
 {
 {
-    throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset));
+    throw DeadlyImportError("FBX-Tokenize", Util::GetOffsetText(offset), message);
 }
 }
 
 
 
 
@@ -456,10 +457,20 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
 	ASSIMP_LOG_DEBUG_F("FBX version: ", version);
 	ASSIMP_LOG_DEBUG_F("FBX version: ", version);
 	const bool is64bits = version >= 7500;
 	const bool is64bits = version >= 7500;
     const char *end = input + length;
     const char *end = input + length;
-    while (cursor < end ) {
-		if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
-            break;
+    try
+    {
+        while (cursor < end ) {
+		    if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
+                break;
+            }
+        }
+    }
+    catch (const DeadlyImportError& e)
+    {
+        if (!is64bits && (length > std::numeric_limits<std::uint32_t>::max())) {
+            throw DeadlyImportError("The FBX file is invalid. This may be because the content is too big for this older version (", to_string(version), ") of the FBX format. (", e.what(), ")");
         }
         }
+        throw;
     }
     }
 }
 }
 
 

+ 1 - 0
code/AssetLib/FBX/FBXConverter.cpp

@@ -1988,6 +1988,7 @@ void FBXConverter::SetTextureProperties(aiMaterial *out_mat, const TextureMap &_
     TrySetTextureProperties(out_mat, _textures, "ShininessExponent", aiTextureType_SHININESS, mesh);
     TrySetTextureProperties(out_mat, _textures, "ShininessExponent", aiTextureType_SHININESS, mesh);
     TrySetTextureProperties(out_mat, _textures, "TransparencyFactor", aiTextureType_OPACITY, mesh);
     TrySetTextureProperties(out_mat, _textures, "TransparencyFactor", aiTextureType_OPACITY, mesh);
     TrySetTextureProperties(out_mat, _textures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh);
     TrySetTextureProperties(out_mat, _textures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh);
+    TrySetTextureProperties(out_mat, _textures, "ReflectionFactor", aiTextureType_METALNESS, mesh);
     //Maya counterparts
     //Maya counterparts
     TrySetTextureProperties(out_mat, _textures, "Maya|DiffuseTexture", aiTextureType_DIFFUSE, mesh);
     TrySetTextureProperties(out_mat, _textures, "Maya|DiffuseTexture", aiTextureType_DIFFUSE, mesh);
     TrySetTextureProperties(out_mat, _textures, "Maya|NormalTexture", aiTextureType_NORMALS, mesh);
     TrySetTextureProperties(out_mat, _textures, "Maya|NormalTexture", aiTextureType_NORMALS, mesh);

+ 3 - 3
code/AssetLib/FBX/FBXDocumentUtil.cpp

@@ -61,7 +61,7 @@ namespace Util {
 // signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError.
 // signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError.
 void DOMError(const std::string& message, const Token& token)
 void DOMError(const std::string& message, const Token& token)
 {
 {
-    throw DeadlyImportError(Util::AddTokenText("FBX-DOM",message,&token));
+    throw DeadlyImportError("FBX-DOM", Util::GetTokenText(&token), message);
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -70,7 +70,7 @@ void DOMError(const std::string& message, const Element* element /*= nullptr*/)
     if(element) {
     if(element) {
         DOMError(message,element->KeyToken());
         DOMError(message,element->KeyToken());
     }
     }
-    throw DeadlyImportError("FBX-DOM " + message);
+    throw DeadlyImportError("FBX-DOM ", message);
 }
 }
 
 
 
 
@@ -79,7 +79,7 @@ void DOMError(const std::string& message, const Element* element /*= nullptr*/)
 void DOMWarning(const std::string& message, const Token& token)
 void DOMWarning(const std::string& message, const Token& token)
 {
 {
     if(DefaultLogger::get()) {
     if(DefaultLogger::get()) {
-        ASSIMP_LOG_WARN(Util::AddTokenText("FBX-DOM",message,&token));
+        ASSIMP_LOG_WARN_F("FBX-DOM", Util::GetTokenText(&token), message);
     }
     }
 }
 }
 
 

+ 76 - 17
code/AssetLib/FBX/FBXExporter.cpp

@@ -400,6 +400,65 @@ void FBXExporter::WriteHeaderExtension ()
     );
     );
 }
 }
 
 
+// WriteGlobalSettings helpers
+
+void WritePropInt(const aiScene* scene, FBX::Node& p, const std::string& key, int defaultValue)
+{
+    int value;
+    if (scene->mMetaData != nullptr && scene->mMetaData->Get(key, value)) {
+        p.AddP70int(key, value);
+    } else {
+        p.AddP70int(key, defaultValue);
+    }
+}
+
+void WritePropDouble(const aiScene* scene, FBX::Node& p, const std::string& key, double defaultValue)
+{
+    double value;
+    if (scene->mMetaData != nullptr && scene->mMetaData->Get(key, value)) {
+        p.AddP70double(key, value);
+    } else {
+        // fallback lookup float instead
+        float floatValue;
+        if (scene->mMetaData != nullptr && scene->mMetaData->Get(key, floatValue)) {
+            p.AddP70double(key, (double)floatValue);
+        } else {
+            p.AddP70double(key, defaultValue);
+        }
+    }
+}
+
+void WritePropEnum(const aiScene* scene, FBX::Node& p, const std::string& key, int defaultValue)
+{
+    int value;
+    if (scene->mMetaData != nullptr && scene->mMetaData->Get(key, value)) {
+        p.AddP70enum(key, value);
+    } else {
+        p.AddP70enum(key, defaultValue);
+    }
+}
+
+void WritePropColor(const aiScene* scene, FBX::Node& p, const std::string& key, const aiVector3D& defaultValue)
+{
+    aiVector3D value;
+    if (scene->mMetaData != nullptr && scene->mMetaData->Get(key, value)) {
+        // ai_real can be float or double, cast to avoid warnings
+        p.AddP70color(key, (double)value.x, (double)value.y, (double)value.z);
+    } else {
+        p.AddP70color(key, (double)defaultValue.x, (double)defaultValue.y, (double)defaultValue.z);
+    }
+}
+
+void WritePropString(const aiScene* scene, FBX::Node& p, const std::string& key, const std::string& defaultValue)
+{
+    aiString value; // MetaData doesn't hold std::string
+    if (scene->mMetaData != nullptr && scene->mMetaData->Get(key, value)) {
+        p.AddP70string(key, value.C_Str());
+    } else {
+        p.AddP70string(key, defaultValue);
+    }
+}
+
 void FBXExporter::WriteGlobalSettings ()
 void FBXExporter::WriteGlobalSettings ()
 {
 {
     if (!binary) {
     if (!binary) {
@@ -409,26 +468,26 @@ void FBXExporter::WriteGlobalSettings ()
     gs.AddChild("Version", int32_t(1000));
     gs.AddChild("Version", int32_t(1000));
 
 
     FBX::Node p("Properties70");
     FBX::Node p("Properties70");
-    p.AddP70int("UpAxis", 1);
-    p.AddP70int("UpAxisSign", 1);
-    p.AddP70int("FrontAxis", 2);
-    p.AddP70int("FrontAxisSign", 1);
-    p.AddP70int("CoordAxis", 0);
-    p.AddP70int("CoordAxisSign", 1);
-    p.AddP70int("OriginalUpAxis", 1);
-    p.AddP70int("OriginalUpAxisSign", 1);
-    p.AddP70double("UnitScaleFactor", 1.0);
-    p.AddP70double("OriginalUnitScaleFactor", 1.0);
-    p.AddP70color("AmbientColor", 0.0, 0.0, 0.0);
-    p.AddP70string("DefaultCamera", "Producer Perspective");
-    p.AddP70enum("TimeMode", 11);
-    p.AddP70enum("TimeProtocol", 2);
-    p.AddP70enum("SnapOnFrameMode", 0);
+    WritePropInt(mScene, p, "UpAxis", 1);
+    WritePropInt(mScene, p, "UpAxisSign", 1);
+    WritePropInt(mScene, p, "FrontAxis", 2);
+    WritePropInt(mScene, p, "FrontAxisSign", 1);
+    WritePropInt(mScene, p, "CoordAxis", 0);
+    WritePropInt(mScene, p, "CoordAxisSign", 1);
+    WritePropInt(mScene, p, "OriginalUpAxis", 1);
+    WritePropInt(mScene, p, "OriginalUpAxisSign", 1);
+    WritePropDouble(mScene, p, "UnitScaleFactor", 1.0);
+    WritePropDouble(mScene, p, "OriginalUnitScaleFactor", 1.0);
+    WritePropColor(mScene, p, "AmbientColor", aiVector3D((ai_real)0.0, (ai_real)0.0, (ai_real)0.0));
+    WritePropString(mScene, p,"DefaultCamera", "Producer Perspective");
+    WritePropEnum(mScene, p, "TimeMode", 11);
+    WritePropEnum(mScene, p, "TimeProtocol", 2);
+    WritePropEnum(mScene, p, "SnapOnFrameMode", 0);
     p.AddP70time("TimeSpanStart", 0); // TODO: animation support
     p.AddP70time("TimeSpanStart", 0); // TODO: animation support
     p.AddP70time("TimeSpanStop", FBX::SECOND); // TODO: animation support
     p.AddP70time("TimeSpanStop", FBX::SECOND); // TODO: animation support
-    p.AddP70double("CustomFrameRate", -1.0);
+    WritePropDouble(mScene, p, "CustomFrameRate", -1.0);
     p.AddP70("TimeMarker", "Compound", "", ""); // not sure what this is
     p.AddP70("TimeMarker", "Compound", "", ""); // not sure what this is
-    p.AddP70int("CurrentTimeMarker", -1);
+    WritePropInt(mScene, p, "CurrentTimeMarker", -1);
     gs.AddChild(p);
     gs.AddChild(p);
 
 
     gs.Dump(outfile, binary, 0);
     gs.Dump(outfile, binary, 0);

+ 4 - 1
code/AssetLib/FBX/FBXImporter.cpp

@@ -141,7 +141,10 @@ void FBXImporter::SetupProperties(const Importer *pImp) {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
 // Imports the given file into the given scene structure.
 void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
 void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
-	std::unique_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
+	auto streamCloser = [&](IOStream *pStream) {
+		pIOHandler->Close(pStream);
+	};
+	std::unique_ptr<IOStream, decltype(streamCloser)> stream(pIOHandler->Open(pFile, "rb"), streamCloser);
 	if (!stream) {
 	if (!stream) {
 		ThrowException("Could not open file for reading");
 		ThrowException("Could not open file for reading");
 	}
 	}

+ 2 - 2
code/AssetLib/FBX/FBXParser.cpp

@@ -73,7 +73,7 @@ namespace {
     AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX;
     AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX;
     AI_WONT_RETURN void ParseError(const std::string& message, const Token& token)
     AI_WONT_RETURN void ParseError(const std::string& message, const Token& token)
     {
     {
-        throw DeadlyImportError(Util::AddTokenText("FBX-Parser",message,&token));
+        throw DeadlyImportError("FBX-Parser", Util::GetTokenText(&token), message);
     }
     }
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
@@ -83,7 +83,7 @@ namespace {
         if(element) {
         if(element) {
             ParseError(message,element->KeyToken());
             ParseError(message,element->KeyToken());
         }
         }
-        throw DeadlyImportError("FBX-Parser " + message);
+        throw DeadlyImportError("FBX-Parser ", message);
     }
     }
 
 
 
 

+ 1 - 1
code/AssetLib/FBX/FBXTokenizer.cpp

@@ -90,7 +90,7 @@ namespace {
 AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column) AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column)
 AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line, unsigned int column)
 {
 {
-    throw DeadlyImportError(Util::AddLineAndColumn("FBX-Tokenize",message,line,column));
+    throw DeadlyImportError("FBX-Tokenize", Util::GetLineAndColumnText(line,column), message);
 }
 }
 
 
 
 

+ 9 - 11
code/AssetLib/FBX/FBXUtil.cpp

@@ -86,32 +86,30 @@ const char* TokenTypeString(TokenType t)
 
 
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-std::string AddOffset(const std::string& prefix, const std::string& text, size_t offset)
+std::string GetOffsetText(size_t offset)
 {
 {
-    return static_cast<std::string>( (Formatter::format() << prefix << " (offset 0x" << std::hex << offset << ") " << text) );
+    return static_cast<std::string>( Formatter::format() << " (offset 0x" << std::hex << offset << ") " );
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column)
+std::string GetLineAndColumnText(unsigned int line, unsigned int column)
 {
 {
-    return static_cast<std::string>( (Formatter::format() << prefix << " (line " << line << " <<  col " << column << ") " << text) );
+    return static_cast<std::string>( Formatter::format() << " (line " << line << " <<  col " << column << ") " );
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok)
+std::string GetTokenText(const Token* tok)
 {
 {
     if(tok->IsBinary()) {
     if(tok->IsBinary()) {
-        return static_cast<std::string>( (Formatter::format() << prefix <<
+        return static_cast<std::string>( Formatter::format() << 
             " (" << TokenTypeString(tok->Type()) <<
             " (" << TokenTypeString(tok->Type()) <<
-            ", offset 0x" << std::hex << tok->Offset() << ") " <<
-            text) );
+            ", offset 0x" << std::hex << tok->Offset() << ") " );
     }
     }
 
 
-    return static_cast<std::string>( (Formatter::format() << prefix <<
+    return static_cast<std::string>( Formatter::format() <<
         " (" << TokenTypeString(tok->Type()) <<
         " (" << TokenTypeString(tok->Type()) <<
         ", line " << tok->Line() <<
         ", line " << tok->Line() <<
-        ", col " << tok->Column() << ") " <<
-        text) );
+        ", col " << tok->Column() << ") " );
 }
 }
 
 
 // Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
 // Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;

+ 7 - 14
code/AssetLib/FBX/FBXUtil.h

@@ -73,31 +73,24 @@ const char* TokenTypeString(TokenType t);
 
 
 /** Format log/error messages using a given offset in the source binary file
 /** Format log/error messages using a given offset in the source binary file
  *
  *
- *  @param prefix Message prefix to be preprended to the location info.
- *  @param text Message text
- *  @param line Line index, 1-based
- *  @param column Column index, 1-based
- *  @return A string of the following format: {prefix} (offset 0x{offset}) {text}*/
-std::string AddOffset(const std::string& prefix, const std::string& text, size_t offset);
+ *  @param offset offset within the file
+ *  @return A string of the following format: " (offset 0x{offset}) "*/
+std::string GetOffsetText(size_t offset);
 
 
 
 
 /** Format log/error messages using a given line location in the source file.
 /** Format log/error messages using a given line location in the source file.
  *
  *
- *  @param prefix Message prefix to be preprended to the location info.
- *  @param text Message text
  *  @param line Line index, 1-based
  *  @param line Line index, 1-based
  *  @param column Column index, 1-based
  *  @param column Column index, 1-based
- *  @return A string of the following format: {prefix} (line {line}, col {column}) {text}*/
-std::string AddLineAndColumn(const std::string& prefix, const std::string& text, unsigned int line, unsigned int column);
+ *  @return A string of the following format: " (line {line}, col {column}) "*/
+std::string GetLineAndColumnText(unsigned int line, unsigned int column);
 
 
 
 
 /** Format log/error messages using a given cursor token.
 /** Format log/error messages using a given cursor token.
  *
  *
- *  @param prefix Message prefix to be preprended to the location info.
- *  @param text Message text
  *  @param tok Token where parsing/processing stopped
  *  @param tok Token where parsing/processing stopped
- *  @return A string of the following format: {prefix} ({token-type}, line {line}, col {column}) {text}*/
-std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok);
+ *  @return A string of the following format: " ({token-type}, line {line}, col {column}) "*/
+std::string GetTokenText(const Token* tok);
 
 
 /** Decode a single Base64-encoded character.
 /** Decode a single Base64-encoded character.
 *
 *

+ 3 - 3
code/AssetLib/HMP/HMPLoader.cpp

@@ -115,7 +115,7 @@ void HMPImporter::InternReadFile(const std::string &pFile,
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open HMP file " + pFile + ".");
+        throw DeadlyImportError("Failed to open HMP file ", pFile, ".");
     }
     }
 
 
     // Check whether the HMP file is large enough to contain
     // Check whether the HMP file is large enough to contain
@@ -159,8 +159,8 @@ void HMPImporter::InternReadFile(const std::string &pFile,
         szBuffer[4] = '\0';
         szBuffer[4] = '\0';
 
 
         // We're definitely unable to load this file
         // We're definitely unable to load this file
-        throw DeadlyImportError("Unknown HMP subformat " + pFile +
-                                ". Magic word (" + szBuffer + ") is not known");
+        throw DeadlyImportError("Unknown HMP subformat ", pFile,
+                                ". Magic word (", szBuffer, ") is not known");
     }
     }
 
 
     // Set the AI_SCENE_FLAGS_TERRAIN bit
     // Set the AI_SCENE_FLAGS_TERRAIN bit

+ 9 - 0
code/AssetLib/IFC/IFCReaderGen1_2x3.cpp

@@ -45,6 +45,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #include "IFCReaderGen_2x3.h"
 #include "IFCReaderGen_2x3.h"
 
 
+#if _MSC_VER
+#    pragma warning(push)
+#    pragma warning(disable : 4702)
+#endif // _MSC_VER
+
 namespace Assimp {
 namespace Assimp {
 
 
 using namespace ::Assimp::IFC;
 using namespace ::Assimp::IFC;
@@ -3165,4 +3170,8 @@ template <> size_t GenericFill<IfcLightSourceDirectional>(const DB& db, const LI
 } // ! STEP
 } // ! STEP
 } // ! Assimp
 } // ! Assimp
 
 
+#if _MSC_VER
+#    pragma warning(pop)
+#endif // _MSC_VER
+
 #endif
 #endif

+ 9 - 0
code/AssetLib/IFC/IFCReaderGen2_2x3.cpp

@@ -43,6 +43,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #include "IFCReaderGen_2x3.h"
 #include "IFCReaderGen_2x3.h"
 
 
+#if _MSC_VER
+#    pragma warning(push)
+#    pragma warning(disable : 4702)
+#endif // _MSC_VER
+
 namespace Assimp {
 namespace Assimp {
 using namespace IFC;
 using namespace IFC;
 using namespace ::Assimp::IFC::Schema_2x3;
 using namespace ::Assimp::IFC::Schema_2x3;
@@ -1915,4 +1920,8 @@ template <> size_t GenericFill<IfcConditionCriterion>(const DB& db, const LIST&
 } // ! STEP
 } // ! STEP
 } // ! Assimp
 } // ! Assimp
 
 
+#if _MSC_VER
+#    pragma warning(pop)
+#endif // _MSC_VER
+
 #endif
 #endif

+ 7 - 2
code/AssetLib/IFC/IFCReaderGen_2x3.h

@@ -45,9 +45,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #include "AssetLib/Step/STEPFile.h"
 #include "AssetLib/Step/STEPFile.h"
 
 
-#ifdef _WIN32
+#ifdef _MSC_VER
+#    pragma warning(push)
 #    pragma warning( disable : 4512 )
 #    pragma warning( disable : 4512 )
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 namespace Assimp {
 namespace Assimp {
 namespace IFC {
 namespace IFC {
@@ -4372,4 +4373,8 @@ namespace STEP {
 } //! STEP
 } //! STEP
 } //! Assimp
 } //! Assimp
 
 
+#ifdef _MSC_VER
+#    pragma warning(pop)
+#endif // _MSC_VER
+
 #endif // INCLUDED_IFC_READER_GEN_H
 #endif // INCLUDED_IFC_READER_GEN_H

+ 3 - 3
code/AssetLib/LWO/LWOLoader.cpp

@@ -145,7 +145,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open LWO file " + pFile + ".");
+        throw DeadlyImportError("Failed to open LWO file ", pFile, ".");
     }
     }
 
 
     if ((this->fileSize = (unsigned int)file->FileSize()) < 12) {
     if ((this->fileSize = (unsigned int)file->FileSize()) < 12) {
@@ -212,7 +212,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
         szBuff[2] = (char)(fileType >> 8u);
         szBuff[2] = (char)(fileType >> 8u);
         szBuff[3] = (char)(fileType);
         szBuff[3] = (char)(fileType);
         szBuff[4] = '\0';
         szBuff[4] = '\0';
-        throw DeadlyImportError(std::string("Unknown LWO sub format: ") + szBuff);
+        throw DeadlyImportError("Unknown LWO sub format: ", szBuff);
     }
     }
 
 
     if (AI_LWO_FOURCC_LWOB != fileType) {
     if (AI_LWO_FOURCC_LWOB != fileType) {
@@ -232,7 +232,7 @@ void LWOImporter::InternReadFile(const std::string &pFile,
         }
         }
 
 
         if (configLayerName.length() && !hasNamedLayer) {
         if (configLayerName.length() && !hasNamedLayer) {
-            throw DeadlyImportError("LWO2: Unable to find the requested layer: " + configLayerName);
+            throw DeadlyImportError("LWO2: Unable to find the requested layer: ", configLayerName);
         }
         }
     }
     }
 
 

+ 1 - 1
code/AssetLib/LWS/LWSLoader.cpp

@@ -502,7 +502,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open LWS file " + pFile + ".");
+        throw DeadlyImportError("Failed to open LWS file ", pFile, ".");
     }
     }
 
 
     // Allocate storage and copy the contents of the file to a memory buffer
     // Allocate storage and copy the contents of the file to a memory buffer

+ 5 - 5
code/AssetLib/M3D/M3DImporter.cpp

@@ -160,21 +160,21 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys
     // Read file into memory
     // Read file into memory
     std::unique_ptr<IOStream> pStream(pIOHandler->Open(file, "rb"));
     std::unique_ptr<IOStream> pStream(pIOHandler->Open(file, "rb"));
     if (!pStream.get()) {
     if (!pStream.get()) {
-        throw DeadlyImportError("Failed to open file " + file + ".");
+        throw DeadlyImportError("Failed to open file ", file, ".");
     }
     }
 
 
     // Get the file-size and validate it, throwing an exception when fails
     // Get the file-size and validate it, throwing an exception when fails
     size_t fileSize = pStream->FileSize();
     size_t fileSize = pStream->FileSize();
     if (fileSize < 8) {
     if (fileSize < 8) {
-        throw DeadlyImportError("M3D-file " + file + " is too small.");
+        throw DeadlyImportError("M3D-file ", file, " is too small.");
     }
     }
     std::vector<unsigned char> buffer(fileSize);
     std::vector<unsigned char> buffer(fileSize);
     if (fileSize != pStream->Read(buffer.data(), 1, fileSize)) {
     if (fileSize != pStream->Read(buffer.data(), 1, fileSize)) {
-        throw DeadlyImportError("Failed to read the file " + file + ".");
+        throw DeadlyImportError("Failed to read the file ", file, ".");
     }
     }
     // extra check for binary format's first 8 bytes. Not done for the ASCII variant
     // extra check for binary format's first 8 bytes. Not done for the ASCII variant
     if (!memcmp(buffer.data(), "3DMO", 4) && memcmp(buffer.data() + 4, &fileSize, 4)) {
     if (!memcmp(buffer.data(), "3DMO", 4) && memcmp(buffer.data() + 4, &fileSize, 4)) {
-        throw DeadlyImportError("Bad binary header in file " + file + ".");
+        throw DeadlyImportError("Bad binary header in file ", file, ".");
     }
     }
 #ifdef M3D_ASCII
 #ifdef M3D_ASCII
     // make sure there's a terminator zero character, as input must be ASCIIZ
     // make sure there's a terminator zero character, as input must be ASCIIZ
@@ -200,7 +200,7 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys
     M3DWrapper m3d(pIOHandler, buffer);
     M3DWrapper m3d(pIOHandler, buffer);
 
 
     if (!m3d) {
     if (!m3d) {
-        throw DeadlyImportError("Unable to parse " + file + " as M3D.");
+        throw DeadlyImportError("Unable to parse ", file, " as M3D.");
     }
     }
 
 
     // create the root node
     // create the root node

Diferenças do arquivo suprimidas por serem muito extensas
+ 423 - 421
code/AssetLib/M3D/m3d.h


+ 1 - 1
code/AssetLib/MD2/MD2Loader.cpp

@@ -222,7 +222,7 @@ void MD2Importer::InternReadFile( const std::string& pFile,
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open MD2 file " + pFile + "");
+        throw DeadlyImportError("Failed to open MD2 file ", pFile, "");
     }
     }
 
 
     // check whether the md3 file is large enough to contain
     // check whether the md3 file is large enough to contain

+ 1 - 1
code/AssetLib/MD3/MD3Loader.cpp

@@ -715,7 +715,7 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open MD3 file " + pFile + ".");
+        throw DeadlyImportError("Failed to open MD3 file ", pFile, ".");
     }
     }
 
 
     // Check whether the md3 file is large enough to contain the header
     // Check whether the md3 file is large enough to contain the header

+ 1 - 1
code/AssetLib/MD5/MD5Loader.cpp

@@ -675,7 +675,7 @@ void MD5Importer::LoadMD5CameraFile() {
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (!file.get() || !file->FileSize()) {
     if (!file.get() || !file->FileSize()) {
-        throw DeadlyImportError("Failed to read MD5CAMERA file: " + pFile);
+        throw DeadlyImportError("Failed to read MD5CAMERA file: ", pFile);
     }
     }
     mHadMD5Camera = true;
     mHadMD5Camera = true;
     LoadFileIntoMemory(file.get());
     LoadFileIntoMemory(file.get());

+ 1 - 1
code/AssetLib/MDC/MDCLoader.cpp

@@ -219,7 +219,7 @@ void MDCImporter::InternReadFile(
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open MDC file " + pFile + ".");
+        throw DeadlyImportError("Failed to open MDC file ", pFile, ".");
     }
     }
 
 
     // check whether the mdc file is large enough to contain the file header
     // check whether the mdc file is large enough to contain the file header

+ 3 - 3
code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp

@@ -68,9 +68,9 @@ namespace Assimp {
 namespace MDL {
 namespace MDL {
 namespace HalfLife {
 namespace HalfLife {
 
 
-#ifdef _WIN32
-#    pragma warning(disable : 4706) 
-#endif // _WIN32
+#ifdef _MSC_VER
+#    pragma warning(disable : 4706)
+#endif // _MSC_VER
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 HL1MDLLoader::HL1MDLLoader(
 HL1MDLLoader::HL1MDLLoader(

+ 2 - 2
code/AssetLib/MDL/HalfLife/HL1MDLLoader.h

@@ -218,12 +218,12 @@ private:
 template <typename MDLFileHeader>
 template <typename MDLFileHeader>
 void HL1MDLLoader::load_file_into_buffer(const std::string &file_path, unsigned char *&buffer) {
 void HL1MDLLoader::load_file_into_buffer(const std::string &file_path, unsigned char *&buffer) {
     if (!io_->Exists(file_path))
     if (!io_->Exists(file_path))
-        throw DeadlyImportError("Missing file " + DefaultIOSystem::fileName(file_path) + ".");
+        throw DeadlyImportError("Missing file ", DefaultIOSystem::fileName(file_path), ".");
 
 
     std::unique_ptr<IOStream> file(io_->Open(file_path));
     std::unique_ptr<IOStream> file(io_->Open(file_path));
 
 
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open MDL file " + DefaultIOSystem::fileName(file_path) + ".");
+        throw DeadlyImportError("Failed to open MDL file ", DefaultIOSystem::fileName(file_path), ".");
     }
     }
 
 
     const size_t file_size = file->FileSize();
     const size_t file_size = file->FileSize();

+ 3 - 3
code/AssetLib/MDL/MDLLoader.cpp

@@ -167,7 +167,7 @@ void MDLImporter::InternReadFile(const std::string &pFile,
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open MDL file " + pFile + ".");
+        throw DeadlyImportError("Failed to open MDL file ", pFile, ".");
     }
     }
 
 
     // This should work for all other types of MDL files, too ...
     // This should work for all other types of MDL files, too ...
@@ -251,8 +251,8 @@ void MDLImporter::InternReadFile(const std::string &pFile,
             }
             }
         } else {
         } else {
             // print the magic word to the log file
             // print the magic word to the log file
-            throw DeadlyImportError("Unknown MDL subformat " + pFile +
-                                    ". Magic word (" + std::string((char *)&iMagicWord, 4) + ") is not known");
+            throw DeadlyImportError("Unknown MDL subformat ", pFile,
+                                    ". Magic word (", std::string((char *)&iMagicWord, 4), ") is not known");
         }
         }
 
 
         // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
         // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system

+ 2 - 2
code/AssetLib/MMD/MMDImporter.cpp

@@ -111,7 +111,7 @@ void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
     // Read file by istream
     // Read file by istream
     std::filebuf fb;
     std::filebuf fb;
     if (!fb.open(file, std::ios::in | std::ios::binary)) {
     if (!fb.open(file, std::ios::in | std::ios::binary)) {
-        throw DeadlyImportError("Failed to open file " + file + ".");
+        throw DeadlyImportError("Failed to open file ", file, ".");
     }
     }
 
 
     std::istream fileStream(&fb);
     std::istream fileStream(&fb);
@@ -122,7 +122,7 @@ void MMDImporter::InternReadFile(const std::string &file, aiScene *pScene,
     fileStream.seekg(0, fileStream.beg);
     fileStream.seekg(0, fileStream.beg);
 
 
     if (fileSize < sizeof(pmx::PmxModel)) {
     if (fileSize < sizeof(pmx::PmxModel)) {
-        throw DeadlyImportError(file + " is too small.");
+        throw DeadlyImportError(file, " is too small.");
     }
     }
 
 
     pmx::PmxModel model;
     pmx::PmxModel model;

+ 2 - 2
code/AssetLib/MMD/MMDPmxParser.cpp

@@ -43,7 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "MMDPmxParser.h"
 #include "MMDPmxParser.h"
 #include <assimp/StringUtils.h>
 #include <assimp/StringUtils.h>
 #ifdef ASSIMP_USE_HUNTER
 #ifdef ASSIMP_USE_HUNTER
-#  include <utf8/utf8.h>
+#  include <utf8.h>
 #else
 #else
 #  include "../contrib/utf8cpp/source/utf8.h"
 #  include "../contrib/utf8cpp/source/utf8.h"
 #endif
 #endif
@@ -524,7 +524,7 @@ namespace pmx
 		if (version != 2.0f && version != 2.1f)
 		if (version != 2.0f && version != 2.1f)
 		{
 		{
 			std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl;
 			std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl;
-            throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but " + to_string(version));
+            throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but ", to_string(version));
     }
     }
 		this->setting.Read(stream);
 		this->setting.Read(stream);
 
 

+ 1 - 1
code/AssetLib/MS3D/MS3DLoader.cpp

@@ -229,7 +229,7 @@ void MS3DImporter::InternReadFile( const std::string& pFile,
     stream.CopyAndAdvance(head,10);
     stream.CopyAndAdvance(head,10);
     stream >> version;
     stream >> version;
     if (strncmp(head,"MS3D000000",10)) {
     if (strncmp(head,"MS3D000000",10)) {
-        throw DeadlyImportError("Not a MS3D file, magic string MS3D000000 not found: "+pFile);
+        throw DeadlyImportError("Not a MS3D file, magic string MS3D000000 not found: ", pFile);
     }
     }
 
 
     if (version != 4) {
     if (version != 4) {

+ 7 - 7
code/AssetLib/NFF/NFFLoader.cpp

@@ -96,7 +96,7 @@ const aiImporterDesc *NFFImporter::GetInfo() const {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 #define AI_NFF_PARSE_FLOAT(f) \
 #define AI_NFF_PARSE_FLOAT(f) \
     SkipSpaces(&sz);          \
     SkipSpaces(&sz);          \
-    if (!::IsLineEnd(*sz)) sz = fast_atoreal_move<float>(sz, (float &)f);
+    if (!::IsLineEnd(*sz)) sz = fast_atoreal_move<ai_real>(sz, (ai_real &)f);
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 #define AI_NFF_PARSE_TRIPLE(v) \
 #define AI_NFF_PARSE_TRIPLE(v) \
@@ -214,7 +214,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (!file.get())
     if (!file.get())
-        throw DeadlyImportError("Failed to open NFF file " + pFile + ".");
+        throw DeadlyImportError("Failed to open NFF file ", pFile, ".");
 
 
     // allocate storage and copy the contents of the file to a memory buffer
     // allocate storage and copy the contents of the file to a memory buffer
     // (terminate it with zero)
     // (terminate it with zero)
@@ -233,7 +233,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
 
 
     // camera parameters
     // camera parameters
     aiVector3D camPos, camUp(0.f, 1.f, 0.f), camLookAt(0.f, 0.f, 1.f);
     aiVector3D camPos, camUp(0.f, 1.f, 0.f), camLookAt(0.f, 0.f, 1.f);
-    float angle = 45.f;
+    ai_real angle = 45.f;
     aiVector2D resolution;
     aiVector2D resolution;
 
 
     bool hasCam = false;
     bool hasCam = false;
@@ -262,7 +262,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
 
 
     // check whether this is the NFF2 file format
     // check whether this is the NFF2 file format
     if (TokenMatch(buffer, "nff", 3)) {
     if (TokenMatch(buffer, "nff", 3)) {
-        const float qnan = get_qnan();
+        const ai_real qnan = get_qnan();
         const aiColor4D cQNAN = aiColor4D(qnan, 0.f, 0.f, 1.f);
         const aiColor4D cQNAN = aiColor4D(qnan, 0.f, 0.f, 1.f);
         const aiVector3D vQNAN = aiVector3D(qnan, 0.f, 0.f);
         const aiVector3D vQNAN = aiVector3D(qnan, 0.f, 0.f);
 
 
@@ -706,7 +706,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
             }
             }
             // 'f' - shading information block
             // 'f' - shading information block
             else if (TokenMatch(sz, "f", 1)) {
             else if (TokenMatch(sz, "f", 1)) {
-                float d;
+                ai_real d;
 
 
                 // read the RGB colors
                 // read the RGB colors
                 AI_NFF_PARSE_TRIPLE(s.color);
                 AI_NFF_PARSE_TRIPLE(s.color);
@@ -856,7 +856,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
 
 
                 // read the two center points and the respective radii
                 // read the two center points and the respective radii
                 aiVector3D center1, center2;
                 aiVector3D center1, center2;
-                float radius1 = 0.f, radius2 = 0.f;
+                ai_real radius1 = 0.f, radius2 = 0.f;
                 AI_NFF_PARSE_TRIPLE(center1);
                 AI_NFF_PARSE_TRIPLE(center1);
                 AI_NFF_PARSE_FLOAT(radius1);
                 AI_NFF_PARSE_FLOAT(radius1);
 
 
@@ -874,7 +874,7 @@ void NFFImporter::InternReadFile(const std::string &pFile,
                 curMesh.dir = center2 - center1;
                 curMesh.dir = center2 - center1;
                 curMesh.center = center1 + curMesh.dir / (ai_real)2.0;
                 curMesh.center = center1 + curMesh.dir / (ai_real)2.0;
 
 
-                float f;
+                ai_real f;
                 if ((f = curMesh.dir.Length()) < 10e-3f) {
                 if ((f = curMesh.dir.Length()) < 10e-3f) {
                     ASSIMP_LOG_ERROR("NFF: Cone height is close to zero");
                     ASSIMP_LOG_ERROR("NFF: Cone height is close to zero");
                     continue;
                     continue;

+ 3 - 3
code/AssetLib/NFF/NFFLoader.h

@@ -113,14 +113,14 @@ private:
         {}
         {}
 
 
         aiColor3D color,diffuse,specular,ambient,emissive;
         aiColor3D color,diffuse,specular,ambient,emissive;
-        float refracti;
+        ai_real refracti;
 
 
         std::string texFile;
         std::string texFile;
 
 
         // For NFF2
         // For NFF2
         bool twoSided;
         bool twoSided;
         bool shaded;
         bool shaded;
-        float opacity, shininess;
+        ai_real opacity, shininess;
 
 
         std::string name;
         std::string name;
 
 
@@ -155,7 +155,7 @@ private:
         {}
         {}
 
 
         aiVector3D position;
         aiVector3D position;
-        float intensity;
+        ai_real intensity;
         aiColor3D color;
         aiColor3D color;
     };
     };
 
 

+ 1 - 1
code/AssetLib/OFF/OFFLoader.cpp

@@ -123,7 +123,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if( file.get() == nullptr) {
     if( file.get() == nullptr) {
-        throw DeadlyImportError( "Failed to open OFF file " + pFile + ".");
+        throw DeadlyImportError( "Failed to open OFF file ", pFile, ".");
     }
     }
 
 
     // allocate storage and copy the contents of the file to a memory buffer
     // allocate storage and copy the contents of the file to a memory buffer

+ 5 - 2
code/AssetLib/Obj/ObjFileImporter.cpp

@@ -109,9 +109,12 @@ const aiImporterDesc *ObjFileImporter::GetInfo() const {
 void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
 void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
     // Read file into memory
     // Read file into memory
     static const std::string mode = "rb";
     static const std::string mode = "rb";
-    std::unique_ptr<IOStream> fileStream(pIOHandler->Open(file, mode));
+    auto streamCloser = [&](IOStream *pStream) {
+        pIOHandler->Close(pStream);
+    };
+    std::unique_ptr<IOStream, decltype(streamCloser)> fileStream(pIOHandler->Open(file, mode), streamCloser);
     if (!fileStream.get()) {
     if (!fileStream.get()) {
-        throw DeadlyImportError("Failed to open file " + file + ".");
+        throw DeadlyImportError("Failed to open file ", file, ".");
     }
     }
 
 
     // Get the file-size and validate it, throwing an exception when fails
     // Get the file-size and validate it, throwing an exception when fails

+ 9 - 9
code/AssetLib/Ogre/OgreBinarySerializer.cpp

@@ -187,8 +187,8 @@ Mesh *OgreBinarySerializer::ImportMesh(MemoryStreamReader *stream) {
     /// @todo Check what we can actually support.
     /// @todo Check what we can actually support.
     std::string version = serializer.ReadLine();
     std::string version = serializer.ReadLine();
     if (version != MESH_VERSION_1_8) {
     if (version != MESH_VERSION_1_8) {
-        throw DeadlyExportError(Formatter::format() << "Mesh version " << version << " not supported by this importer. Run OgreMeshUpgrader tool on the file and try again."
-                                                    << " Supported versions: " << MESH_VERSION_1_8);
+        throw DeadlyExportError("Mesh version ", version, " not supported by this importer. Run OgreMeshUpgrader tool on the file and try again.",
+                                                    " Supported versions: ", MESH_VERSION_1_8);
     }
     }
 
 
     Mesh *mesh = new Mesh();
     Mesh *mesh = new Mesh();
@@ -471,7 +471,7 @@ void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh) {
             uint16_t submeshIndex = Read<uint16_t>();
             uint16_t submeshIndex = Read<uint16_t>();
             SubMesh *submesh = mesh->GetSubMesh(submeshIndex);
             SubMesh *submesh = mesh->GetSubMesh(submeshIndex);
             if (!submesh) {
             if (!submesh) {
-                throw DeadlyImportError(Formatter::format() << "Ogre Mesh does not include submesh " << submeshIndex << " referenced in M_SUBMESH_NAME_TABLE_ELEMENT. Invalid mesh file.");
+                throw DeadlyImportError("Ogre Mesh does not include submesh ", submeshIndex, " referenced in M_SUBMESH_NAME_TABLE_ELEMENT. Invalid mesh file.");
             }
             }
 
 
             submesh->name = ReadLine();
             submesh->name = ReadLine();
@@ -788,7 +788,7 @@ MemoryStreamReaderPtr OgreBinarySerializer::OpenReader(Assimp::IOSystem *pIOHand
 
 
     IOStream *f = pIOHandler->Open(filename, "rb");
     IOStream *f = pIOHandler->Open(filename, "rb");
     if (!f) {
     if (!f) {
-        throw DeadlyImportError("Failed to open skeleton file " + filename);
+        throw DeadlyImportError("Failed to open skeleton file ", filename);
     }
     }
 
 
     return MemoryStreamReaderPtr(new MemoryStreamReader(f));
     return MemoryStreamReaderPtr(new MemoryStreamReader(f));
@@ -803,8 +803,8 @@ void OgreBinarySerializer::ReadSkeleton(Skeleton *skeleton) {
     // This deserialization supports both versions of the skeleton spec
     // This deserialization supports both versions of the skeleton spec
     std::string version = ReadLine();
     std::string version = ReadLine();
     if (version != SKELETON_VERSION_1_8 && version != SKELETON_VERSION_1_1) {
     if (version != SKELETON_VERSION_1_8 && version != SKELETON_VERSION_1_1) {
-        throw DeadlyExportError(Formatter::format() << "Skeleton version " << version << " not supported by this importer."
-                                                    << " Supported versions: " << SKELETON_VERSION_1_8 << " and " << SKELETON_VERSION_1_1);
+        throw DeadlyExportError("Skeleton version ", version, " not supported by this importer.",
+                                                    " Supported versions: ", SKELETON_VERSION_1_8, " and ", SKELETON_VERSION_1_1);
     }
     }
 
 
     ASSIMP_LOG_VERBOSE_DEBUG("Reading Skeleton");
     ASSIMP_LOG_VERBOSE_DEBUG("Reading Skeleton");
@@ -871,7 +871,7 @@ void OgreBinarySerializer::ReadBone(Skeleton *skeleton) {
 
 
     // Bone indexes need to start from 0 and be contiguous
     // Bone indexes need to start from 0 and be contiguous
     if (bone->id != skeleton->bones.size()) {
     if (bone->id != skeleton->bones.size()) {
-        throw DeadlyImportError(Formatter::format() << "Ogre Skeleton bone indexes not contiguous. Error at bone index " << bone->id);
+        throw DeadlyImportError("Ogre Skeleton bone indexes not contiguous. Error at bone index ", bone->id);
     }
     }
 
 
     ASSIMP_LOG_VERBOSE_DEBUG_F("    ", bone->id, " ", bone->name);
     ASSIMP_LOG_VERBOSE_DEBUG_F("    ", bone->id, " ", bone->name);
@@ -889,7 +889,7 @@ void OgreBinarySerializer::ReadBoneParent(Skeleton *skeleton) {
     if (child && parent)
     if (child && parent)
         parent->AddChild(child);
         parent->AddChild(child);
     else
     else
-        throw DeadlyImportError(Formatter::format() << "Failed to find bones for parenting: Child id " << childId << " for parent id " << parentId);
+        throw DeadlyImportError("Failed to find bones for parenting: Child id ", childId, " for parent id ", parentId);
 }
 }
 
 
 void OgreBinarySerializer::ReadSkeletonAnimation(Skeleton *skeleton) {
 void OgreBinarySerializer::ReadSkeletonAnimation(Skeleton *skeleton) {
@@ -926,7 +926,7 @@ void OgreBinarySerializer::ReadSkeletonAnimationTrack(Skeleton * /*skeleton*/, A
     uint16_t boneId = Read<uint16_t>();
     uint16_t boneId = Read<uint16_t>();
     Bone *bone = dest->parentSkeleton->BoneById(boneId);
     Bone *bone = dest->parentSkeleton->BoneById(boneId);
     if (!bone) {
     if (!bone) {
-        throw DeadlyImportError(Formatter::format() << "Cannot read animation track, target bone " << boneId << " not in target Skeleton");
+        throw DeadlyImportError("Cannot read animation track, target bone ", boneId, " not in target Skeleton");
     }
     }
 
 
     VertexAnimationTrack track;
     VertexAnimationTrack track;

+ 1 - 1
code/AssetLib/Ogre/OgreImporter.cpp

@@ -91,7 +91,7 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
     // Open source file
     // Open source file
     IOStream *f = pIOHandler->Open(pFile, "rb");
     IOStream *f = pIOHandler->Open(pFile, "rb");
     if (!f) {
     if (!f) {
-        throw DeadlyImportError("Failed to open file " + pFile);
+        throw DeadlyImportError("Failed to open file ", pFile);
     }
     }
 
 
     // Binary .mesh import
     // Binary .mesh import

+ 5 - 5
code/AssetLib/Ogre/OgreStructs.cpp

@@ -476,7 +476,7 @@ void SubMesh::Reset(){
 
 
 aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent) {
 aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent) {
     if (operationType != OT_TRIANGLE_LIST) {
     if (operationType != OT_TRIANGLE_LIST) {
-        throw DeadlyImportError(Formatter::format() << "Only mesh operation type OT_TRIANGLE_LIST is supported. Found " << operationType);
+        throw DeadlyImportError("Only mesh operation type OT_TRIANGLE_LIST is supported. Found ", operationType);
     }
     }
 
 
     aiMesh *dest = new aiMesh();
     aiMesh *dest = new aiMesh();
@@ -944,7 +944,7 @@ void Bone::AddChild(Bone *bone) {
     if (!bone)
     if (!bone)
         return;
         return;
     if (bone->IsParented())
     if (bone->IsParented())
-        throw DeadlyImportError("Attaching child Bone that is already parented: " + bone->name);
+        throw DeadlyImportError("Attaching child Bone that is already parented: ", bone->name);
 
 
     bone->parent = this;
     bone->parent = this;
     bone->parentId = id;
     bone->parentId = id;
@@ -963,7 +963,7 @@ void Bone::CalculateWorldMatrixAndDefaultPose(Skeleton *skeleton) {
     for (auto boneId : children) {
     for (auto boneId : children) {
         Bone *child = skeleton->BoneById(boneId);
         Bone *child = skeleton->BoneById(boneId);
         if (!child) {
         if (!child) {
-            throw DeadlyImportError(Formatter::format() << "CalculateWorldMatrixAndDefaultPose: Failed to find child bone " << boneId << " for parent " << id << " " << name);
+            throw DeadlyImportError("CalculateWorldMatrixAndDefaultPose: Failed to find child bone ", boneId, " for parent ", id, " ", name);
         }
         }
         child->CalculateWorldMatrixAndDefaultPose(skeleton);
         child->CalculateWorldMatrixAndDefaultPose(skeleton);
     }
     }
@@ -983,7 +983,7 @@ aiNode *Bone::ConvertToAssimpNode(Skeleton *skeleton, aiNode *parentNode) {
         for (size_t i = 0, len = children.size(); i < len; ++i) {
         for (size_t i = 0, len = children.size(); i < len; ++i) {
             Bone *child = skeleton->BoneById(children[i]);
             Bone *child = skeleton->BoneById(children[i]);
             if (!child) {
             if (!child) {
-                throw DeadlyImportError(Formatter::format() << "ConvertToAssimpNode: Failed to find child bone " << children[i] << " for parent " << id << " " << name);
+                throw DeadlyImportError("ConvertToAssimpNode: Failed to find child bone ", children[i], " for parent ", id, " ", name);
             }
             }
             node->mChildren[i] = child->ConvertToAssimpNode(skeleton, node);
             node->mChildren[i] = child->ConvertToAssimpNode(skeleton, node);
         }
         }
@@ -1022,7 +1022,7 @@ aiNodeAnim *VertexAnimationTrack::ConvertToAssimpAnimationNode(Skeleton *skeleto
 
 
     Bone *bone = skeleton->BoneByName(boneName);
     Bone *bone = skeleton->BoneByName(boneName);
     if (!bone) {
     if (!bone) {
-        throw DeadlyImportError("VertexAnimationTrack::ConvertToAssimpAnimationNode: Failed to find bone " + boneName + " from parent Skeleton");
+        throw DeadlyImportError("VertexAnimationTrack::ConvertToAssimpAnimationNode: Failed to find bone ", boneName, " from parent Skeleton");
     }
     }
 
 
     // Keyframes
     // Keyframes

+ 14 - 14
code/AssetLib/Ogre/OgreXmlSerializer.cpp

@@ -60,9 +60,9 @@ namespace Ogre {
 
 
 AI_WONT_RETURN void ThrowAttibuteError(const std::string &nodeName, const std::string &name, const std::string &error) {
 AI_WONT_RETURN void ThrowAttibuteError(const std::string &nodeName, const std::string &name, const std::string &error) {
     if (!error.empty()) {
     if (!error.empty()) {
-        throw DeadlyImportError(error + " in node '" + nodeName + "' and attribute '" + name + "'");
+        throw DeadlyImportError(error, " in node '", nodeName, "' and attribute '", name, "'");
     } else {
     } else {
-        throw DeadlyImportError("Attribute '" + name + "' does not exist in node '" + nodeName + "'");
+        throw DeadlyImportError("Attribute '", name, "' does not exist in node '", nodeName, "'");
     }
     }
 }
 }
 
 
@@ -321,18 +321,18 @@ void OgreXmlSerializer::ReadGeometryVertexBuffer(XmlNode &node, VertexDataXml *d
 
 
     // Sanity checks
     // Sanity checks
     if (dest->positions.size() != dest->count) {
     if (dest->positions.size() != dest->count) {
-        throw DeadlyImportError(Formatter::format() << "Read only " << dest->positions.size() << " positions when should have read " << dest->count);
+        throw DeadlyImportError("Read only ", dest->positions.size(), " positions when should have read ", dest->count);
     }
     }
     if (normals && dest->normals.size() != dest->count) {
     if (normals && dest->normals.size() != dest->count) {
-        throw DeadlyImportError(Formatter::format() << "Read only " << dest->normals.size() << " normals when should have read " << dest->count);
+        throw DeadlyImportError("Read only ", dest->normals.size(), " normals when should have read ", dest->count);
     }
     }
     if (tangents && dest->tangents.size() != dest->count) {
     if (tangents && dest->tangents.size() != dest->count) {
-        throw DeadlyImportError(Formatter::format() << "Read only " << dest->tangents.size() << " tangents when should have read " << dest->count);
+        throw DeadlyImportError("Read only ", dest->tangents.size(), " tangents when should have read ", dest->count);
     }
     }
     for (unsigned int i = 0; i < dest->uvs.size(); ++i) {
     for (unsigned int i = 0; i < dest->uvs.size(); ++i) {
         if (dest->uvs[i].size() != dest->count) {
         if (dest->uvs[i].size() != dest->count) {
-            throw DeadlyImportError(Formatter::format() << "Read only " << dest->uvs[i].size()
-                                                        << " uvs for uv index " << i << " when should have read " << dest->count);
+            throw DeadlyImportError("Read only ", dest->uvs[i].size(),
+                                                        " uvs for uv index ", i, " when should have read ", dest->count);
         }
         }
     }
     }
 }
 }
@@ -389,7 +389,7 @@ void OgreXmlSerializer::ReadSubMesh(XmlNode &node, MeshXml *mesh) {
             if (submesh->indexData->faces.size() == submesh->indexData->faceCount) {
             if (submesh->indexData->faces.size() == submesh->indexData->faceCount) {
                 ASSIMP_LOG_VERBOSE_DEBUG_F("  - Faces ", submesh->indexData->faceCount);
                 ASSIMP_LOG_VERBOSE_DEBUG_F("  - Faces ", submesh->indexData->faceCount);
             } else {
             } else {
-                throw DeadlyImportError(Formatter::format() << "Read only " << submesh->indexData->faces.size() << " faces when should have read " << submesh->indexData->faceCount);
+                throw DeadlyImportError("Read only ", submesh->indexData->faces.size(), " faces when should have read ", submesh->indexData->faceCount);
             }
             }
         } else if (currentName == nnGeometry) {
         } else if (currentName == nnGeometry) {
             if (submesh->usesSharedVertexData) {
             if (submesh->usesSharedVertexData) {
@@ -515,7 +515,7 @@ XmlParserPtr OgreXmlSerializer::OpenXmlParser(Assimp::IOSystem *pIOHandler, cons
 
 
     std::unique_ptr<IOStream> file(pIOHandler->Open(filename));
     std::unique_ptr<IOStream> file(pIOHandler->Open(filename));
     if (!file.get()) {
     if (!file.get()) {
-        throw DeadlyImportError("Failed to open skeleton file " + filename);
+        throw DeadlyImportError("Failed to open skeleton file ", filename);
     }
     }
 
 
     XmlParserPtr xmlParser = XmlParserPtr(new XmlParser);
     XmlParserPtr xmlParser = XmlParserPtr(new XmlParser);
@@ -568,7 +568,7 @@ void OgreXmlSerializer::ReadAnimations(XmlNode &node, Skeleton *skeleton) {
                     ReadAnimationTracks(currentChildNode, anim);
                     ReadAnimationTracks(currentChildNode, anim);
                     skeleton->animations.push_back(anim);
                     skeleton->animations.push_back(anim);
                 } else {
                 } else {
-                    throw DeadlyImportError(Formatter::format() << "No <tracks> found in <animation> " << anim->name);
+                    throw DeadlyImportError( "No <tracks> found in <animation> ", anim->name);
                 }
                 }
             }
             }
         }
         }
@@ -588,7 +588,7 @@ void OgreXmlSerializer::ReadAnimationTracks(XmlNode &node, Animation *dest) {
                     ReadAnimationKeyFrames(currentChildNode, dest, &track);
                     ReadAnimationKeyFrames(currentChildNode, dest, &track);
                     dest->tracks.push_back(track);
                     dest->tracks.push_back(track);
                 } else {
                 } else {
-                    throw DeadlyImportError(Formatter::format() << "No <keyframes> found in <track> " << dest->name);
+                    throw DeadlyImportError( "No <keyframes> found in <track> ", dest->name);
                 }
                 }
             }
             }
 
 
@@ -657,7 +657,7 @@ void OgreXmlSerializer::ReadBoneHierarchy(XmlNode &node, Skeleton *skeleton) {
             if (bone && parent) {
             if (bone && parent) {
                 parent->AddChild(bone);
                 parent->AddChild(bone);
             } else {
             } else {
-                throw DeadlyImportError("Failed to find bones for parenting: Child " + name + " for parent " + parentName);
+                throw DeadlyImportError("Failed to find bones for parenting: Child ", name, " for parent ", parentName);
             }
             }
         }
         }
     }
     }
@@ -704,7 +704,7 @@ void OgreXmlSerializer::ReadBones(XmlNode &node, Skeleton *skeleton) {
 
 
                             bone->rotation = aiQuaternion(axis, angle);
                             bone->rotation = aiQuaternion(axis, angle);
                         } else {
                         } else {
-                            throw DeadlyImportError(Formatter::format() << "No axis specified for bone rotation in bone " << bone->id);
+                            throw DeadlyImportError( "No axis specified for bone rotation in bone ", bone->id);
                         }
                         }
                     }
                     }
                 } else if (currentChildName == nnScale) {
                 } else if (currentChildName == nnScale) {
@@ -736,7 +736,7 @@ void OgreXmlSerializer::ReadBones(XmlNode &node, Skeleton *skeleton) {
         ASSIMP_LOG_VERBOSE_DEBUG_F("    ", b->id, " ", b->name);
         ASSIMP_LOG_VERBOSE_DEBUG_F("    ", b->id, " ", b->name);
 
 
         if (b->id != static_cast<uint16_t>(i)) {
         if (b->id != static_cast<uint16_t>(i)) {
-            throw DeadlyImportError(Formatter::format() << "Bone ids are not in sequence starting from 0. Missing index " << i);
+            throw DeadlyImportError("Bone ids are not in sequence starting from 0. Missing index ", i);
         }
         }
     }
     }
 }
 }

+ 1 - 1
code/AssetLib/OpenGEX/OpenGEXImporter.cpp

@@ -302,7 +302,7 @@ void OpenGEXImporter::InternReadFile( const std::string &filename, aiScene *pSce
     // open source file
     // open source file
     IOStream *file = pIOHandler->Open( filename, "rb" );
     IOStream *file = pIOHandler->Open( filename, "rb" );
     if( !file ) {
     if( !file ) {
-        throw DeadlyImportError( "Failed to open file " + filename );
+        throw DeadlyImportError( "Failed to open file ", filename );
     }
     }
 
 
     std::vector<char> buffer;
     std::vector<char> buffer;

+ 2 - 2
code/AssetLib/Ply/PlyLoader.cpp

@@ -151,13 +151,13 @@ void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
     const std::string mode = "rb";
     const std::string mode = "rb";
     std::unique_ptr<IOStream> fileStream(pIOHandler->Open(pFile, mode));
     std::unique_ptr<IOStream> fileStream(pIOHandler->Open(pFile, mode));
     if (!fileStream.get()) {
     if (!fileStream.get()) {
-        throw DeadlyImportError("Failed to open file " + pFile + ".");
+        throw DeadlyImportError("Failed to open file ", pFile, ".");
     }
     }
 
 
     // Get the file-size
     // Get the file-size
     const size_t fileSize(fileStream->FileSize());
     const size_t fileSize(fileStream->FileSize());
     if (0 == fileSize) {
     if (0 == fileSize) {
-        throw DeadlyImportError("File " + pFile + " is empty.");
+        throw DeadlyImportError("File ", pFile, " is empty.");
     }
     }
 
 
     IOStreamBuffer<char> streamedBuffer(1024 * 1024);
     IOStreamBuffer<char> streamedBuffer(1024 * 1024);

+ 1 - 1
code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp

@@ -180,7 +180,7 @@ const aiImporterDesc *Q3BSPFileImporter::GetInfo() const {
 void Q3BSPFileImporter::InternReadFile(const std::string &rFile, aiScene *scene, IOSystem *ioHandler) {
 void Q3BSPFileImporter::InternReadFile(const std::string &rFile, aiScene *scene, IOSystem *ioHandler) {
     ZipArchiveIOSystem Archive(ioHandler, rFile);
     ZipArchiveIOSystem Archive(ioHandler, rFile);
     if (!Archive.isOpen()) {
     if (!Archive.isOpen()) {
-        throw DeadlyImportError("Failed to open file " + rFile + ".");
+        throw DeadlyImportError("Failed to open file ", rFile, ".");
     }
     }
 
 
     std::string archiveName(""), mapName("");
     std::string archiveName(""), mapName("");

+ 2 - 3
code/AssetLib/Q3D/Q3DLoader.cpp

@@ -110,13 +110,12 @@ void Q3DImporter::InternReadFile(const std::string &pFile,
 
 
     // The header is 22 bytes large
     // The header is 22 bytes large
     if (stream.GetRemainingSize() < 22)
     if (stream.GetRemainingSize() < 22)
-        throw DeadlyImportError("File is either empty or corrupt: " + pFile);
+        throw DeadlyImportError("File is either empty or corrupt: ", pFile);
 
 
     // Check the file's signature
     // Check the file's signature
     if (ASSIMP_strincmp((const char *)stream.GetPtr(), "quick3Do", 8) &&
     if (ASSIMP_strincmp((const char *)stream.GetPtr(), "quick3Do", 8) &&
             ASSIMP_strincmp((const char *)stream.GetPtr(), "quick3Ds", 8)) {
             ASSIMP_strincmp((const char *)stream.GetPtr(), "quick3Ds", 8)) {
-        throw DeadlyImportError("Not a Quick3D file. Signature string is: " +
-                                std::string((const char *)stream.GetPtr(), 8));
+        throw DeadlyImportError("Not a Quick3D file. Signature string is: ", std::string((const char *)stream.GetPtr(), 8));
     }
     }
 
 
     // Print the file format version
     // Print the file format version

+ 1 - 1
code/AssetLib/Raw/RawLoader.cpp

@@ -101,7 +101,7 @@ void RAWImporter::InternReadFile(const std::string &pFile,
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open RAW file " + pFile + ".");
+        throw DeadlyImportError("Failed to open RAW file ", pFile, ".");
     }
     }
 
 
     // allocate storage and copy the contents of the file to a memory buffer
     // allocate storage and copy the contents of the file to a memory buffer

+ 2 - 2
code/AssetLib/SIB/SIBImporter.cpp

@@ -59,7 +59,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/StreamReader.h>
 #include <assimp/StreamReader.h>
 #include <assimp/TinyFormatter.h>
 #include <assimp/TinyFormatter.h>
 #ifdef ASSIMP_USE_HUNTER
 #ifdef ASSIMP_USE_HUNTER
-#include <utf8/utf8.h>
+#include <utf8.h>
 #else
 #else
 //#  include "../contrib/ConvertUTF/ConvertUTF.h"
 //#  include "../contrib/ConvertUTF/ConvertUTF.h"
 #include "../contrib/utf8cpp/source/utf8.h"
 #include "../contrib/utf8cpp/source/utf8.h"
@@ -808,7 +808,7 @@ void SIBImporter::InternReadFile(const std::string &pFile,
 
 
     // We should have at least one chunk
     // We should have at least one chunk
     if (stream.GetRemainingSize() < 16)
     if (stream.GetRemainingSize() < 16)
-        throw DeadlyImportError("SIB file is either empty or corrupt: " + pFile);
+        throw DeadlyImportError("SIB file is either empty or corrupt: ", pFile);
 
 
     SIB sib;
     SIB sib;
 
 

+ 1 - 1
code/AssetLib/SMD/SMDLoader.cpp

@@ -695,7 +695,7 @@ void SMDImporter::ReadSmd(const std::string &pFile, IOSystem* pIOHandler) {
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open SMD/VTA file " + pFile + ".");
+        throw DeadlyImportError("Failed to open SMD/VTA file ", pFile, ".");
     }
     }
 
 
     iFileSize = (unsigned int)file->FileSize();
     iFileSize = (unsigned int)file->FileSize();

+ 1 - 1
code/AssetLib/STEPParser/STEPFileEncoding.cpp

@@ -46,7 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "STEPFileEncoding.h"
 #include "STEPFileEncoding.h"
 #include <assimp/fast_atof.h>
 #include <assimp/fast_atof.h>
 #ifdef ASSIMP_USE_HUNTER
 #ifdef ASSIMP_USE_HUNTER
-#  include <utf8/utf8.h>
+#  include <utf8.h>
 #else
 #else
 #  include <contrib/utf8cpp/source/utf8.h>
 #  include <contrib/utf8cpp/source/utf8.h>
 #endif
 #endif

+ 2 - 2
code/AssetLib/STL/STLLoader.cpp

@@ -181,7 +181,7 @@ void STLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file.get() == nullptr) {
     if (file.get() == nullptr) {
-        throw DeadlyImportError("Failed to open STL file " + pFile + ".");
+        throw DeadlyImportError("Failed to open STL file ", pFile, ".");
     }
     }
 
 
     mFileSize = (unsigned int)file->FileSize();
     mFileSize = (unsigned int)file->FileSize();
@@ -207,7 +207,7 @@ void STLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
     } else if (IsAsciiSTL(mBuffer, mFileSize)) {
     } else if (IsAsciiSTL(mBuffer, mFileSize)) {
         LoadASCIIFile(mScene->mRootNode);
         LoadASCIIFile(mScene->mRootNode);
     } else {
     } else {
-        throw DeadlyImportError("Failed to determine STL storage representation for " + pFile + ".");
+        throw DeadlyImportError("Failed to determine STL storage representation for ", pFile, ".");
     }
     }
 
 
     // create a single default material, using a white diffuse color for consistency with
     // create a single default material, using a white diffuse color for consistency with

+ 9 - 9
code/AssetLib/Step/STEPFile.h

@@ -54,10 +54,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/DefaultLogger.hpp>
 
 
-#ifdef _WIN32
+#ifdef _MSC_VER
 #    pragma warning(push)
 #    pragma warning(push)
 #    pragma warning(disable : 4127 4456 4245 4512 )
 #    pragma warning(disable : 4127 4456 4245 4512 )
-#endif // _WIN32 
+#endif // _MSC_VER 
 
 
 //
 //
 #if _MSC_VER > 1500 || (defined __GNUC___)
 #if _MSC_VER > 1500 || (defined __GNUC___)
@@ -130,8 +130,8 @@ namespace STEP {
      *  coupled with a line number. */
      *  coupled with a line number. */
 // -------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------
 struct SyntaxError : DeadlyImportError {
 struct SyntaxError : DeadlyImportError {
-    enum {
-        LINE_NOT_SPECIFIED = 0xffffffffffffffffLL
+    enum : uint64_t {
+        LINE_NOT_SPECIFIED = 0xfffffffffffffffLL
     };
     };
 
 
     SyntaxError(const std::string &s, uint64_t line = LINE_NOT_SPECIFIED);
     SyntaxError(const std::string &s, uint64_t line = LINE_NOT_SPECIFIED);
@@ -143,8 +143,8 @@ struct SyntaxError : DeadlyImportError {
      *  It is typically coupled with both an entity id and a line number.*/
      *  It is typically coupled with both an entity id and a line number.*/
 // -------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------
 struct TypeError : DeadlyImportError {
 struct TypeError : DeadlyImportError {
-    enum {
-        ENTITY_NOT_SPECIFIED = 0xffffffffffffffffLL,
+    enum : uint64_t {
+        ENTITY_NOT_SPECIFIED = 0xffffffffffffffffUL,
         ENTITY_NOT_SPECIFIED_32 = 0x00000000ffffffff
         ENTITY_NOT_SPECIFIED_32 = 0x00000000ffffffff
     };
     };
 
 
@@ -727,7 +727,7 @@ struct InternGenericConvert<Maybe<T>> {
     }
     }
 };
 };
 
 
-#ifdef _WIN32
+#if _MSC_VER > 1920
 #pragma warning(push)
 #pragma warning(push)
 #pragma warning(disable : 4127)
 #pragma warning(disable : 4127)
 #endif // _WIN32
 #endif // _WIN32
@@ -960,9 +960,9 @@ private:
     const EXPRESS::ConversionSchema *schema;
     const EXPRESS::ConversionSchema *schema;
 };
 };
 
 
-#ifdef _WIN32
+#ifdef _MSC_VER
 #pragma warning(pop)
 #pragma warning(pop)
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 } // namespace STEP
 } // namespace STEP
 
 

+ 1 - 1
code/AssetLib/Terragen/TerragenLoader.cpp

@@ -121,7 +121,7 @@ void TerragenImporter::InternReadFile(const std::string &pFile,
 
 
     // Check whether we can read from the file
     // Check whether we can read from the file
     if (file == nullptr)
     if (file == nullptr)
-        throw DeadlyImportError("Failed to open TERRAGEN TERRAIN file " + pFile + ".");
+        throw DeadlyImportError("Failed to open TERRAGEN TERRAIN file ", pFile, ".");
 
 
     // Construct a stream reader to read all data in the correct endianness
     // Construct a stream reader to read all data in the correct endianness
     StreamReaderLE reader(file);
     StreamReaderLE reader(file);

+ 1 - 1
code/AssetLib/X/XFileImporter.cpp

@@ -114,7 +114,7 @@ void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, I
     // read file into memory
     // read file into memory
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
     if ( file.get() == nullptr ) {
     if ( file.get() == nullptr ) {
-        throw DeadlyImportError( "Failed to open file " + pFile + "." );
+        throw DeadlyImportError( "Failed to open file ", pFile, "." );
     }
     }
 
 
     static const size_t MinSize = 16;
     static const size_t MinSize = 16;

+ 14 - 13
code/AssetLib/X/XFileParser.cpp

@@ -82,6 +82,17 @@ static void dummy_free(void * /*opaque*/, void *address) {
 
 
 #endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
 #endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
 
 
+// ------------------------------------------------------------------------------------------------
+// Throws an exception with a line number and the given text.
+template<typename... T>
+AI_WONT_RETURN void XFileParser::ThrowException(T&&... args) {
+    if (mIsBinaryFormat) {
+        throw DeadlyImportError(args...);
+    } else {
+        throw DeadlyImportError("Line ", mLineNumber, ": ", args...);
+    }
+}
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor. Creates a data structure out of the XFile given in the memory block.
 // Constructor. Creates a data structure out of the XFile given in the memory block.
 XFileParser::XFileParser(const std::vector<char> &pBuffer) :
 XFileParser::XFileParser(const std::vector<char> &pBuffer) :
@@ -122,13 +133,13 @@ XFileParser::XFileParser(const std::vector<char> &pBuffer) :
         mIsBinaryFormat = true;
         mIsBinaryFormat = true;
         compressed = true;
         compressed = true;
     } else
     } else
-        ThrowException(format() << "Unsupported xfile format '" << mP[8] << mP[9] << mP[10] << mP[11] << "'");
+        ThrowException("Unsupported xfile format '", mP[8], mP[9], mP[10], mP[11], "'");
 
 
     // float size
     // float size
     mBinaryFloatSize = (unsigned int)(mP[12] - 48) * 1000 + (unsigned int)(mP[13] - 48) * 100 + (unsigned int)(mP[14] - 48) * 10 + (unsigned int)(mP[15] - 48);
     mBinaryFloatSize = (unsigned int)(mP[12] - 48) * 1000 + (unsigned int)(mP[13] - 48) * 100 + (unsigned int)(mP[14] - 48) * 10 + (unsigned int)(mP[15] - 48);
 
 
     if (mBinaryFloatSize != 32 && mBinaryFloatSize != 64)
     if (mBinaryFloatSize != 32 && mBinaryFloatSize != 64)
-        ThrowException(format() << "Unknown float size " << mBinaryFloatSize << " specified in xfile header.");
+        ThrowException("Unknown float size ", mBinaryFloatSize, " specified in xfile header.");
 
 
     // The x format specifies size in bits, but we work in bytes
     // The x format specifies size in bits, but we work in bytes
     mBinaryFloatSize /= 8;
     mBinaryFloatSize /= 8;
@@ -864,7 +875,7 @@ void XFileParser::ParseDataObjectAnimationKey(AnimBone *pAnimBone) {
         }
         }
 
 
         default:
         default:
-            ThrowException(format() << "Unknown key type " << keyType << " in animation.");
+            ThrowException("Unknown key type ", keyType, " in animation.");
             break;
             break;
         } // end switch
         } // end switch
 
 
@@ -1355,16 +1366,6 @@ aiColor3D XFileParser::ReadRGB() {
     return color;
     return color;
 }
 }
 
 
-// ------------------------------------------------------------------------------------------------
-// Throws an exception with a line number and the given text.
-AI_WONT_RETURN void XFileParser::ThrowException(const std::string &pText) {
-    if (mIsBinaryFormat) {
-        throw DeadlyImportError(pText);
-    } else {
-        throw DeadlyImportError(format() << "Line " << mLineNumber << ": " << pText);
-    }
-}
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Filters the imported hierarchy for some degenerated cases that some exporters produce.
 // Filters the imported hierarchy for some degenerated cases that some exporters produce.
 void XFileParser::FilterHierarchy(XFile::Node *pNode) {
 void XFileParser::FilterHierarchy(XFile::Node *pNode) {

+ 2 - 1
code/AssetLib/X/XFileParser.h

@@ -133,7 +133,8 @@ protected:
     aiColor4D ReadRGBA();
     aiColor4D ReadRGBA();
 
 
     /** Throws an exception with a line number and the given text. */
     /** Throws an exception with a line number and the given text. */
-    AI_WONT_RETURN void ThrowException( const std::string& pText) AI_WONT_RETURN_SUFFIX;
+    template<typename... T>
+    AI_WONT_RETURN void ThrowException(T&&... args) AI_WONT_RETURN_SUFFIX;
 
 
     /**
     /**
       * @brief  Filters the imported hierarchy for some degenerated cases that some exporters produce.
       * @brief  Filters the imported hierarchy for some degenerated cases that some exporters produce.

+ 19 - 81
code/AssetLib/X3D/X3DImporter.cpp

@@ -155,99 +155,37 @@ void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) {
     std::unique_ptr<IOStream> fileStream(pIOHandler->Open(file, mode));
     std::unique_ptr<IOStream> fileStream(pIOHandler->Open(file, mode));
     if (!fileStream.get()) {
     if (!fileStream.get()) {
         throw DeadlyImportError("Failed to open file " + file + ".");
         throw DeadlyImportError("Failed to open file " + file + ".");
-    }
+	}
 }
 }
 
 
-/*********************************************************************************************************************************************/
-/************************************************************ Functions: find set ************************************************************/
-/*********************************************************************************************************************************************/
-
-bool X3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const {
-    const std::string extension = GetExtension(pFile);
-
-    if ((extension == "x3d") || (extension == "x3db")) {
-        return true;
-    }
-
-    if (!extension.length() || pCheckSig) {
-        const char *tokens[] = { "DOCTYPE X3D PUBLIC", "http://www.web3d.org/specifications/x3d" };
-
-        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2);
+bool X3DImporter::CanRead( const std::string &pFile, IOSystem * /*pIOHandler*/, bool checkSig ) const {
+    if (checkSig) {
+        std::string::size_type pos = pFile.find_last_of(".x3d");
+        if (pos != std::string::npos) {
+            return true;
+        }
     }
     }
 
 
     return false;
     return false;
 }
 }
 
 
-void X3DImporter::GetExtensionList(std::set<std::string> &pExtensionList) {
-    pExtensionList.insert("x3d");
-    pExtensionList.insert("x3db");
-}
-
-const aiImporterDesc *X3DImporter::GetInfo() const {
-    return &Description;
+void X3DImporter::GetExtensionList( std::set<std::string> &extensionList ) {
+    extensionList.insert("x3d");
 }
 }
 
 
-void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
-    mpIOHandler = pIOHandler;
-
-    Clear(); // delete old graph.
-    std::string::size_type slashPos = pFile.find_last_of("\\/");
-    pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1));
-    ParseFile(pFile, pIOHandler);
-    pIOHandler->PopDirectory();
-    if (NodeElement_List.empty())
-        return;
-    //
-    // Assimp use static arrays of objects for fast speed of rendering. That's good, but need some additional operations/
-    // We know that geometry objects(meshes) are stored in <Shape>, also in <Shape>-><Appearance> materials(in Assimp logical view)
-    // are stored. So at first we need to count how meshes and materials are stored in scene graph.
-    //
-    // at first creating root node for aiScene.
-    pScene->mRootNode = new aiNode;
-    pScene->mRootNode->mParent = nullptr;
-    pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED;
-    //search for root node element
-    mNodeElementCur = NodeElement_List.front();
-    while (mNodeElementCur->Parent != nullptr)
-        mNodeElementCur = mNodeElementCur->Parent;
-
-    { // fill aiScene with objects.
-        std::list<aiMesh *> mesh_list;
-        std::list<aiMaterial *> mat_list;
-        std::list<aiLight *> light_list;
-
-        // create nodes tree
-//        Postprocess_BuildNode(*mNodeElementCur, *pScene->mRootNode, mesh_list, mat_list, light_list);
-        // copy needed data to scene
-        if (!mesh_list.empty()) {
-            std::list<aiMesh *>::const_iterator it = mesh_list.begin();
-
-            pScene->mNumMeshes = static_cast<unsigned int>(mesh_list.size());
-            pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
-            for (size_t i = 0; i < pScene->mNumMeshes; i++)
-                pScene->mMeshes[i] = *it++;
-        }
-
-        if (!mat_list.empty()) {
-            std::list<aiMaterial *>::const_iterator it = mat_list.begin();
-
-            pScene->mNumMaterials = static_cast<unsigned int>(mat_list.size());
-            pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
-            for (size_t i = 0; i < pScene->mNumMaterials; i++)
-                pScene->mMaterials[i] = *it++;
-        }
+void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler ) {
+    std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
+    if (!stream) {
+        throw DeadlyImportError("Could not open file for reading");
+    }
 
 
-        if (!light_list.empty()) {
-            std::list<aiLight *>::const_iterator it = light_list.begin();
+    pScene->mRootNode = new aiNode(pFile);
+}
 
 
-            pScene->mNumLights = static_cast<unsigned int>(light_list.size());
-            pScene->mLights = new aiLight *[pScene->mNumLights];
-            for (size_t i = 0; i < pScene->mNumLights; i++)
-                pScene->mLights[i] = *it++;
-        }
-    }
+const aiImporterDesc *X3DImporter::GetInfo() const {
+    return &Description;
 }
 }
 
 
-} // namespace Assimp
+} 
 
 
 #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER
 #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER

+ 2 - 10
code/AssetLib/glTF/glTFAsset.h

@@ -1,4 +1,4 @@
-/*
+/*
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
@@ -49,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef GLTFASSET_H_INC
 #ifndef GLTFASSET_H_INC
 #define GLTFASSET_H_INC
 #define GLTFASSET_H_INC
 
 
-#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_IMPORTER)
 
 
 #include <assimp/Exceptional.h>
 #include <assimp/Exceptional.h>
 
 
@@ -60,19 +60,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <algorithm>
 #include <algorithm>
 #include <stdexcept>
 #include <stdexcept>
 
 
-#ifndef RAPIDJSON_HAS_STDSTRING
-#define RAPIDJSON_HAS_STDSTRING 1
-#endif
-
 #if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0)
 #if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wclass-memaccess"
 #pragma GCC diagnostic ignored "-Wclass-memaccess"
 #endif
 #endif
 
 
-#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
-#define RAPIDJSON_NOMEMBERITERATORCLASS
-#endif
-
 #include <rapidjson/rapidjson.h>
 #include <rapidjson/rapidjson.h>
 #include <rapidjson/document.h>
 #include <rapidjson/document.h>
 #include <rapidjson/error/en.h>
 #include <rapidjson/error/en.h>

+ 37 - 37
code/AssetLib/glTF/glTFAsset.inl

@@ -57,10 +57,10 @@ namespace glTF {
 
 
 namespace {
 namespace {
 
 
-#ifdef _WIN32
+#if _MSC_VER
 #    pragma warning(push)
 #    pragma warning(push)
 #    pragma warning(disable : 4706)
 #    pragma warning(disable : 4706)
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 //
 //
 // JSON Value reading helpers
 // JSON Value reading helpers
@@ -235,15 +235,15 @@ Ref<T> LazyDict<T>::Get(const char *id) {
 
 
     // read it from the JSON object
     // read it from the JSON object
     if (!mDict) {
     if (!mDict) {
-        throw DeadlyImportError("GLTF: Missing section \"" + std::string(mDictId) + "\"");
+        throw DeadlyImportError("GLTF: Missing section \"", mDictId, "\"");
     }
     }
 
 
     Value::MemberIterator obj = mDict->FindMember(id);
     Value::MemberIterator obj = mDict->FindMember(id);
     if (obj == mDict->MemberEnd()) {
     if (obj == mDict->MemberEnd()) {
-        throw DeadlyImportError("GLTF: Missing object with id \"" + std::string(id) + "\" in \"" + mDictId + "\"");
+        throw DeadlyImportError("GLTF: Missing object with id \"", id, "\" in \"", mDictId, "\"");
     }
     }
     if (!obj->value.IsObject()) {
     if (!obj->value.IsObject()) {
-        throw DeadlyImportError("GLTF: Object with id \"" + std::string(id) + "\" is not a JSON object");
+        throw DeadlyImportError("GLTF: Object with id \"", id, "\" is not a JSON object");
     }
     }
 
 
     // create an instance of the given type
     // create an instance of the given type
@@ -317,13 +317,13 @@ inline void Buffer::Read(Value &obj, Asset &r) {
             this->mData.reset(data, std::default_delete<uint8_t[]>());
             this->mData.reset(data, std::default_delete<uint8_t[]>());
 
 
             if (statedLength > 0 && this->byteLength != statedLength) {
             if (statedLength > 0 && this->byteLength != statedLength) {
-                throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
-                                        " bytes, but found " + to_string(dataURI.dataLength));
+                throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", to_string(statedLength),
+                                        " bytes, but found ", to_string(dataURI.dataLength));
             }
             }
         } else { // assume raw data
         } else { // assume raw data
             if (statedLength != dataURI.dataLength) {
             if (statedLength != dataURI.dataLength) {
-                throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
-                                        " bytes, but found " + to_string(dataURI.dataLength));
+                throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", to_string(statedLength),
+                                        " bytes, but found ", to_string(dataURI.dataLength));
             }
             }
 
 
             this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete<uint8_t[]>());
             this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete<uint8_t[]>());
@@ -339,9 +339,9 @@ inline void Buffer::Read(Value &obj, Asset &r) {
                 delete file;
                 delete file;
 
 
                 if (!ok)
                 if (!ok)
-                    throw DeadlyImportError("GLTF: error while reading referenced file \"" + std::string(uri) + "\"");
+                    throw DeadlyImportError("GLTF: error while reading referenced file \"", uri, "\"");
             } else {
             } else {
-                throw DeadlyImportError("GLTF: could not open referenced file \"" + std::string(uri) + "\"");
+                throw DeadlyImportError("GLTF: could not open referenced file \"", uri, "\"");
             }
             }
         }
         }
     }
     }
@@ -372,8 +372,8 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod
 
 
         char val[val_size];
         char val[val_size];
 
 
-        ai_snprintf(val, val_size, "%llu", (long long)pOffset);
-        throw DeadlyImportError(std::string("GLTF: incorrect offset value (") + val + ") for marking encoded region.");
+        ai_snprintf(val, val_size, AI_SIZEFMT, pOffset);
+        throw DeadlyImportError("GLTF: incorrect offset value (", val, ") for marking encoded region.");
     }
     }
 
 
     // Check length
     // Check length
@@ -382,8 +382,8 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod
 
 
         char val[val_size];
         char val[val_size];
 
 
-        ai_snprintf(val, val_size, "%llu, %llu", (long long)pOffset, (long long)pEncodedData_Length);
-        throw DeadlyImportError(std::string("GLTF: encoded region with offset/length (") + val + ") is out of range.");
+        ai_snprintf(val, val_size, AI_SIZEFMT "/" AI_SIZEFMT, pOffset, pEncodedData_Length);
+        throw DeadlyImportError("GLTF: encoded region with offset/length (", val, ") is out of range.");
     }
     }
 
 
     // Add new region
     // Add new region
@@ -403,7 +403,7 @@ inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) {
         }
         }
     }
     }
 
 
-    throw DeadlyImportError("GLTF: EncodedRegion with ID: \"" + pID + "\" not found.");
+    throw DeadlyImportError("GLTF: EncodedRegion with ID: \"", pID, "\" not found.");
 }
 }
 
 
 inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) {
 inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) {
@@ -836,8 +836,8 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
 
 
     if (json_extensions == nullptr) goto mr_skip_extensions;
     if (json_extensions == nullptr) goto mr_skip_extensions;
 
 
-    for (Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); it_memb++) {
 #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
 #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
+    for (Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); it_memb++) {
         if (it_memb->name.GetString() == std::string("Open3DGC-compression")) {
         if (it_memb->name.GetString() == std::string("Open3DGC-compression")) {
             // Search for compressed data.
             // Search for compressed data.
             // Compressed data contain description of part of "buffer" which is encoded. This part must be decoded and
             // Compressed data contain description of part of "buffer" which is encoded. This part must be decoded and
@@ -851,7 +851,7 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
 /************** Read data from JSON-document **************/
 /************** Read data from JSON-document **************/
 #define MESH_READ_COMPRESSEDDATA_MEMBER(pFieldName, pOut)                                                  \
 #define MESH_READ_COMPRESSEDDATA_MEMBER(pFieldName, pOut)                                                  \
     if (!ReadMember(*comp_data, pFieldName, pOut)) {                                                       \
     if (!ReadMember(*comp_data, pFieldName, pOut)) {                                                       \
-        throw DeadlyImportError(std::string("GLTF: \"compressedData\" must has \"") + pFieldName + "\"."); \
+        throw DeadlyImportError("GLTF: \"compressedData\" must has \"", pFieldName, "\"."); \
     }
     }
 
 
             const char *mode_str;
             const char *mode_str;
@@ -880,18 +880,18 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
             else if (strcmp(mode_str, "ascii") == 0)
             else if (strcmp(mode_str, "ascii") == 0)
                 ext_o3dgc->Binary = false;
                 ext_o3dgc->Binary = false;
             else
             else
-                throw DeadlyImportError(std::string("GLTF: for compressed data supported modes is: \"ascii\", \"binary\". Not the: \"") + mode_str + "\".");
+                throw DeadlyImportError("GLTF: for compressed data supported modes is: \"ascii\", \"binary\". Not the: \"", mode_str, "\".");
 
 
             /************************ Decoding ************************/
             /************************ Decoding ************************/
             Decode_O3DGC(*ext_o3dgc, pAsset_Root);
             Decode_O3DGC(*ext_o3dgc, pAsset_Root);
             Extension.push_back(ext_o3dgc); // store info in mesh extensions list.
             Extension.push_back(ext_o3dgc); // store info in mesh extensions list.
         } // if(it_memb->name.GetString() == "Open3DGC-compression")
         } // if(it_memb->name.GetString() == "Open3DGC-compression")
         else
         else
-#endif
         {
         {
-            throw DeadlyImportError(std::string("GLTF: Unknown mesh extension: \"") + it_memb->name.GetString() + "\".");
+            throw DeadlyImportError("GLTF: Unknown mesh extension: \"", it_memb->name.GetString(), "\".");
         }
         }
     } // for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); json_extensions++)
     } // for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); json_extensions++)
+#endif
 
 
 mr_skip_extensions:
 mr_skip_extensions:
 
 
@@ -923,24 +923,24 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
     size_t size_coordindex = ifs.GetNCoordIndex() * 3; // See float attributes note.
     size_t size_coordindex = ifs.GetNCoordIndex() * 3; // See float attributes note.
 
 
     if (primitives[0].indices->count != size_coordindex)
     if (primitives[0].indices->count != size_coordindex)
-        throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (" + to_string(size_coordindex) +
-                                ") not equal to uncompressed (" + to_string(primitives[0].indices->count) + ").");
+        throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (", to_string(size_coordindex),
+                                ") not equal to uncompressed (", to_string(primitives[0].indices->count), ").");
 
 
     size_coordindex *= sizeof(IndicesType);
     size_coordindex *= sizeof(IndicesType);
     // Coordinates
     // Coordinates
     size_t size_coord = ifs.GetNCoord(); // See float attributes note.
     size_t size_coord = ifs.GetNCoord(); // See float attributes note.
 
 
     if (primitives[0].attributes.position[0]->count != size_coord)
     if (primitives[0].attributes.position[0]->count != size_coord)
-        throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (" + to_string(size_coord) +
-                                ") not equal to uncompressed (" + to_string(primitives[0].attributes.position[0]->count) + ").");
+        throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (", to_string(size_coord),
+                                ") not equal to uncompressed (", to_string(primitives[0].attributes.position[0]->count), ").");
 
 
     size_coord *= 3 * sizeof(float);
     size_coord *= 3 * sizeof(float);
     // Normals
     // Normals
     size_t size_normal = ifs.GetNNormal(); // See float attributes note.
     size_t size_normal = ifs.GetNNormal(); // See float attributes note.
 
 
     if (primitives[0].attributes.normal[0]->count != size_normal)
     if (primitives[0].attributes.normal[0]->count != size_normal)
-        throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (" + to_string(size_normal) +
-                                ") not equal to uncompressed (" + to_string(primitives[0].attributes.normal[0]->count) + ").");
+        throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (", to_string(size_normal),
+                                ") not equal to uncompressed (", to_string(primitives[0].attributes.normal[0]->count), ").");
 
 
     size_normal *= 3 * sizeof(float);
     size_normal *= 3 * sizeof(float);
     // Additional attributes.
     // Additional attributes.
@@ -961,8 +961,8 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
                 // Check situation when encoded data contain texture coordinates but primitive not.
                 // Check situation when encoded data contain texture coordinates but primitive not.
                 if (idx_texcoord < primitives[0].attributes.texcoord.size()) {
                 if (idx_texcoord < primitives[0].attributes.texcoord.size()) {
                     if (primitives[0].attributes.texcoord[idx]->count != tval)
                     if (primitives[0].attributes.texcoord[idx]->count != tval)
-                        throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (" + to_string(tval) +
-                                                ") not equal to uncompressed (" + to_string(primitives[0].attributes.texcoord[idx]->count) + ").");
+                        throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (", to_string(tval),
+                                                ") not equal to uncompressed (", to_string(primitives[0].attributes.texcoord[idx]->count), ").");
 
 
                     idx_texcoord++;
                     idx_texcoord++;
                 } else {
                 } else {
@@ -971,7 +971,7 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
 
 
                 break;
                 break;
             default:
             default:
-                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
+                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: ", to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
         }
         }
 
 
         tval *= ifs.GetFloatAttributeDim(static_cast<unsigned long>(idx)) * sizeof(o3dgc::Real); // After checking count of objects we can get size of array.
         tval *= ifs.GetFloatAttributeDim(static_cast<unsigned long>(idx)) * sizeof(o3dgc::Real); // After checking count of objects we can get size of array.
@@ -990,7 +990,7 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
                 break;
                 break;
 
 
             default:
             default:
-                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
+                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: ", to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
         }
         }
 
 
         tval *= ifs.GetIntAttributeDim(static_cast<unsigned long>(idx)) * sizeof(long); // See float attributes note.
         tval *= ifs.GetIntAttributeDim(static_cast<unsigned long>(idx)) * sizeof(long); // See float attributes note.
@@ -1025,7 +1025,7 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
 
 
                 break;
                 break;
             default:
             default:
-                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
+                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: ", to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
         }
         }
     }
     }
 
 
@@ -1039,7 +1039,7 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG
 
 
             // ifs.SetIntAttribute(idx, (long* const)(decoded_data + get_buf_offset(primitives[0].attributes.joint)));
             // ifs.SetIntAttribute(idx, (long* const)(decoded_data + get_buf_offset(primitives[0].attributes.joint)));
             default:
             default:
-                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
+                throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: ", to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
         }
         }
     }
     }
 
 
@@ -1231,7 +1231,7 @@ inline void AssetMetadata::Read(Document &doc) {
     }
     }
 
 
     if (version.empty() || version[0] != '1') {
     if (version.empty() || version[0] != '1') {
-        throw DeadlyImportError("GLTF: Unsupported glTF version: " + version);
+        throw DeadlyImportError("GLTF: Unsupported glTF version: ", version);
     }
     }
 }
 }
 
 
@@ -1309,7 +1309,7 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) {
     if (doc.HasParseError()) {
     if (doc.HasParseError()) {
         char buffer[32];
         char buffer[32];
         ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset()));
         ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset()));
-        throw DeadlyImportError(std::string("GLTF: JSON parse error, offset ") + buffer + ": " + GetParseError_En(doc.GetParseError()));
+        throw DeadlyImportError("GLTF: JSON parse error, offset ", buffer, ": ", GetParseError_En(doc.GetParseError()));
     }
     }
 
 
     if (!doc.IsObject()) {
     if (!doc.IsObject()) {
@@ -1412,8 +1412,8 @@ inline std::string Asset::FindUniqueID(const std::string &str, const char *suffi
     return id;
     return id;
 }
 }
 
 
-#ifdef _WIN32
+#if _MSC_VER
 #    pragma warning(pop)
 #    pragma warning(pop)
-#endif // WIN32
+#endif // _MSC_VER
 
 
 } // namespace glTF
 } // namespace glTF

+ 1 - 1
code/AssetLib/glTF/glTFAssetWriter.h

@@ -50,7 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef GLTFASSETWRITER_H_INC
 #ifndef GLTFASSETWRITER_H_INC
 #define GLTFASSETWRITER_H_INC
 #define GLTFASSETWRITER_H_INC
 
 
-#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_IMPORTER)
 
 
 #include "glTFAsset.h"
 #include "glTFAsset.h"
 
 

+ 5 - 5
code/AssetLib/glTF/glTFAssetWriter.inl

@@ -43,10 +43,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <rapidjson/writer.h>
 #include <rapidjson/writer.h>
 #include <rapidjson/prettywriter.h>
 #include <rapidjson/prettywriter.h>
 
 
-#ifdef _WIN32
+#if _MSC_VER
 #    pragma warning(push)
 #    pragma warning(push)
 #    pragma warning( disable : 4706)
 #    pragma warning( disable : 4706)
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 namespace glTF {
 namespace glTF {
 
 
@@ -305,11 +305,11 @@ namespace glTF {
 			Value json_extensions;
 			Value json_extensions;
 
 
 			json_extensions.SetObject();
 			json_extensions.SetObject();
+#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
 			for(Mesh::SExtension* ptr_ext : m.Extension)
 			for(Mesh::SExtension* ptr_ext : m.Extension)
 			{
 			{
 				switch(ptr_ext->Type)
 				switch(ptr_ext->Type)
 				{
 				{
-#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
 					case Mesh::SExtension::EType::Compression_Open3DGC:
 					case Mesh::SExtension::EType::Compression_Open3DGC:
 						{
 						{
 							Value json_comp_data;
 							Value json_comp_data;
@@ -339,11 +339,11 @@ namespace glTF {
 						}
 						}
 
 
 						break;
 						break;
-#endif
 					default:
 					default:
 						throw DeadlyImportError("GLTF: Can not write mesh: unknown mesh extension, only Open3DGC is supported.");
 						throw DeadlyImportError("GLTF: Can not write mesh: unknown mesh extension, only Open3DGC is supported.");
 				}// switch(ptr_ext->Type)
 				}// switch(ptr_ext->Type)
 			}// for(Mesh::SExtension* ptr_ext : m.Extension)
 			}// for(Mesh::SExtension* ptr_ext : m.Extension)
+#endif
 
 
 			// Add extensions to mesh
 			// Add extensions to mesh
 			obj.AddMember("extensions", json_extensions, w.mAl);
 			obj.AddMember("extensions", json_extensions, w.mAl);
@@ -707,7 +707,7 @@ namespace glTF {
         w.WriteObjects(d);
         w.WriteObjects(d);
     }
     }
 
 
-#ifdef _WIN32
+#if _MSC_VER
 #    pragma warning(pop)
 #    pragma warning(pop)
 #endif // _WIN32
 #endif // _WIN32
 
 

+ 4 - 0
code/AssetLib/glTF/glTFCommon.cpp

@@ -38,6 +38,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 */
 */
+#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+
 #include "AssetLib/glTF/glTFCommon.h"
 #include "AssetLib/glTF/glTFCommon.h"
 
 
 namespace glTFCommon {
 namespace glTFCommon {
@@ -187,3 +189,5 @@ bool ParseDataURI(const char *const_uri, size_t uriLen, DataURI &out) {
 
 
 } // namespace Util
 } // namespace Util
 } // namespace glTFCommon
 } // namespace glTFCommon
+
+#endif

+ 4 - 6
code/AssetLib/glTF/glTFCommon.h

@@ -52,8 +52,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
 
 
-#define RAPIDJSON_HAS_STDSTRING 1
-#define RAPIDJSON_NOMEMBERITERATORCLASS
 #include <rapidjson/document.h>
 #include <rapidjson/document.h>
 #include <rapidjson/error/en.h>
 #include <rapidjson/error/en.h>
 #include <rapidjson/rapidjson.h>
 #include <rapidjson/rapidjson.h>
@@ -190,10 +188,10 @@ inline void CopyValue(const glTFCommon::mat4 &v, aiMatrix4x4 &o) {
     o.d4 = v[15];
     o.d4 = v[15];
 }
 }
 
 
-#ifdef _WIN32
+#if _MSC_VER
 #    pragma warning(push)
 #    pragma warning(push)
 #    pragma warning(disable : 4310)
 #    pragma warning(disable : 4310)
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 inline std::string getCurrentAssetDir(const std::string &pFile) {
 inline std::string getCurrentAssetDir(const std::string &pFile) {
     std::string path = pFile;
     std::string path = pFile;
@@ -204,9 +202,9 @@ inline std::string getCurrentAssetDir(const std::string &pFile) {
 
 
     return path;
     return path;
 }
 }
-#ifdef _WIN32
+#if _MSC_VER
 #    pragma warning(pop)
 #    pragma warning(pop)
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 namespace Util {
 namespace Util {
 
 

+ 22 - 2
code/AssetLib/glTF/glTFExporter.cpp

@@ -525,6 +525,12 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref<Mesh>& meshRef, Ref<Buf
     delete[] vertexJointData;
     delete[] vertexJointData;
 }
 }
 
 
+#if defined(__has_warning)
+#if __has_warning("-Wunused-but-set-variable")
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
+#endif
+
 void glTFExporter::ExportMeshes()
 void glTFExporter::ExportMeshes()
 {
 {
     // Not for
     // Not for
@@ -536,10 +542,12 @@ void glTFExporter::ExportMeshes()
 
 
     // Variables needed for compression. BEGIN.
     // Variables needed for compression. BEGIN.
     // Indices, not pointers - because pointer to buffer is changing while writing to it.
     // Indices, not pointers - because pointer to buffer is changing while writing to it.
+#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
     size_t idx_srcdata_begin = 0; // Index of buffer before writing mesh data. Also, index of begin of coordinates array in buffer.
     size_t idx_srcdata_begin = 0; // Index of buffer before writing mesh data. Also, index of begin of coordinates array in buffer.
     size_t idx_srcdata_normal = SIZE_MAX;// Index of begin of normals array in buffer. SIZE_MAX - mean that mesh has no normals.
     size_t idx_srcdata_normal = SIZE_MAX;// Index of begin of normals array in buffer. SIZE_MAX - mean that mesh has no normals.
-    std::vector<size_t> idx_srcdata_tc;// Array of indices. Every index point to begin of texture coordinates array in buffer.
     size_t idx_srcdata_ind;// Index of begin of coordinates indices array in buffer.
     size_t idx_srcdata_ind;// Index of begin of coordinates indices array in buffer.
+#endif
+    std::vector<size_t> idx_srcdata_tc;// Array of indices. Every index point to begin of texture coordinates array in buffer.
     bool comp_allow;// Point that data of current mesh can be compressed.
     bool comp_allow;// Point that data of current mesh can be compressed.
     // Variables needed for compression. END.
     // Variables needed for compression. END.
 
 
@@ -609,13 +617,17 @@ void glTFExporter::ExportMeshes()
 
 
 		/******************* Vertices ********************/
 		/******************* Vertices ********************/
 		// If compression is used then you need parameters of uncompressed region: begin and size. At this step "begin" is stored.
 		// If compression is used then you need parameters of uncompressed region: begin and size. At this step "begin" is stored.
+#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
 		if(comp_allow) idx_srcdata_begin = b->byteLength;
 		if(comp_allow) idx_srcdata_begin = b->byteLength;
+#endif
 
 
         Ref<Accessor> v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
         Ref<Accessor> v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
 		if (v) p.attributes.position.push_back(v);
 		if (v) p.attributes.position.push_back(v);
 
 
 		/******************** Normals ********************/
 		/******************** Normals ********************/
+#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
 		if(comp_allow && (aim->mNormals != 0)) idx_srcdata_normal = b->byteLength;// Store index of normals array.
 		if(comp_allow && (aim->mNormals != 0)) idx_srcdata_normal = b->byteLength;// Store index of normals array.
+#endif
 
 
 		Ref<Accessor> n = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mNormals, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
 		Ref<Accessor> n = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mNormals, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
 		if (n) p.attributes.normal.push_back(n);
 		if (n) p.attributes.normal.push_back(n);
@@ -640,7 +652,9 @@ void glTFExporter::ExportMeshes()
 		}
 		}
 
 
 		/*************** Vertices indices ****************/
 		/*************** Vertices indices ****************/
+#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
 		idx_srcdata_ind = b->byteLength;// Store index of indices array.
 		idx_srcdata_ind = b->byteLength;// Store index of indices array.
+#endif
 
 
 		if (aim->mNumFaces > 0) {
 		if (aim->mNumFaces > 0) {
 			std::vector<IndicesType> indices;
 			std::vector<IndicesType> indices;
@@ -677,7 +691,7 @@ void glTFExporter::ExportMeshes()
 		{
 		{
 #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
 #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
 			// Only one type of compression supported at now - Open3DGC.
 			// Only one type of compression supported at now - Open3DGC.
-			//
+		//
 			o3dgc::BinaryStream bs;
 			o3dgc::BinaryStream bs;
 			o3dgc::SC3DMCEncoder<IndicesType> encoder;
 			o3dgc::SC3DMCEncoder<IndicesType> encoder;
 			o3dgc::IndexedFaceSet<IndicesType> comp_o3dgc_ifs;
 			o3dgc::IndexedFaceSet<IndicesType> comp_o3dgc_ifs;
@@ -793,6 +807,12 @@ void glTFExporter::ExportMeshes()
     }
     }
 }
 }
 
 
+#if defined(__has_warning)
+#if __has_warning("-Wunused-but-set-variable")
+#pragma GCC diagnostic pop
+#endif
+#endif
+
 /*
 /*
  * Export the root node of the node hierarchy.
  * Export the root node of the node hierarchy.
  * Calls ExportNode for all children.
  * Calls ExportNode for all children.

+ 1 - 1
code/AssetLib/glTF/glTFExporter.h

@@ -46,7 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_GLTFEXPORTER_H_INC
 #ifndef AI_GLTFEXPORTER_H_INC
 #define AI_GLTFEXPORTER_H_INC
 #define AI_GLTFEXPORTER_H_INC
 
 
-#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
+#if !defined(ASSIMP_BUILD_NO_GLTF_EXPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_EXPORTER)
 
 
 #include <assimp/types.h>
 #include <assimp/types.h>
 #include <assimp/material.h>
 #include <assimp/material.h>

+ 5 - 5
code/AssetLib/glTF/glTFImporter.cpp

@@ -1,4 +1,4 @@
-/*
+/*
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
@@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 */
 */
 
 
-#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_IMPORTER)
 
 
 #include "AssetLib/glTF/glTFImporter.h"
 #include "AssetLib/glTF/glTFImporter.h"
 #include "AssetLib/glTF/glTFAsset.h"
 #include "AssetLib/glTF/glTFAsset.h"
@@ -215,8 +215,8 @@ void glTFImporter::ImportMeshes(glTF::Asset &r) {
 
 
         // Check if mesh extensions is used
         // Check if mesh extensions is used
         if (mesh.Extension.size() > 0) {
         if (mesh.Extension.size() > 0) {
-            for (Mesh::SExtension *cur_ext : mesh.Extension) {
 #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
 #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
+            for (Mesh::SExtension *cur_ext : mesh.Extension) {
                 if (cur_ext->Type == Mesh::SExtension::EType::Compression_Open3DGC) {
                 if (cur_ext->Type == Mesh::SExtension::EType::Compression_Open3DGC) {
                     // Limitations for meshes when using Open3DGC-compression.
                     // Limitations for meshes when using Open3DGC-compression.
                     // It's a current limitation of sp... Specification have not this part still - about mesh compression. Why only one primitive?
                     // It's a current limitation of sp... Specification have not this part still - about mesh compression. Why only one primitive?
@@ -233,12 +233,12 @@ void glTFImporter::ImportMeshes(glTF::Asset &r) {
 
 
                     buf->EncodedRegion_SetCurrent(mesh.id);
                     buf->EncodedRegion_SetCurrent(mesh.id);
                 } else
                 } else
-#endif
                 {
                 {
-                    throw DeadlyImportError("GLTF: Can not import mesh: unknown mesh extension (code: \"" + to_string(cur_ext->Type) +
+                    throw DeadlyImportError("GLTF: Can not import mesh: unknown mesh extension (code: \"", to_string(cur_ext->Type),
                                             "\"), only Open3DGC is supported.");
                                             "\"), only Open3DGC is supported.");
                 }
                 }
             }
             }
+#endif
         } // if(mesh.Extension.size() > 0)
         } // if(mesh.Extension.size() > 0)
 
 
         meshOffsets.push_back(k);
         meshOffsets.push_back(k);

+ 7 - 12
code/AssetLib/glTF2/glTF2Asset.h

@@ -1,4 +1,4 @@
-/*
+/*
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
@@ -50,7 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef GLTF2ASSET_H_INC
 #ifndef GLTF2ASSET_H_INC
 #define GLTF2ASSET_H_INC
 #define GLTF2ASSET_H_INC
 
 
-#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
 
 
 #include <assimp/Exceptional.h>
 #include <assimp/Exceptional.h>
 
 
@@ -62,19 +62,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
 
 
-#ifndef RAPIDJSON_HAS_STDSTRING
-#define RAPIDJSON_HAS_STDSTRING 1
-#endif
-
 #if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0)
 #if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wclass-memaccess"
 #pragma GCC diagnostic ignored "-Wclass-memaccess"
 #endif
 #endif
 
 
-#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
-#define RAPIDJSON_NOMEMBERITERATORCLASS
-#endif 
-
 #include <rapidjson/document.h>
 #include <rapidjson/document.h>
 #include <rapidjson/error/en.h>
 #include <rapidjson/error/en.h>
 #include <rapidjson/rapidjson.h>
 #include <rapidjson/rapidjson.h>
@@ -202,7 +194,7 @@ inline unsigned int ComponentTypeSize(ComponentType t) {
     case ComponentType_UNSIGNED_BYTE:
     case ComponentType_UNSIGNED_BYTE:
         return 1;
         return 1;
     default:
     default:
-        throw DeadlyImportError("GLTF: Unsupported Component Type " + to_string(t));
+        throw DeadlyImportError("GLTF: Unsupported Component Type ", to_string(t));
     }
     }
 }
 }
 
 
@@ -406,6 +398,8 @@ struct Accessor : public Object {
     void ExtractData(T *&outData);
     void ExtractData(T *&outData);
 
 
     void WriteData(size_t count, const void *src_buffer, size_t src_stride);
     void WriteData(size_t count, const void *src_buffer, size_t src_stride);
+    void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride);
+    void WriteSparseIndices(size_t count, const void *src_idx, size_t src_idxStride);
 
 
     //! Helper class to iterate the data
     //! Helper class to iterate the data
     class Indexer {
     class Indexer {
@@ -802,7 +796,7 @@ struct CustomExtension : public Object {
     Nullable<std::vector<CustomExtension>> mValues;
     Nullable<std::vector<CustomExtension>> mValues;
 
 
     operator bool() const {
     operator bool() const {
-        return Size();
+        return Size() != 0;
     }
     }
 
 
     size_t Size() const {
     size_t Size() const {
@@ -1066,6 +1060,7 @@ public:
     } extensionsRequired;
     } extensionsRequired;
 
 
     AssetMetadata asset;
     AssetMetadata asset;
+    Value* extras = nullptr;
 
 
     // Dictionaries for each type of object
     // Dictionaries for each type of object
 
 

+ 59 - 25
code/AssetLib/glTF2/glTF2Asset.inl

@@ -269,21 +269,21 @@ Ref<T> LazyDict<T>::Retrieve(unsigned int i) {
 
 
     // read it from the JSON object
     // read it from the JSON object
     if (!mDict) {
     if (!mDict) {
-        throw DeadlyImportError("GLTF: Missing section \"" + std::string(mDictId) + "\"");
+        throw DeadlyImportError("GLTF: Missing section \"", mDictId, "\"");
     }
     }
 
 
     if (!mDict->IsArray()) {
     if (!mDict->IsArray()) {
-        throw DeadlyImportError("GLTF: Field is not an array \"" + std::string(mDictId) + "\"");
+        throw DeadlyImportError("GLTF: Field is not an array \"", mDictId, "\"");
     }
     }
 
 
     Value &obj = (*mDict)[i];
     Value &obj = (*mDict)[i];
 
 
     if (!obj.IsObject()) {
     if (!obj.IsObject()) {
-        throw DeadlyImportError("GLTF: Object at index \"" + to_string(i) + "\" is not a JSON object");
+        throw DeadlyImportError("GLTF: Object at index \"", to_string(i), "\" is not a JSON object");
     }
     }
 
 
     if (mRecursiveReferenceCheck.find(i) != mRecursiveReferenceCheck.end()) {
     if (mRecursiveReferenceCheck.find(i) != mRecursiveReferenceCheck.end()) {
-        throw DeadlyImportError("GLTF: Object at index \"" + to_string(i) + "\" has recursive reference to itself");
+        throw DeadlyImportError("GLTF: Object at index \"", to_string(i), "\" has recursive reference to itself");
     }
     }
     mRecursiveReferenceCheck.insert(i);
     mRecursiveReferenceCheck.insert(i);
 
 
@@ -381,13 +381,13 @@ inline void Buffer::Read(Value &obj, Asset &r) {
             this->mData.reset(data, std::default_delete<uint8_t[]>());
             this->mData.reset(data, std::default_delete<uint8_t[]>());
 
 
             if (statedLength > 0 && this->byteLength != statedLength) {
             if (statedLength > 0 && this->byteLength != statedLength) {
-                throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
-                                        " bytes, but found " + to_string(dataURI.dataLength));
+                throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", to_string(statedLength),
+                                        " bytes, but found ", to_string(dataURI.dataLength));
             }
             }
         } else { // assume raw data
         } else { // assume raw data
             if (statedLength != dataURI.dataLength) {
             if (statedLength != dataURI.dataLength) {
-                throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
-                                        " bytes, but found " + to_string(dataURI.dataLength));
+                throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", to_string(statedLength),
+                                        " bytes, but found ", to_string(dataURI.dataLength));
             }
             }
 
 
             this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete<uint8_t[]>());
             this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete<uint8_t[]>());
@@ -403,9 +403,9 @@ inline void Buffer::Read(Value &obj, Asset &r) {
                 delete file;
                 delete file;
 
 
                 if (!ok)
                 if (!ok)
-                    throw DeadlyImportError("GLTF: error while reading referenced file \"" + std::string(uri) + "\"");
+                    throw DeadlyImportError("GLTF: error while reading referenced file \"", uri, "\"");
             } else {
             } else {
-                throw DeadlyImportError("GLTF: could not open referenced file \"" + std::string(uri) + "\"");
+                throw DeadlyImportError("GLTF: could not open referenced file \"", uri, "\"");
             }
             }
         }
         }
     }
     }
@@ -436,8 +436,8 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod
 
 
         char val[val_size];
         char val[val_size];
 
 
-        ai_snprintf(val, val_size, "%llu", (long long)pOffset);
-        throw DeadlyImportError(std::string("GLTF: incorrect offset value (") + val + ") for marking encoded region.");
+        ai_snprintf(val, val_size, AI_SIZEFMT, pOffset);
+        throw DeadlyImportError("GLTF: incorrect offset value (", val, ") for marking encoded region.");
     }
     }
 
 
     // Check length
     // Check length
@@ -446,8 +446,8 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod
 
 
         char val[val_size];
         char val[val_size];
 
 
-        ai_snprintf(val, val_size, "%llu, %llu", (long long)pOffset, (long long)pEncodedData_Length);
-        throw DeadlyImportError(std::string("GLTF: encoded region with offset/length (") + val + ") is out of range.");
+        ai_snprintf(val, val_size, AI_SIZEFMT "/" AI_SIZEFMT, pOffset, pEncodedData_Length);
+        throw DeadlyImportError("GLTF: encoded region with offset/length (", val, ") is out of range.");
     }
     }
 
 
     // Add new region
     // Add new region
@@ -467,7 +467,7 @@ inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) {
         }
         }
     }
     }
 
 
-    throw DeadlyImportError("GLTF: EncodedRegion with ID: \"" + pID + "\" not found.");
+    throw DeadlyImportError("GLTF: EncodedRegion with ID: \"", pID, "\" not found.");
 }
 }
 
 
 inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) {
 inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) {
@@ -731,8 +731,14 @@ void Accessor::ExtractData(T *&outData) {
     const size_t stride = bufferView && bufferView->byteStride ? bufferView->byteStride : elemSize;
     const size_t stride = bufferView && bufferView->byteStride ? bufferView->byteStride : elemSize;
 
 
     const size_t targetElemSize = sizeof(T);
     const size_t targetElemSize = sizeof(T);
-    ai_assert(elemSize <= targetElemSize);
-    ai_assert(count * stride <= (bufferView ? bufferView->byteLength : sparse->data.size()));
+
+    if (elemSize > targetElemSize) {
+        throw DeadlyImportError("GLTF: elemSize > targetElemSize");
+    }
+
+    if (count*stride > (bufferView ? bufferView->byteLength : sparse->data.size())) {
+        throw DeadlyImportError("GLTF: count*stride out of range");
+    }
 
 
     outData = new T[count];
     outData = new T[count];
     if (stride == elemSize && targetElemSize == elemSize) {
     if (stride == elemSize && targetElemSize == elemSize) {
@@ -757,6 +763,33 @@ inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t sr
     CopyData(_count, src, src_stride, dst, dst_stride);
     CopyData(_count, src, src_stride, dst, dst_stride);
 }
 }
 
 
+inline void Accessor::WriteSparseValues(size_t _count, const void *src_data, size_t src_dataStride) {
+    if (!sparse)
+        return;
+
+    // values
+    uint8_t *value_buffer_ptr = sparse->values->buffer->GetPointer();
+    size_t value_offset = sparse->valuesByteOffset + sparse->values->byteOffset;
+    size_t value_dst_stride = GetNumComponents() * GetBytesPerComponent();
+    const uint8_t *value_src = reinterpret_cast<const uint8_t *>(src_data);
+    uint8_t *value_dst = reinterpret_cast<uint8_t *>(value_buffer_ptr + value_offset);
+    ai_assert(value_dst + _count * value_dst_stride <= value_buffer_ptr + sparse->values->buffer->byteLength);
+    CopyData(_count, value_src, src_dataStride, value_dst, value_dst_stride);
+}
+
+inline void Accessor::WriteSparseIndices(size_t _count, const void *src_idx, size_t src_idxStride) {
+    if (!sparse)
+        return;
+
+    // indices
+    uint8_t *indices_buffer_ptr = sparse->indices->buffer->GetPointer();
+    size_t indices_offset = sparse->indicesByteOffset + sparse->indices->byteOffset;
+    size_t indices_dst_stride = 1 * sizeof(unsigned short);
+    const uint8_t *indices_src = reinterpret_cast<const uint8_t *>(src_idx);
+    uint8_t *indices_dst = reinterpret_cast<uint8_t *>(indices_buffer_ptr + indices_offset);
+    ai_assert(indices_dst + _count * indices_dst_stride <= indices_buffer_ptr + sparse->indices->buffer->byteLength);
+    CopyData(_count, indices_src, src_idxStride, indices_dst, indices_dst_stride);
+}
 inline Accessor::Indexer::Indexer(Accessor &acc) :
 inline Accessor::Indexer::Indexer(Accessor &acc) :
         accessor(acc),
         accessor(acc),
         data(acc.GetPointer()),
         data(acc.GetPointer()),
@@ -1015,10 +1048,10 @@ inline int Compare(const char *attr, const char (&str)[N]) {
     return (strncmp(attr, str, N - 1) == 0) ? N - 1 : 0;
     return (strncmp(attr, str, N - 1) == 0) ? N - 1 : 0;
 }
 }
 
 
-#ifdef _WIN32
+#if _MSC_VER
 #pragma warning(push)
 #pragma warning(push)
 #pragma warning(disable : 4706)
 #pragma warning(disable : 4706)
-#endif // _WIN32
+#endif // _MSC_VER
 
 
 inline bool GetAttribVector(Mesh::Primitive &p, const char *attr, Mesh::AccessorList *&v, int &pos) {
 inline bool GetAttribVector(Mesh::Primitive &p, const char *attr, Mesh::AccessorList *&v, int &pos) {
     if ((pos = Compare(attr, "POSITION"))) {
     if ((pos = Compare(attr, "POSITION"))) {
@@ -1287,6 +1320,8 @@ inline void Node::Read(Value &obj, Asset &r) {
         }
         }
     }
     }
 
 
+	// Do not retrieve a skin here, just take a reference, to avoid infinite recursion
+    // Skins will be properly loaded later
     Value *curSkin = FindUInt(obj, "skin");
     Value *curSkin = FindUInt(obj, "skin");
     if (nullptr != curSkin) {
     if (nullptr != curSkin) {
         this->skin = r.skins.Get(curSkin->GetUint());
         this->skin = r.skins.Get(curSkin->GetUint());
@@ -1429,7 +1464,7 @@ inline void AssetMetadata::Read(Document &doc) {
     }
     }
 
 
     if (version.empty() || version[0] != '2') {
     if (version.empty() || version[0] != '2') {
-        throw DeadlyImportError("GLTF: Unsupported glTF version: " + version);
+        throw DeadlyImportError("GLTF: Unsupported glTF version: ", version);
     }
     }
 }
 }
 
 
@@ -1541,7 +1576,7 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) {
     if (doc.HasParseError()) {
     if (doc.HasParseError()) {
         char buffer[32];
         char buffer[32];
         ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset()));
         ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset()));
-        throw DeadlyImportError(std::string("GLTF: JSON parse error, offset ") + buffer + ": " + GetParseError_En(doc.GetParseError()));
+        throw DeadlyImportError("GLTF: JSON parse error, offset ", buffer, ": ", GetParseError_En(doc.GetParseError()));
     }
     }
 
 
     if (!doc.IsObject()) {
     if (!doc.IsObject()) {
@@ -1584,7 +1619,6 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) {
         }
         }
     }
     }
 
 
-    // Read skins after nodes have been loaded to avoid infinite recursion
     if (Value *skinsArray = FindArray(doc, "skins")) {
     if (Value *skinsArray = FindArray(doc, "skins")) {
         for (unsigned int i = 0; i < skinsArray->Size(); ++i) {
         for (unsigned int i = 0; i < skinsArray->Size(); ++i) {
             skins.Retrieve(i);
             skins.Retrieve(i);
@@ -1695,8 +1729,8 @@ inline std::string Asset::FindUniqueID(const std::string &str, const char *suffi
     return id;
     return id;
 }
 }
 
 
-#ifdef _WIN32
-#pragma warning(pop)
-#endif // _WIN32
+#if _MSC_VER
+#   pragma warning(pop)
+#endif // _MSC_VER
 
 
 } // namespace glTF2
 } // namespace glTF2

+ 1 - 1
code/AssetLib/glTF2/glTF2AssetWriter.h

@@ -50,7 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef GLTF2ASSETWRITER_H_INC
 #ifndef GLTF2ASSETWRITER_H_INC
 #define GLTF2ASSETWRITER_H_INC
 #define GLTF2ASSETWRITER_H_INC
 
 
-#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
 
 
 #include "glTF2Asset.h"
 #include "glTF2Asset.h"
 
 

+ 47 - 14
code/AssetLib/glTF2/glTF2AssetWriter.inl

@@ -1,4 +1,4 @@
-/*
+/*
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
@@ -107,21 +107,47 @@ namespace glTF2 {
 
 
     inline void Write(Value& obj, Accessor& a, AssetWriter& w)
     inline void Write(Value& obj, Accessor& a, AssetWriter& w)
     {
     {
-        obj.AddMember("bufferView", a.bufferView->index, w.mAl);
-        obj.AddMember("byteOffset", (unsigned int)a.byteOffset, w.mAl);
+        if (a.bufferView) {
+            obj.AddMember("bufferView", a.bufferView->index, w.mAl);
+            obj.AddMember("byteOffset", (unsigned int)a.byteOffset, w.mAl);
+            Value vTmpMax, vTmpMin;
+            if (a.componentType == ComponentType_FLOAT) {
+                obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl);
+                obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl);
+            } else {
+                obj.AddMember("max", MakeValueCast<int64_t>(vTmpMax, a.max, w.mAl), w.mAl);
+                obj.AddMember("min", MakeValueCast<int64_t>(vTmpMin, a.min, w.mAl), w.mAl);
+            }
+        }
 
 
         obj.AddMember("componentType", int(a.componentType), w.mAl);
         obj.AddMember("componentType", int(a.componentType), w.mAl);
         obj.AddMember("count", (unsigned int)a.count, w.mAl);
         obj.AddMember("count", (unsigned int)a.count, w.mAl);
         obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl);
         obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl);
 
 
-        Value vTmpMax, vTmpMin;
-		if (a.componentType == ComponentType_FLOAT) {
-			obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl);
-			obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl);
-		} else {
-			obj.AddMember("max", MakeValueCast<int64_t>(vTmpMax, a.max, w.mAl), w.mAl);
-			obj.AddMember("min", MakeValueCast<int64_t>(vTmpMin, a.min, w.mAl), w.mAl);
-		}
+        if (a.sparse) {
+            Value sparseValue;
+            sparseValue.SetObject();
+
+            //count
+            sparseValue.AddMember("count", (unsigned int)a.sparse->count, w.mAl);
+
+            //indices
+            Value indices;
+            indices.SetObject();
+            indices.AddMember("bufferView", a.sparse->indices->index, w.mAl);
+            indices.AddMember("byteOffset", (unsigned int)a.sparse->indicesByteOffset, w.mAl);
+            indices.AddMember("componentType", int(a.sparse->indicesType), w.mAl);
+            sparseValue.AddMember("indices", indices, w.mAl);
+
+            //values
+            Value values;
+            values.SetObject();
+            values.AddMember("bufferView", a.sparse->values->index, w.mAl);
+            values.AddMember("byteOffset", (unsigned int)a.sparse->valuesByteOffset, w.mAl);
+            sparseValue.AddMember("values", values, w.mAl);
+
+            obj.AddMember("sparse", sparseValue, w.mAl);
+        }
     }
     }
 
 
     inline void Write(Value& obj, Animation& a, AssetWriter& w)
     inline void Write(Value& obj, Animation& a, AssetWriter& w)
@@ -616,6 +642,10 @@ namespace glTF2 {
         if (mAsset.scene) {
         if (mAsset.scene) {
             mDoc.AddMember("scene", mAsset.scene->index, mAl);
             mDoc.AddMember("scene", mAsset.scene->index, mAl);
         }
         }
+        
+        if(mAsset.extras) {
+            mDoc.AddMember("extras", *mAsset.extras, mAl);
+        }
     }
     }
 
 
     inline void AssetWriter::WriteFile(const char* path)
     inline void AssetWriter::WriteFile(const char* path)
@@ -709,10 +739,13 @@ namespace glTF2 {
         // Binary chunk
         // Binary chunk
         //
         //
 
 
+        int GLB_Chunk_count = 1;
         uint32_t binaryChunkLength = 0;
         uint32_t binaryChunkLength = 0;
         if (bodyBuffer->byteLength > 0) {
         if (bodyBuffer->byteLength > 0) {
             binaryChunkLength = (bodyBuffer->byteLength + 3) & ~3; // Round up to next multiple of 4
             binaryChunkLength = (bodyBuffer->byteLength + 3) & ~3; // Round up to next multiple of 4
-            //auto curPaddingLength = binaryChunkLength - bodyBuffer->byteLength;
+            
+            auto curPaddingLength = binaryChunkLength - bodyBuffer->byteLength;
+            ++GLB_Chunk_count;
 
 
             GLB_Chunk binaryChunk;
             GLB_Chunk binaryChunk;
             binaryChunk.chunkLength = binaryChunkLength;
             binaryChunk.chunkLength = binaryChunkLength;
@@ -727,7 +760,7 @@ namespace glTF2 {
             if (outfile->Write(bodyBuffer->GetPointer(), 1, bodyBuffer->byteLength) != bodyBuffer->byteLength) {
             if (outfile->Write(bodyBuffer->GetPointer(), 1, bodyBuffer->byteLength) != bodyBuffer->byteLength) {
                 throw DeadlyExportError("Failed to write body data!");
                 throw DeadlyExportError("Failed to write body data!");
             }
             }
-            if (paddingLength && outfile->Write(&padding, 1, paddingLength) != paddingLength) {
+            if (curPaddingLength && outfile->Write(&padding, 1, paddingLength) != paddingLength) {
                 throw DeadlyExportError("Failed to write body data padding!");
                 throw DeadlyExportError("Failed to write body data padding!");
             }
             }
         }
         }
@@ -742,7 +775,7 @@ namespace glTF2 {
         header.version = 2;
         header.version = 2;
         AI_SWAP4(header.version);
         AI_SWAP4(header.version);
 
 
-        header.length = uint32_t(sizeof(GLB_Header) + 2 * sizeof(GLB_Chunk) + jsonChunkLength + binaryChunkLength);
+        header.length = uint32_t(sizeof(GLB_Header) + GLB_Chunk_count * sizeof(GLB_Chunk) + jsonChunkLength + binaryChunkLength);
         AI_SWAP4(header.length);
         AI_SWAP4(header.length);
 
 
         outfile->Seek(0, aiOrigin_SET);
         outfile->Seek(0, aiOrigin_SET);

+ 186 - 10
code/AssetLib/glTF2/glTF2Exporter.cpp

@@ -1,4 +1,4 @@
-/*
+/*
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
@@ -115,7 +115,14 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai
     ExportScene();
     ExportScene();
 
 
     ExportAnimations();
     ExportAnimations();
-
+    
+    // export extras
+    if(mProperties->HasPropertyCallback("extras"))
+    {
+        std::function<void*(void*)> ExportExtras = mProperties->GetPropertyCallback("extras");
+        mAsset->extras = (rapidjson::Value*)ExportExtras(0);
+    }
+    
     AssetWriter writer(*mAsset);
     AssetWriter writer(*mAsset);
 
 
     if (isBinary) {
     if (isBinary) {
@@ -214,6 +221,158 @@ inline void SetAccessorRange(ComponentType compType, Ref<Accessor> acc, void* da
 	}
 	}
 }
 }
 
 
+// compute the (data-dataBase), store the non-zero data items
+template <typename T>
+size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn, unsigned int numCompsOut, void *&outputNZDiff, void *&outputNZIdx) {
+    std::vector<T> vNZDiff;
+    std::vector<unsigned short> vNZIdx;
+    size_t totalComps = count * numCompsIn;
+    T *bufferData_ptr = static_cast<T *>(data);
+    T *bufferData_end = bufferData_ptr + totalComps;
+    T *bufferBase_ptr = static_cast<T *>(dataBase);
+
+    // Search and set extreme values.
+    for (short idx = 0; bufferData_ptr < bufferData_end; idx += 1, bufferData_ptr += numCompsIn) {
+        bool bNonZero = false;
+
+        //for the data, check any component Non Zero
+        for (unsigned int j = 0; j < numCompsOut; j++) {
+            double valueData = bufferData_ptr[j];
+            double valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0;
+            if ((valueData - valueBase) != 0) {
+                bNonZero = true;
+                break;
+            }
+        }
+
+        //all zeros, continue
+        if (!bNonZero)
+            continue;
+
+        //non zero, store the data
+        for (unsigned int j = 0; j < numCompsOut; j++) {
+            T valueData = bufferData_ptr[j];
+            T valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0;
+            vNZDiff.push_back(valueData - valueBase);
+        }
+        vNZIdx.push_back(idx);
+    }
+
+    //avoid all-0, put 1 item
+    if (vNZDiff.size() == 0) {
+        for (unsigned int j = 0; j < numCompsOut; j++)
+            vNZDiff.push_back(0);
+        vNZIdx.push_back(0);
+    }
+
+    //process data
+    outputNZDiff = new T[vNZDiff.size()];
+    memcpy(outputNZDiff, vNZDiff.data(), vNZDiff.size() * sizeof(T));
+
+    outputNZIdx = new unsigned short[vNZIdx.size()];
+    memcpy(outputNZIdx, vNZIdx.data(), vNZIdx.size() * sizeof(unsigned short));
+    return vNZIdx.size();
+}
+
+inline size_t NZDiff(ComponentType compType, void *data, void *dataBase, size_t count, unsigned int numCompsIn, unsigned int numCompsOut, void *&nzDiff, void *&nzIdx) {
+    switch (compType) {
+    case ComponentType_SHORT:
+        return NZDiff<short>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx);
+    case ComponentType_UNSIGNED_SHORT:
+        return NZDiff<unsigned short>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx);
+    case ComponentType_UNSIGNED_INT:
+        return NZDiff<unsigned int>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx);
+    case ComponentType_FLOAT:
+        return NZDiff<float>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx);
+    case ComponentType_BYTE:
+        return NZDiff<int8_t>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx);
+    case ComponentType_UNSIGNED_BYTE:
+        return NZDiff<uint8_t>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx);
+    }
+    return 0;
+}
+
+inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffer> &buffer,
+        size_t count, void *data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, BufferViewTarget target = BufferViewTarget_NONE, void *dataBase = 0) {
+    if (!count || !data) {
+        return Ref<Accessor>();
+    }
+
+    unsigned int numCompsIn = AttribType::GetNumComponents(typeIn);
+    unsigned int numCompsOut = AttribType::GetNumComponents(typeOut);
+    unsigned int bytesPerComp = ComponentTypeSize(compType);
+
+    // accessor
+    Ref<Accessor> acc = a.accessors.Create(a.FindUniqueID(meshName, "accessor"));
+
+    // if there is a basic data vector
+    if (dataBase) {
+        size_t base_offset = buffer->byteLength;
+        size_t base_padding = base_offset % bytesPerComp;
+        base_offset += base_padding;
+        size_t base_length = count * numCompsOut * bytesPerComp;
+        buffer->Grow(base_length + base_padding);
+
+        Ref<BufferView> bv = a.bufferViews.Create(a.FindUniqueID(meshName, "view"));
+        bv->buffer = buffer;
+        bv->byteOffset = base_offset;
+        bv->byteLength = base_length; //! The target that the WebGL buffer should be bound to.
+        bv->byteStride = 0;
+        bv->target = target;
+        acc->bufferView = bv;
+        acc->WriteData(count, dataBase, numCompsIn * bytesPerComp);
+    }
+    acc->byteOffset = 0;
+    acc->componentType = compType;
+    acc->count = count;
+    acc->type = typeOut;
+
+    if (data) {
+        void *nzDiff = 0, *nzIdx = 0;
+        size_t nzCount = NZDiff(compType, data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx);
+        acc->sparse.reset(new Accessor::Sparse);
+        acc->sparse->count = nzCount;
+
+        //indices
+        unsigned int bytesPerIdx = sizeof(unsigned short);
+        size_t indices_offset = buffer->byteLength;
+        size_t indices_padding = indices_offset % bytesPerIdx;
+        indices_offset += indices_padding;
+        size_t indices_length = nzCount * 1 * bytesPerIdx;
+        buffer->Grow(indices_length + indices_padding);
+
+        Ref<BufferView> indicesBV = a.bufferViews.Create(a.FindUniqueID(meshName, "view"));
+        indicesBV->buffer = buffer;
+        indicesBV->byteOffset = indices_offset;
+        indicesBV->byteLength = indices_length;
+        indicesBV->byteStride = 0;
+        acc->sparse->indices = indicesBV;
+        acc->sparse->indicesType = ComponentType_UNSIGNED_SHORT;
+        acc->sparse->indicesByteOffset = 0;
+        acc->WriteSparseIndices(nzCount, nzIdx, 1 * bytesPerIdx);
+
+        //values
+        size_t values_offset = buffer->byteLength;
+        size_t values_padding = values_offset % bytesPerComp;
+        values_offset += values_padding;
+        size_t values_length = nzCount * numCompsOut * bytesPerComp;
+        buffer->Grow(values_length + values_padding);
+
+        Ref<BufferView> valuesBV = a.bufferViews.Create(a.FindUniqueID(meshName, "view"));
+        valuesBV->buffer = buffer;
+        valuesBV->byteOffset = values_offset;
+        valuesBV->byteLength = values_length;
+        valuesBV->byteStride = 0;
+        acc->sparse->values = valuesBV;
+        acc->sparse->valuesByteOffset = 0;
+        acc->WriteSparseValues(nzCount, nzDiff, numCompsIn * bytesPerComp);
+
+        //clear
+        delete[] (char*)nzDiff;
+        delete[] (char*)nzIdx;
+    }
+    return acc;
+}
 inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& buffer,
 inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& buffer,
     size_t count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, BufferViewTarget target = BufferViewTarget_NONE)
     size_t count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, BufferViewTarget target = BufferViewTarget_NONE)
 {
 {
@@ -824,6 +983,10 @@ void glTF2Exporter::ExportMeshes()
 
 
         /*************** Targets for blendshapes ****************/
         /*************** Targets for blendshapes ****************/
         if (aim->mNumAnimMeshes > 0) {
         if (aim->mNumAnimMeshes > 0) {
+            bool bUseSparse = this->mProperties->HasPropertyBool("GLTF2_SPARSE_ACCESSOR_EXP") &&
+                              this->mProperties->GetPropertyBool("GLTF2_SPARSE_ACCESSOR_EXP");
+            bool bIncludeNormal = this->mProperties->HasPropertyBool("GLTF2_TARGET_NORMAL_EXP") &&
+                                  this->mProperties->GetPropertyBool("GLTF2_TARGET_NORMAL_EXP");
             bool bExportTargetNames = this->mProperties->HasPropertyBool("GLTF2_TARGETNAMES_EXP") &&
             bool bExportTargetNames = this->mProperties->HasPropertyBool("GLTF2_TARGETNAMES_EXP") &&
                               this->mProperties->GetPropertyBool("GLTF2_TARGETNAMES_EXP");
                               this->mProperties->GetPropertyBool("GLTF2_TARGETNAMES_EXP");
 
 
@@ -832,7 +995,6 @@ void glTF2Exporter::ExportMeshes()
                 aiAnimMesh *pAnimMesh = aim->mAnimMeshes[am];
                 aiAnimMesh *pAnimMesh = aim->mAnimMeshes[am];
                 if (bExportTargetNames)
                 if (bExportTargetNames)
                     m->targetNames.push_back(pAnimMesh->mName.data);
                     m->targetNames.push_back(pAnimMesh->mName.data);
-
                 // position
                 // position
                 if (pAnimMesh->HasPositions()) {
                 if (pAnimMesh->HasPositions()) {
                     // NOTE: in gltf it is the diff stored
                     // NOTE: in gltf it is the diff stored
@@ -840,9 +1002,16 @@ void glTF2Exporter::ExportMeshes()
                     for (unsigned int vt = 0; vt < pAnimMesh->mNumVertices; ++vt) {
                     for (unsigned int vt = 0; vt < pAnimMesh->mNumVertices; ++vt) {
                         pPositionDiff[vt] = pAnimMesh->mVertices[vt] - aim->mVertices[vt];
                         pPositionDiff[vt] = pAnimMesh->mVertices[vt] - aim->mVertices[vt];
                     }
                     }
-                    Ref<Accessor> vec = ExportData(*mAsset, meshId, b,
-                            pAnimMesh->mNumVertices, pPositionDiff,
-                            AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+                    Ref<Accessor> vec;
+                    if (bUseSparse) {
+                        vec = ExportDataSparse(*mAsset, meshId, b,
+                                pAnimMesh->mNumVertices, pPositionDiff,
+                                AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+                    } else {
+                        vec = ExportData(*mAsset, meshId, b,
+                                pAnimMesh->mNumVertices, pPositionDiff,
+                                AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+                    }
                     if (vec) {
                     if (vec) {
                         p.targets[am].position.push_back(vec);
                         p.targets[am].position.push_back(vec);
                     }
                     }
@@ -850,14 +1019,21 @@ void glTF2Exporter::ExportMeshes()
                 }
                 }
 
 
                 // normal
                 // normal
-                if (pAnimMesh->HasNormals()) {
+                if (pAnimMesh->HasNormals() && bIncludeNormal) {
                     aiVector3D *pNormalDiff = new aiVector3D[pAnimMesh->mNumVertices];
                     aiVector3D *pNormalDiff = new aiVector3D[pAnimMesh->mNumVertices];
                     for (unsigned int vt = 0; vt < pAnimMesh->mNumVertices; ++vt) {
                     for (unsigned int vt = 0; vt < pAnimMesh->mNumVertices; ++vt) {
                         pNormalDiff[vt] = pAnimMesh->mNormals[vt] - aim->mNormals[vt];
                         pNormalDiff[vt] = pAnimMesh->mNormals[vt] - aim->mNormals[vt];
                     }
                     }
-                    Ref<Accessor> vec = ExportData(*mAsset, meshId, b,
-                            pAnimMesh->mNumVertices, pNormalDiff,
-                            AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+                    Ref<Accessor> vec;
+                    if (bUseSparse) {
+                        vec = ExportDataSparse(*mAsset, meshId, b,
+                                pAnimMesh->mNumVertices, pNormalDiff,
+                                AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+                    } else {
+                        vec = ExportData(*mAsset, meshId, b,
+                                pAnimMesh->mNumVertices, pNormalDiff,
+                                AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);
+                    }
                     if (vec) {
                     if (vec) {
                         p.targets[am].normal.push_back(vec);
                         p.targets[am].normal.push_back(vec);
                     }
                     }

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

@@ -46,7 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_GLTF2EXPORTER_H_INC
 #ifndef AI_GLTF2EXPORTER_H_INC
 #define AI_GLTF2EXPORTER_H_INC
 #define AI_GLTF2EXPORTER_H_INC
 
 
-#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
 
 
 #include <assimp/types.h>
 #include <assimp/types.h>
 #include <assimp/material.h>
 #include <assimp/material.h>

+ 74 - 53
code/AssetLib/glTF2/glTF2Importer.cpp

@@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 */
 */
 
 
-#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
+#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
 
 
 #include "AssetLib/glTF2/glTF2Importer.h"
 #include "AssetLib/glTF2/glTF2Importer.h"
 #include "PostProcessing/MakeVerboseFormat.h"
 #include "PostProcessing/MakeVerboseFormat.h"
@@ -298,25 +298,37 @@ void glTF2Importer::ImportMaterials(glTF2::Asset &r) {
 	}
 	}
 }
 }
 
 
-static inline void SetFace(aiFace &face, int a) {
-	face.mNumIndices = 1;
-	face.mIndices = new unsigned int[1];
-	face.mIndices[0] = a;
+static inline void SetFaceAndAdvance1(aiFace*& face, unsigned int numVertices, unsigned int a) {
+	if (a >= numVertices) {
+		return;
+	}
+	face->mNumIndices = 1;
+	face->mIndices = new unsigned int[1];
+	face->mIndices[0] = a;
+	++face;
 }
 }
 
 
-static inline void SetFace(aiFace &face, int a, int b) {
-	face.mNumIndices = 2;
-	face.mIndices = new unsigned int[2];
-	face.mIndices[0] = a;
-	face.mIndices[1] = b;
+static inline void SetFaceAndAdvance2(aiFace*& face, unsigned int numVertices, unsigned int a, unsigned int b) {
+	if ((a >= numVertices) || (b >= numVertices)) {
+		return;
+	}
+	face->mNumIndices = 2;
+	face->mIndices = new unsigned int[2];
+	face->mIndices[0] = a;
+	face->mIndices[1] = b;
+	++face;
 }
 }
 
 
-static inline void SetFace(aiFace &face, int a, int b, int c) {
-	face.mNumIndices = 3;
-	face.mIndices = new unsigned int[3];
-	face.mIndices[0] = a;
-	face.mIndices[1] = b;
-	face.mIndices[2] = c;
+static inline void SetFaceAndAdvance3(aiFace*& face, unsigned int numVertices, unsigned int a, unsigned int b, unsigned int c) {
+	if ((a >= numVertices) || (b >= numVertices) || (c >= numVertices)) {
+		return;
+	}
+	face->mNumIndices = 3;
+	face->mIndices = new unsigned int[3];
+	face->mIndices[0] = a;
+	face->mIndices[1] = b;
+	face->mIndices[2] = c;
+	++face;
 }
 }
 
 
 #ifdef ASSIMP_BUILD_DEBUG
 #ifdef ASSIMP_BUILD_DEBUG
@@ -335,7 +347,7 @@ static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsign
 
 
 void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 	ASSIMP_LOG_DEBUG_F("Importing ", r.meshes.Size(), " meshes");
 	ASSIMP_LOG_DEBUG_F("Importing ", r.meshes.Size(), " meshes");
-	std::vector<aiMesh *> meshes;
+	std::vector<std::unique_ptr<aiMesh>> meshes;
 
 
 	unsigned int k = 0;
 	unsigned int k = 0;
     meshOffsets.clear();
     meshOffsets.clear();
@@ -350,7 +362,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 			Mesh::Primitive &prim = mesh.primitives[p];
 			Mesh::Primitive &prim = mesh.primitives[p];
 
 
 			aiMesh *aim = new aiMesh();
 			aiMesh *aim = new aiMesh();
-			meshes.push_back(aim);
+			meshes.push_back(std::unique_ptr<aiMesh>(aim));
 
 
 			aim->mName = mesh.name.empty() ? mesh.id : mesh.name;
 			aim->mName = mesh.name.empty() ? mesh.id : mesh.name;
 
 
@@ -486,6 +498,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 			}
 			}
 
 
 			aiFace *faces = nullptr;
 			aiFace *faces = nullptr;
+			aiFace *facePtr = nullptr;
 			size_t nFaces = 0;
 			size_t nFaces = 0;
 
 
 			if (prim.indices) {
 			if (prim.indices) {
@@ -497,9 +510,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 				switch (prim.mode) {
 				switch (prim.mode) {
 					case PrimitiveMode_POINTS: {
 					case PrimitiveMode_POINTS: {
 						nFaces = count;
 						nFaces = count;
-						faces = new aiFace[nFaces];
+						facePtr = faces = new aiFace[nFaces];
 						for (unsigned int i = 0; i < count; ++i) {
 						for (unsigned int i = 0; i < count; ++i) {
-							SetFace(faces[i], data.GetUInt(i));
+							SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i));
 						}
 						}
 						break;
 						break;
 					}
 					}
@@ -510,9 +523,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 							ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
 							ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
 							count = nFaces * 2;
 							count = nFaces * 2;
 						}
 						}
-						faces = new aiFace[nFaces];
+						facePtr = faces = new aiFace[nFaces];
 						for (unsigned int i = 0; i < count; i += 2) {
 						for (unsigned int i = 0; i < count; i += 2) {
-							SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1));
+							SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1));
 						}
 						}
 						break;
 						break;
 					}
 					}
@@ -520,13 +533,13 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 					case PrimitiveMode_LINE_LOOP:
 					case PrimitiveMode_LINE_LOOP:
 					case PrimitiveMode_LINE_STRIP: {
 					case PrimitiveMode_LINE_STRIP: {
 						nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
 						nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
-						faces = new aiFace[nFaces];
-						SetFace(faces[0], data.GetUInt(0), data.GetUInt(1));
+						facePtr = faces = new aiFace[nFaces];
+						SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1));
 						for (unsigned int i = 2; i < count; ++i) {
 						for (unsigned int i = 2; i < count; ++i) {
-							SetFace(faces[i - 1], faces[i - 2].mIndices[1], data.GetUInt(i));
+							SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i - 1), data.GetUInt(i));
 						}
 						}
 						if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
 						if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
-							SetFace(faces[count - 1], faces[count - 2].mIndices[1], faces[0].mIndices[0]);
+							SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(static_cast<int>(count) - 1), faces[0].mIndices[0]);
 						}
 						}
 						break;
 						break;
 					}
 					}
@@ -537,33 +550,33 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 							ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
 							ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
 							count = nFaces * 3;
 							count = nFaces * 3;
 						}
 						}
-						faces = new aiFace[nFaces];
+						facePtr = faces = new aiFace[nFaces];
 						for (unsigned int i = 0; i < count; i += 3) {
 						for (unsigned int i = 0; i < count; i += 3) {
-							SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
+							SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
 						}
 						}
 						break;
 						break;
 					}
 					}
 					case PrimitiveMode_TRIANGLE_STRIP: {
 					case PrimitiveMode_TRIANGLE_STRIP: {
 						nFaces = count - 2;
 						nFaces = count - 2;
-						faces = new aiFace[nFaces];
+						facePtr = faces = new aiFace[nFaces];
 						for (unsigned int i = 0; i < nFaces; ++i) {
 						for (unsigned int i = 0; i < nFaces; ++i) {
 							//The ordering is to ensure that the triangles are all drawn with the same orientation
 							//The ordering is to ensure that the triangles are all drawn with the same orientation
 							if ((i + 1) % 2 == 0) {
 							if ((i + 1) % 2 == 0) {
 								//For even n, vertices n + 1, n, and n + 2 define triangle n
 								//For even n, vertices n + 1, n, and n + 2 define triangle n
-								SetFace(faces[i], data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2));
+								SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2));
 							} else {
 							} else {
 								//For odd n, vertices n, n+1, and n+2 define triangle n
 								//For odd n, vertices n, n+1, and n+2 define triangle n
-								SetFace(faces[i], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
+								SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
 							}
 							}
 						}
 						}
 						break;
 						break;
 					}
 					}
 					case PrimitiveMode_TRIANGLE_FAN:
 					case PrimitiveMode_TRIANGLE_FAN:
 						nFaces = count - 2;
 						nFaces = count - 2;
-						faces = new aiFace[nFaces];
-						SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
+						facePtr = faces = new aiFace[nFaces];
+						SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
 						for (unsigned int i = 1; i < nFaces; ++i) {
 						for (unsigned int i = 1; i < nFaces; ++i) {
-							SetFace(faces[i], faces[0].mIndices[0], faces[i - 1].mIndices[2], data.GetUInt(i + 2));
+							SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(i + 1), data.GetUInt(i + 2));
 						}
 						}
 						break;
 						break;
 				}
 				}
@@ -575,9 +588,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 				switch (prim.mode) {
 				switch (prim.mode) {
 					case PrimitiveMode_POINTS: {
 					case PrimitiveMode_POINTS: {
 						nFaces = count;
 						nFaces = count;
-						faces = new aiFace[nFaces];
+						facePtr = faces = new aiFace[nFaces];
 						for (unsigned int i = 0; i < count; ++i) {
 						for (unsigned int i = 0; i < count; ++i) {
-							SetFace(faces[i], i);
+							SetFaceAndAdvance1(facePtr, aim->mNumVertices, i);
 						}
 						}
 						break;
 						break;
 					}
 					}
@@ -588,9 +601,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 							ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
 							ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
 							count = (unsigned int)nFaces * 2;
 							count = (unsigned int)nFaces * 2;
 						}
 						}
-						faces = new aiFace[nFaces];
+						facePtr = faces = new aiFace[nFaces];
 						for (unsigned int i = 0; i < count; i += 2) {
 						for (unsigned int i = 0; i < count; i += 2) {
-							SetFace(faces[i / 2], i, i + 1);
+							SetFaceAndAdvance2(facePtr, aim->mNumVertices, i, i + 1);
 						}
 						}
 						break;
 						break;
 					}
 					}
@@ -598,13 +611,13 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 					case PrimitiveMode_LINE_LOOP:
 					case PrimitiveMode_LINE_LOOP:
 					case PrimitiveMode_LINE_STRIP: {
 					case PrimitiveMode_LINE_STRIP: {
 						nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
 						nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
-						faces = new aiFace[nFaces];
-						SetFace(faces[0], 0, 1);
+						facePtr = faces = new aiFace[nFaces];
+						SetFaceAndAdvance2(facePtr, aim->mNumVertices, 0, 1);
 						for (unsigned int i = 2; i < count; ++i) {
 						for (unsigned int i = 2; i < count; ++i) {
-							SetFace(faces[i - 1], faces[i - 2].mIndices[1], i);
+							SetFaceAndAdvance2(facePtr, aim->mNumVertices, i - 1, i);
 						}
 						}
 						if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
 						if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
-							SetFace(faces[count - 1], faces[count - 2].mIndices[1], faces[0].mIndices[0]);
+							SetFaceAndAdvance2(facePtr, aim->mNumVertices, count - 1, 0);
 						}
 						}
 						break;
 						break;
 					}
 					}
@@ -615,42 +628,50 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
 							ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
 							ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
 							count = (unsigned int)nFaces * 3;
 							count = (unsigned int)nFaces * 3;
 						}
 						}
-						faces = new aiFace[nFaces];
+						facePtr = faces = new aiFace[nFaces];
 						for (unsigned int i = 0; i < count; i += 3) {
 						for (unsigned int i = 0; i < count; i += 3) {
-							SetFace(faces[i / 3], i, i + 1, i + 2);
+							SetFaceAndAdvance3(facePtr, aim->mNumVertices, i, i + 1, i + 2);
 						}
 						}
 						break;
 						break;
 					}
 					}
 					case PrimitiveMode_TRIANGLE_STRIP: {
 					case PrimitiveMode_TRIANGLE_STRIP: {
 						nFaces = count - 2;
 						nFaces = count - 2;
-						faces = new aiFace[nFaces];
+						facePtr = faces = new aiFace[nFaces];
 						for (unsigned int i = 0; i < nFaces; ++i) {
 						for (unsigned int i = 0; i < nFaces; ++i) {
 							//The ordering is to ensure that the triangles are all drawn with the same orientation
 							//The ordering is to ensure that the triangles are all drawn with the same orientation
 							if ((i + 1) % 2 == 0) {
 							if ((i + 1) % 2 == 0) {
 								//For even n, vertices n + 1, n, and n + 2 define triangle n
 								//For even n, vertices n + 1, n, and n + 2 define triangle n
-								SetFace(faces[i], i + 1, i, i + 2);
+								SetFaceAndAdvance3(facePtr, aim->mNumVertices, i + 1, i, i + 2);
 							} else {
 							} else {
 								//For odd n, vertices n, n+1, and n+2 define triangle n
 								//For odd n, vertices n, n+1, and n+2 define triangle n
-								SetFace(faces[i], i, i + 1, i + 2);
+								SetFaceAndAdvance3(facePtr, aim->mNumVertices, i, i + 1, i + 2);
 							}
 							}
 						}
 						}
 						break;
 						break;
 					}
 					}
 					case PrimitiveMode_TRIANGLE_FAN:
 					case PrimitiveMode_TRIANGLE_FAN:
 						nFaces = count - 2;
 						nFaces = count - 2;
-						faces = new aiFace[nFaces];
-						SetFace(faces[0], 0, 1, 2);
+						facePtr = faces = new aiFace[nFaces];
+						SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, 1, 2);
 						for (unsigned int i = 1; i < nFaces; ++i) {
 						for (unsigned int i = 1; i < nFaces; ++i) {
-							SetFace(faces[i], faces[0].mIndices[0], faces[i - 1].mIndices[2], i + 2);
+							SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, i + 1, i + 2);
 						}
 						}
 						break;
 						break;
 				}
 				}
 			}
 			}
 
 
-			if (nullptr != faces) {
+			if (faces) {
 				aim->mFaces = faces;
 				aim->mFaces = faces;
-				aim->mNumFaces = static_cast<unsigned int>(nFaces);
-				ai_assert(CheckValidFacesIndices(faces, static_cast<unsigned>(nFaces), aim->mNumVertices));
+                const unsigned int actualNumFaces = static_cast<unsigned int>(facePtr - faces);
+				if (actualNumFaces < nFaces) {
+					ASSIMP_LOG_WARN("Some faces had out-of-range indices. Those faces were dropped.");
+				}
+				if (actualNumFaces == 0)
+				{
+					throw DeadlyImportError("Mesh \"", aim->mName.C_Str(), "\" has no faces");
+				}
+				aim->mNumFaces = actualNumFaces;
+				ai_assert(CheckValidFacesIndices(faces, actualNumFaces, aim->mNumVertices));
 			}
 			}
 
 
 			if (prim.material) {
 			if (prim.material) {

+ 8 - 3
code/CMakeLists.txt

@@ -200,6 +200,7 @@ SET( Common_SRCS
   Common/simd.cpp
   Common/simd.cpp
   Common/material.cpp
   Common/material.cpp
   Common/AssertHandler.cpp
   Common/AssertHandler.cpp
+  Common/Exceptional.cpp
 )
 )
 SOURCE_GROUP(Common FILES ${Common_SRCS})
 SOURCE_GROUP(Common FILES ${Common_SRCS})
 
 
@@ -884,7 +885,7 @@ ENDIF()
 # utf8
 # utf8
 IF(ASSIMP_HUNTER_ENABLED)
 IF(ASSIMP_HUNTER_ENABLED)
   hunter_add_package(utf8)
   hunter_add_package(utf8)
-  find_package(utf8 CONFIG REQUIRED)
+  find_package(utf8cpp CONFIG REQUIRED)
 ELSE()
 ELSE()
   # utf8 is header-only, so Assimp doesn't need to do anything.
   # utf8 is header-only, so Assimp doesn't need to do anything.
 ENDIF()
 ENDIF()
@@ -1039,6 +1040,8 @@ IF(ASSIMP_HUNTER_ENABLED)
 ELSE()
 ELSE()
   INCLUDE_DIRECTORIES( "../contrib/rapidjson/include" )
   INCLUDE_DIRECTORIES( "../contrib/rapidjson/include" )
   INCLUDE_DIRECTORIES( "../contrib" )
   INCLUDE_DIRECTORIES( "../contrib" )
+  ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1 )
+  ADD_DEFINITIONS( -DRAPIDJSON_NOMEMBERITERATORCLASS )
 ENDIF()
 ENDIF()
 
 
 # VC2010 fixes
 # VC2010 fixes
@@ -1051,7 +1054,7 @@ endif()
 
 
 ADD_DEFINITIONS( -DASSIMP_BUILD_DLL_EXPORT )
 ADD_DEFINITIONS( -DASSIMP_BUILD_DLL_EXPORT )
 
 
-if ( MSVC )
+IF( MSVC OR "${CMAKE_CXX_SIMULATE_ID}" MATCHES "MSVC") # clang with MSVC ABI
   ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS )
   ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS )
   ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS )
   ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS )
 endif ()
 endif ()
@@ -1121,6 +1124,8 @@ ENDIF ()
 ADD_LIBRARY( assimp ${assimp_src} )
 ADD_LIBRARY( assimp ${assimp_src} )
 ADD_LIBRARY(assimp::assimp ALIAS assimp)
 ADD_LIBRARY(assimp::assimp ALIAS assimp)
 
 
+TARGET_USE_COMMON_OUTPUT_DIRECTORY(assimp)
+
 # enable warnings as errors ########################################
 # enable warnings as errors ########################################
 IF (MSVC)
 IF (MSVC)
   TARGET_COMPILE_OPTIONS(assimp PRIVATE /WX)
   TARGET_COMPILE_OPTIONS(assimp PRIVATE /WX)
@@ -1144,7 +1149,7 @@ IF(ASSIMP_HUNTER_ENABLED)
       minizip::minizip
       minizip::minizip
       ZLIB::zlib
       ZLIB::zlib
       RapidJSON::rapidjson
       RapidJSON::rapidjson
-      utf8::utf8
+      utf8cpp
       zip::zip
       zip::zip
       pugixml::pugixml
       pugixml::pugixml
   )
   )

+ 4 - 3
code/Common/BaseImporter.cpp

@@ -130,10 +130,11 @@ aiScene *BaseImporter::ReadFile(Importer *pImp, const std::string &pFile, IOSyst
         // passes scale into ScaleProcess
         // passes scale into ScaleProcess
         UpdateImporterScale(pImp);
         UpdateImporterScale(pImp);
 
 
-    } catch (const std::exception &err) {
+    } catch( const std::exception &err ) {
         // extract error description
         // extract error description
         m_ErrorText = err.what();
         m_ErrorText = err.what();
-        ASSIMP_LOG_ERROR(m_ErrorText);
+        ASSIMP_LOG_ERROR(err.what());
+        m_Exception = std::current_exception();
         return nullptr;
         return nullptr;
     }
     }
 
 
@@ -343,7 +344,7 @@ std::string BaseImporter::GetExtension(const std::string &file) {
 }
 }
 
 
 #ifdef ASSIMP_USE_HUNTER
 #ifdef ASSIMP_USE_HUNTER
-#include <utf8/utf8.h>
+#include <utf8.h>
 #else
 #else
 #include "../contrib/utf8cpp/source/utf8.h"
 #include "../contrib/utf8cpp/source/utf8.h"
 #endif
 #endif

+ 10 - 8
code/Common/DefaultIOStream.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 
 Copyright (c) 2006-2020, assimp team
 Copyright (c) 2006-2020, assimp team
 
 
-
-
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -52,27 +50,32 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 using namespace Assimp;
 
 
 namespace {
 namespace {
+
 template <size_t sizeOfPointer>
 template <size_t sizeOfPointer>
-size_t select_ftell(FILE *file) {
+inline size_t select_ftell(FILE *file) {
     return ::ftell(file);
     return ::ftell(file);
 }
 }
 
 
 template <size_t sizeOfPointer>
 template <size_t sizeOfPointer>
-int select_fseek(FILE *file, int64_t offset, int origin) {
+inline int select_fseek(FILE *file, int64_t offset, int origin) {
     return ::fseek(file, static_cast<long>(offset), origin);
     return ::fseek(file, static_cast<long>(offset), origin);
 }
 }
 
 
+
+    
 #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
 #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
 template <>
 template <>
-size_t select_ftell<8>(FILE *file) {
+inline size_t select_ftell<8>(FILE *file) {
     return (size_t)::_ftelli64(file);
     return (size_t)::_ftelli64(file);
 }
 }
 
 
 template <>
 template <>
-int select_fseek<8>(FILE *file, int64_t offset, int origin) {
+inline int select_fseek<8>(FILE *file, int64_t offset, int origin) {
     return ::_fseeki64(file, offset, origin);
     return ::_fseeki64(file, offset, origin);
 }
 }
-#endif
+
+#endif // #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
+    
 } // namespace
 } // namespace
 
 
 // ----------------------------------------------------------------------------------
 // ----------------------------------------------------------------------------------
@@ -100,7 +103,6 @@ size_t DefaultIOStream::Write(const void *pvBuffer,
         size_t pCount) {
         size_t pCount) {
     ai_assert(nullptr != pvBuffer);
     ai_assert(nullptr != pvBuffer);
     ai_assert(0 != pSize);
     ai_assert(0 != pSize);
-    ai_assert(0 != pCount);
 
 
     return (mFile ? ::fwrite(pvBuffer, pSize, pCount, mFile) : 0);
     return (mFile ? ::fwrite(pvBuffer, pSize, pCount, mFile) : 0);
 }
 }

+ 52 - 0
code/Common/Exceptional.cpp

@@ -0,0 +1,52 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2020, 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 Exceptional.cpp
+
+Implementations of the exception classes.
+
+*/
+
+#include <assimp/Exceptional.h>
+#include <assimp/TinyFormatter.h>
+
+DeadlyErrorBase::DeadlyErrorBase(Assimp::Formatter::format f) :
+        runtime_error(std::string(f)){}

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff