2
0
Эх сурвалжийг харах

Merge branch 'master' into master

Kim Kulling 3 жил өмнө
parent
commit
d4527d4e87
100 өөрчлөгдсөн 1839 нэмэгдсэн , 1455 устгасан
  1. 1 0
      .github/FUNDING.yml
  2. 2 1
      .gitignore
  3. 0 67
      .travis.sh
  4. 0 78
      .travis.yml
  5. 6 7
      BUILDBINARIES_EXAMPLE.bat
  6. 46 17
      CMakeLists.txt
  7. 2 0
      Readme.md
  8. 0 17
      cmake-modules/FindIrrXML.cmake
  9. 4 9
      code/AssetLib/3DS/3DSConverter.cpp
  10. 12 6
      code/AssetLib/3DS/3DSExporter.cpp
  11. 2 2
      code/AssetLib/3DS/3DSExporter.h
  12. 3 3
      code/AssetLib/3DS/3DSHelper.h
  13. 5 16
      code/AssetLib/3DS/3DSLoader.cpp
  14. 15 6
      code/AssetLib/3DS/3DSLoader.h
  15. 165 0
      code/AssetLib/3MF/3MFTypes.h
  16. 10 2
      code/AssetLib/3MF/3MFXmlTags.h
  17. 1 1
      code/AssetLib/3MF/D3MFExporter.cpp
  18. 8 5
      code/AssetLib/3MF/D3MFExporter.h
  19. 18 522
      code/AssetLib/3MF/D3MFImporter.cpp
  20. 33 7
      code/AssetLib/3MF/D3MFImporter.h
  21. 60 14
      code/AssetLib/3MF/D3MFOpcPackage.cpp
  22. 12 6
      code/AssetLib/3MF/D3MFOpcPackage.h
  23. 593 0
      code/AssetLib/3MF/XmlSerializer.cpp
  24. 96 0
      code/AssetLib/3MF/XmlSerializer.h
  25. 4 13
      code/AssetLib/AC/ACLoader.cpp
  26. 6 6
      code/AssetLib/AC/ACLoader.h
  27. 4 14
      code/AssetLib/AMF/AMFImporter.cpp
  28. 5 5
      code/AssetLib/AMF/AMFImporter.hpp
  29. 1 1
      code/AssetLib/AMF/AMFImporter_Geometry.cpp
  30. 1 1
      code/AssetLib/AMF/AMFImporter_Material.cpp
  31. 5 6
      code/AssetLib/AMF/AMFImporter_Node.hpp
  32. 2 2
      code/AssetLib/AMF/AMFImporter_Postprocess.cpp
  33. 5 17
      code/AssetLib/ASE/ASELoader.cpp
  34. 6 21
      code/AssetLib/ASE/ASELoader.h
  35. 8 6
      code/AssetLib/ASE/ASEParser.cpp
  36. 1 1
      code/AssetLib/ASE/ASEParser.h
  37. 1 1
      code/AssetLib/Assbin/AssbinExporter.cpp
  38. 5 3
      code/AssetLib/Assbin/AssbinExporter.h
  39. 15 14
      code/AssetLib/Assbin/AssbinFileWriter.cpp
  40. 1 1
      code/AssetLib/Assbin/AssbinFileWriter.h
  41. 2 2
      code/AssetLib/Assbin/AssbinLoader.cpp
  42. 6 13
      code/AssetLib/Assbin/AssbinLoader.h
  43. 27 15
      code/AssetLib/Assjson/json_exporter.cpp
  44. 6 7
      code/AssetLib/Assjson/mesh_splitter.cpp
  45. 13 22
      code/AssetLib/Assjson/mesh_splitter.h
  46. 1 1
      code/AssetLib/Assxml/AssxmlExporter.cpp
  47. 2 1
      code/AssetLib/Assxml/AssxmlExporter.h
  48. 3 3
      code/AssetLib/Assxml/AssxmlFileWriter.cpp
  49. 2 3
      code/AssetLib/Assxml/AssxmlFileWriter.h
  50. 3 3
      code/AssetLib/B3D/B3DImporter.cpp
  51. 9 11
      code/AssetLib/B3D/B3DImporter.h
  52. 5 14
      code/AssetLib/BVH/BVHLoader.cpp
  53. 2 2
      code/AssetLib/BVH/BVHLoader.h
  54. 1 1
      code/AssetLib/Blender/BlenderBMesh.cpp
  55. 1 1
      code/AssetLib/Blender/BlenderBMesh.h
  56. 1 1
      code/AssetLib/Blender/BlenderDNA.cpp
  57. 2 2
      code/AssetLib/Blender/BlenderDNA.h
  58. 1 1
      code/AssetLib/Blender/BlenderDNA.inl
  59. 1 1
      code/AssetLib/Blender/BlenderIntermediate.h
  60. 80 79
      code/AssetLib/Blender/BlenderLoader.cpp
  61. 97 105
      code/AssetLib/Blender/BlenderLoader.h
  62. 1 1
      code/AssetLib/Blender/BlenderModifier.cpp
  63. 1 1
      code/AssetLib/Blender/BlenderModifier.h
  64. 55 2
      code/AssetLib/Blender/BlenderScene.cpp
  65. 24 2
      code/AssetLib/Blender/BlenderScene.h
  66. 6 0
      code/AssetLib/Blender/BlenderSceneGen.h
  67. 1 1
      code/AssetLib/Blender/BlenderTessellator.cpp
  68. 1 1
      code/AssetLib/Blender/BlenderTessellator.h
  69. 13 2
      code/AssetLib/C4D/C4DImporter.cpp
  70. 7 15
      code/AssetLib/COB/COBLoader.cpp
  71. 53 57
      code/AssetLib/COB/COBLoader.h
  72. 5 6
      code/AssetLib/COB/COBScene.h
  73. 4 13
      code/AssetLib/CSM/CSMLoader.cpp
  74. 12 16
      code/AssetLib/CSM/CSMLoader.h
  75. 1 1
      code/AssetLib/Collada/ColladaExporter.cpp
  76. 1 1
      code/AssetLib/Collada/ColladaExporter.h
  77. 1 1
      code/AssetLib/Collada/ColladaHelper.cpp
  78. 1 1
      code/AssetLib/Collada/ColladaHelper.h
  79. 14 36
      code/AssetLib/Collada/ColladaLoader.cpp
  80. 1 1
      code/AssetLib/Collada/ColladaLoader.h
  81. 65 47
      code/AssetLib/Collada/ColladaParser.cpp
  82. 2 1
      code/AssetLib/Collada/ColladaParser.h
  83. 1 1
      code/AssetLib/DXF/DXFHelper.h
  84. 9 13
      code/AssetLib/DXF/DXFLoader.cpp
  85. 7 10
      code/AssetLib/DXF/DXFLoader.h
  86. 1 1
      code/AssetLib/FBX/FBXAnimation.cpp
  87. 1 1
      code/AssetLib/FBX/FBXBinaryTokenizer.cpp
  88. 3 1
      code/AssetLib/FBX/FBXCommon.h
  89. 1 1
      code/AssetLib/FBX/FBXCompileConfig.h
  90. 103 21
      code/AssetLib/FBX/FBXConverter.cpp
  91. 2 1
      code/AssetLib/FBX/FBXConverter.h
  92. 1 1
      code/AssetLib/FBX/FBXDeformer.cpp
  93. 1 1
      code/AssetLib/FBX/FBXDocument.cpp
  94. 2 2
      code/AssetLib/FBX/FBXDocument.h
  95. 1 1
      code/AssetLib/FBX/FBXDocumentUtil.cpp
  96. 1 1
      code/AssetLib/FBX/FBXExportNode.cpp
  97. 1 1
      code/AssetLib/FBX/FBXExportNode.h
  98. 1 1
      code/AssetLib/FBX/FBXExportProperty.cpp
  99. 1 1
      code/AssetLib/FBX/FBXExportProperty.h
  100. 4 4
      code/AssetLib/FBX/FBXExporter.cpp

+ 1 - 0
.github/FUNDING.yml

@@ -1 +1,2 @@
 open_collective: assimp
+patreon: assimp

+ 2 - 1
.gitignore

@@ -94,6 +94,7 @@ test/gtest/src/gtest-stamp/gtest-gitinfo.txt
 test/gtest/src/gtest-stamp/gtest-gitclone-lastrun.txt
 Assimp.opensdf
 contrib/zlib/CTestTestfile.cmake
+contrib/zlib/Debug/zlibstaticd.pdb
 ipch/assimp_viewer-44bbbcd1/assimp_viewerd-ccc45335.ipch
 bin64/assimp-vc140-mt.dll
 bin64/assimp-vc140-mtd.dll
@@ -118,4 +119,4 @@ tools/assimp_qt_viewer/moc_mainwindow.cpp_parameters
 tools/assimp_qt_viewer/ui_mainwindow.h
 
 #Generated directory
-generated/*
+generated/*

+ 0 - 67
.travis.sh

@@ -1,67 +0,0 @@
-#---------------------------------------------------------------------------
-#Open Asset Import Library (assimp)
-#---------------------------------------------------------------------------
-# Copyright (c) 2006-2020, assimp team
-#
-# License see LICENSE file
-#
-function generate() {
-    OPTIONS="-DASSIMP_WERROR=ON"
-    OPTIONS="$OPTIONS -DASSIMP_NO_EXPORT=NO"
-    
-    if [ "$DISABLE_EXPORTERS" = "YES" ] ; then
-        OPTIONS="$OPTIONS -DASSIMP_NO_EXPORT=YES"
-    else
-        OPTIONS="$OPTIONS -DASSIMP_NO_EXPORT=NO"
-    fi
-
-    if [ "$SHARED_BUILD" = "ON" ] ; then
-        OPTIONS="$OPTIONS -DBUILD_SHARED_LIBS=ON"
-    else
-        OPTIONS="$OPTIONS -DBUILD_SHARED_LIBS=OFF"
-    fi
-
-    if [ "$ENABLE_COVERALLS" = "ON" ] ; then
-        OPTIONS="$OPTIONS -DASSIMP_COVERALLS=ON"
-    else
-        OPTIONS="$OPTIONS -DASSIMP_COVERALLS=OFF"
-    fi
-
-    if [ "$ASAN" = "ON" ] ; then
-        OPTIONS="$OPTIONS -DASSIMP_ASAN=ON"
-    else
-        OPTIONS="$OPTIONS -DASSIMP_ASAN=OFF"
-    fi
-
-    if [ "$UBSAN" = "ON" ] ; then
-        OPTIONS="$OPTIONS -DASSIMP_UBSAN=ON"
-    fi
-
-    cmake -G "Unix Makefiles" $OPTIONS
-}
-# build and run unittests, if not android
-if [ $ANDROID ]; then
-    ant -v -Dmy.dir=${TRAVIS_BUILD_DIR} -f ${TRAVIS_BUILD_DIR}/port/jassimp/build.xml ndk-jni
-fi
-if [ "$TRAVIS_OS_NAME" = "linux" ]; then
-  if [ $ANALYZE = "ON" ] ; then
-    if [ "$CC" = "clang" ]; then
-        scan-build cmake -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF -DASSIMP_BUILD_TESTS=OFF
-        scan-build --status-bugs make -j2
-    else
-        cppcheck --version
-        generate \
-        && cppcheck --error-exitcode=1 -j2 -Iinclude -Icode code 2> cppcheck.txt
-        if [ -s cppcheck.txt ]; then
-            cat cppcheck.txt
-            exit 1
-        fi
-    fi
-  else
-    generate \
-    && make -j4 \
-    && sudo make install \
-    && sudo ldconfig \
-    && (cd test/unit; ../../bin/unit)
-  fi
-fi

+ 0 - 78
.travis.yml

@@ -1,78 +0,0 @@
-sudo: required
-language: cpp
-
-cache: ccache
-
-before_install:
-  - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq && sudo apt-get install cmake cppcheck && sudo apt-get install cmake python3 && sudo apt-get install -qq freeglut3-dev libxmu-dev libxi-dev ; echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- ; fi
-  - 'if [ "$TRAVIS_OS_NAME" = "osx" ];  then
-       if brew ls --versions cmake > /dev/null; then
-         echo cmake already installed.;
-       else
-         brew install cmake;
-       fi;
-       brew install python3;
-       brew install homebrew/x11/freeglut; 
-    fi'
-  - echo -e "#ifndef A_R_H_INC\n#define A_R_H_INC\n#define GitVersion ${TRAVIS_JOB_ID}\n#define GitBranch \"${TRAVIS_BRANCH}\"\n#endif // A_R_H_INC" > revision.h
-  # install latest LCOV (1.9 was failing)
-  - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ${TRAVIS_BUILD_DIR} && wget http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.11.orig.tar.gz && tar xf lcov_1.11.orig.tar.gz && sudo make -C lcov-1.11/ install && gem install coveralls-lcov && lcov --version && g++ --version ; fi
-
-os:
-  - linux
-
-compiler:
-  - gcc
-  - clang
-
-env:
-  global:
-    - secure: "lZ7pHQvl5dpZWzBQAaIMf0wqrvtcZ4wiZKeIZjf83TEsflW8+z0uTpIuN30ZV6Glth/Sq1OhLnTP5+N57fZU/1ebA5twHdvP4bS5CIUUg71/CXQZNl36xeaqvxsG/xRrdpKOsPdjAOsQ9KPTQulsX43XDLS7CasMiLvYOpqKcPc="
-    - PV=r8e PLATF=linux-x86_64 NDK_HOME=${TRAVIS_BUILD_DIR}/android-ndk-${PV} PATH=${PATH}:${NDK_HOME}
-
-git:
-  depth: 1
-
-matrix:
-  include:
-    - os: linux
-      compiler: clang
-      env: ASAN=ON
-    - os: linux
-      compiler: clang
-      env: UBSAN=ON
-    - os: linux
-      compiler: clang
-      env: SHARED_BUILD=ON
-    - os: linux
-      compiler: gcc
-      env: ANALYZE=ON
-    - os: linux
-      compiler: gcc
-      env: ENABLE_COVERALLS=ON
-    - os: linux
-      compiler: gcc
-      env: SHARED_BUILD=ON
-
-install:
-  - if [ $ANDROID ]; then wget -c http://dl.google.com/android/ndk/android-ndk-${PV}-${PLATF}.tar.bz2 && tar xf android-ndk-${PV}-${PLATF}.tar.bz2 ; fi
-
-before_script:
-  cmake . -DASSIMP_ENABLE_BOOST_WORKAROUND=YES
-
-script:
-  - export COVERALLS_SERVICE_NAME=travis-ci
-  - export COVERALLS_REPO_TOKEN=abc12345
-  - . ./.travis.sh
- 
-after_success:
-  - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ${TRAVIS_BUILD_DIR} && lcov --directory . --capture --output-file coverage.info && lcov --remove coverage.info '/usr/*' 'contrib/*' 'test/*' --output-file coverage.info && lcov --list coverage.info && coveralls-lcov --source-encoding=ISO-8859-1 --repo-token=${COVERALLS_TOKEN} coverage.info ; fi
-
-addons:
-  coverity_scan:
-    project:
-      name: "assimp/assimp"
-    notification_email: [email protected]
-    build_command_prepend: "cmake ./"
-    build_command: "make -j4"
-    branch_pattern: coverity_scan

+ 6 - 7
BUILDBINARIES_EXAMPLE.bat

@@ -10,16 +10,15 @@
 :: Also see: https://github.com/assimp/assimp/pull/2646
 
 SET SOURCE_DIR=.
+SET GENERATOR=Visual Studio 16 2019
 
-:: For generators see "cmake --help"
-SET GENERATOR=Visual Studio 15 2017
-
-SET BINARIES_DIR="./BINARIES/Win32"
-cmake CMakeLists.txt -G "%GENERATOR%" -S %SOURCE_DIR% -B %BINARIES_DIR%
+SET BINARIES_DIR="./build/Win32"
+cmake . -G "%GENERATOR%" -A Win32 -S %SOURCE_DIR% -B %BINARIES_DIR%
+cmake --build %BINARIES_DIR% --config debug
 cmake --build %BINARIES_DIR% --config release
 
-SET BINARIES_DIR="./BINARIES/x64"
-cmake CMakeLists.txt -G "%GENERATOR% Win64" -S %SOURCE_DIR% -B %BINARIES_DIR%
+SET BINARIES_DIR="./build/x64"
+cmake . -G "%GENERATOR%" -A x64 -S %SOURCE_DIR% -B %BINARIES_DIR%
 cmake --build %BINARIES_DIR% --config debug
 cmake --build %BINARIES_DIR% --config release
 

+ 46 - 17
CMakeLists.txt

@@ -1,6 +1,6 @@
 # Open Asset Import Library (assimp)
 # ----------------------------------------------------------------------
-# Copyright (c) 2006-2021, assimp team
+# Copyright (c) 2006-2022, assimp team
 #
 # All rights reserved.
 #
@@ -40,20 +40,23 @@ SET(CMAKE_POLICY_DEFAULT_CMP0092 NEW)
 
 CMAKE_MINIMUM_REQUIRED( VERSION 3.10 )
 
+# Disabled importers: m3d for 5.1
+ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_IMPORTER)
+ADD_DEFINITIONS( -DASSIMP_BUILD_NO_M3D_EXPORTER)
 # Toggles the use of the hunter package manager
 option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
 
 IF(ASSIMP_HUNTER_ENABLED)
   include("cmake-modules/HunterGate.cmake")
   HunterGate(
-    URL "https://github.com/cpp-pm/hunter/archive/v0.23.311.tar.gz"
-    SHA1 "1a82b9b73055879181cb1466b2ab5d48ee8ae410"
+    URL "https://github.com/cpp-pm/hunter/archive/v0.24.0.tar.gz"
+    SHA1 "a3d7f4372b1dcd52faa6ff4a3bd5358e1d0e5efd"
   )
 
   add_definitions(-DASSIMP_USE_HUNTER)
 ENDIF()
 
-PROJECT( Assimp VERSION 5.0.1 )
+PROJECT(Assimp VERSION 5.2.0)
 
 # All supported options ###############################################
 
@@ -131,12 +134,12 @@ OPTION ( ASSIMP_IGNORE_GIT_HASH
    OFF
 )
 
-IF ( WIN32 )
+IF (WIN32)
   # Use subset of Windows.h
   ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
 
   IF(MSVC)
-    OPTION ( ASSIMP_BUILD_ASSIMP_VIEW
+    OPTION (ASSIMP_BUILD_ASSIMP_VIEW
       "If the Assimp view tool is built. (requires DirectX)"
       OFF )
 
@@ -180,7 +183,9 @@ SET (ASSIMP_SOVERSION 5)
 SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" )
 if(NOT ASSIMP_HUNTER_ENABLED)
   # Enable C++11 support globally
-  set_property( GLOBAL PROPERTY CXX_STANDARD 11 )
+  set(CMAKE_CXX_STANDARD 11)
+  set(CMAKE_CXX_STANDARD_REQUIRED ON)
+  set(CMAKE_C_STANDARD 99)
 endif()
 
 IF(NOT ASSIMP_IGNORE_GIT_HASH)
@@ -238,10 +243,17 @@ SET(ASSIMP_LIBRARY_SUFFIX "" CACHE STRING "Suffix to append to library names")
 IF( UNIX )
   # Use GNUInstallDirs for Unix predefined directories
   INCLUDE(GNUInstallDirs)
+  # Ensure that we do not run into issues like http://www.tcm.phy.cam.ac.uk/sw/inodes64.html on 32 bit linux
+  IF( ${OPERATING_SYSTEM} MATCHES "Android")
+  ELSE()
+    IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux
+      ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 )
+    ENDIF()
+  ENDIF()
 ENDIF()
 
 # Grouped compiler settings ########################################
-IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT CMAKE_COMPILER_IS_MINGW)
+IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT MINGW)
   IF(NOT ASSIMP_HUNTER_ENABLED)
     SET(CMAKE_CXX_STANDARD 11)
     SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
@@ -265,8 +277,9 @@ ELSEIF(MSVC)
   ENDIF()
   # disable "elements of array '' will be default initialized" warning on MSVC2013
   IF(MSVC12)
-    ADD_COMPILE_OPTIONS(/wd4351)
+    ADD_COMPILE_OPTIONS(/wd4351)	
   ENDIF()
+  ADD_COMPILE_OPTIONS(/wd4244) #supress warning for double to float conversion if Double precission is activated
   SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od")
   SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
   SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF")
@@ -277,19 +290,18 @@ ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" )
   ENDIF()
   SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long ${CMAKE_CXX_FLAGS}" )
   SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
-ELSEIF( CMAKE_COMPILER_IS_MINGW )
+ELSEIF( MINGW )
   IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
     message(FATAL_ERROR "MinGW is too old to be supported. Please update MinGW and try again.")
   ELSEIF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.3)
     message(WARNING "MinGW is old, if you experience errors, update MinGW.")
   ENDIF()
   IF(NOT ASSIMP_HUNTER_ENABLED)
-    SET(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
+    SET(CMAKE_CXX_FLAGS "-std=gnu++11 ${CMAKE_CXX_FLAGS}")
     SET(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}")
   ENDIF()
   SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long -Wa,-mbig-obj -O3 ${CMAKE_CXX_FLAGS}")
   SET(CMAKE_C_FLAGS "-fno-strict-aliasing ${CMAKE_C_FLAGS}")
-  ADD_DEFINITIONS( -U__STRICT_ANSI__ )
 ENDIF()
 
 IF ( IOS AND NOT ASSIMP_HUNTER_ENABLED)
@@ -336,9 +348,24 @@ INCLUDE (FindPkgMacros)
 INCLUDE (PrecompiledHeader)
 
 # Set Assimp project output directory variables.
-SET(ASSIMP_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" CACHE STRING "Path for runtime output files")
-SET(ASSIMP_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" CACHE STRING "Path for library output files")
-SET(ASSIMP_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" CACHE STRING "Path for archive output files")
+# Will respect top-level CMAKE_*_OUTPUT_DIRECTORY variables if any are set.
+IF(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
+  SET(ASSIMP_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" CACHE STRING "Path for runtime output files")
+ELSE()
+  SET(ASSIMP_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} CACHE STRING "Path for runtime output files")
+ENDIF()
+
+IF(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY)
+  SET(ASSIMP_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" CACHE STRING "Path for library output files")
+ELSE()
+  SET(ASSIMP_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} CACHE STRING "Path for runtime output files")
+ENDIF()
+
+IF(NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
+  SET(ASSIMP_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" CACHE STRING "Path for library output files")
+ELSE()
+  SET(ASSIMP_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} CACHE STRING "Path for runtime output files")
+ENDIF()
 
 # Macro used to set the output directories of a target to the
 # respective Assimp output directories.
@@ -667,11 +694,13 @@ ENDIF()
 ADD_SUBDIRECTORY( code/ )
 IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
   # The viewer for windows only
-  IF ( WIN32 )
+  IF (WIN32)
     OPTION ( ASSIMP_BUILD_ASSIMP_VIEW "If the Assimp view tool is built. (requires DirectX)" OFF )
     IF ( ASSIMP_BUILD_ASSIMP_VIEW )
       ADD_SUBDIRECTORY( tools/assimp_view/ )
     ENDIF ()
+  ELSE()
+    MESSAGE("Building Assimp Viewer only supported on Windows.")
   ENDIF ()
   # The command line tool
   ADD_SUBDIRECTORY( tools/assimp_cmd/ )
@@ -759,7 +788,7 @@ if(WIN32)
 
   IF(MSVC_TOOLSET_VERSION)
     SET(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
-    SET(ASSIMP_MSVC_VERSION ${MCVS_PREFIX})
+    SET(ASSIMP_MSVC_VERSION ${MSVC_PREFIX})
   ELSE()
     IF(MSVC12)
       SET(ASSIMP_MSVC_VERSION "vc120")

+ 2 - 0
Readme.md

@@ -42,7 +42,9 @@ Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file.
 * [.NET](https://bitbucket.org/Starnick/assimpnet/src/master/)
 * [Pascal](port/AssimpPascal/Readme.md)
 * [Javascript (Alpha)](https://github.com/makc/assimp2json)
+* [Javascript/Node.js Interface](https://github.com/kovacsv/assimpjs)
 * [Unity 3d Plugin](https://ricardoreis.net/trilib-2/)
+* [Unreal Engine Plugin](https://github.com/irajsb/UE4_Assimp/)
 * [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status))
 * [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port.
 * [Rust](https://github.com/jkvargas/russimp)

+ 0 - 17
cmake-modules/FindIrrXML.cmake

@@ -1,17 +0,0 @@
-# Find IrrXMl from irrlicht project
-#
-# Find LibIrrXML headers and library
-#
-#   IRRXML_FOUND          - IrrXML found
-#   IRRXML_INCLUDE_DIR    - Headers location
-#   IRRXML_LIBRARY        - IrrXML main library
-
-find_path(IRRXML_INCLUDE_DIR irrXML.h
-    PATH_SUFFIXES include/irrlicht include/irrxml)
-find_library(IRRXML_LIBRARY IrrXML)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(IrrXML REQUIRED_VARS IRRXML_INCLUDE_DIR IRRXML_LIBRARY)
-
-
-mark_as_advanced(IRRXML_INCLUDE_DIR IRRXML_LIBRARY)

+ 4 - 9
code/AssetLib/3DS/3DSConverter.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -68,8 +68,8 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() {
     unsigned int idx(NotSet);
     for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) {
         std::string s = mScene->mMaterials[i].mName;
-        for (std::string::iterator it = s.begin(); it != s.end(); ++it) {
-            *it = static_cast<char>(::tolower(static_cast<unsigned char>(*it)));
+        for (char & it : s) {
+            it = static_cast<char>(::tolower(static_cast<unsigned char>(it)));
         }
 
         if (std::string::npos == s.find("default")) continue;
@@ -79,12 +79,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() {
                 mScene->mMaterials[i].mDiffuse.r !=
                         mScene->mMaterials[i].mDiffuse.b) continue;
 
-        if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 ||
-                mScene->mMaterials[i].sTexBump.mMapName.length() != 0 ||
-                mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 ||
-                mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 ||
-                mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 ||
-                mScene->mMaterials[i].sTexShininess.mMapName.length() != 0) {
+        if (ContainsTextures(i)) {
             continue;
         }
         idx = i;

+ 12 - 6
code/AssetLib/3DS/3DSExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -56,8 +56,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <memory>
 
-using namespace Assimp;
 namespace Assimp {
+
 using namespace D3DS;
 
 namespace {
@@ -330,6 +330,7 @@ void Discreet3DSExporter::WriteMaterials() {
             case aiShadingMode_Blinn:
             case aiShadingMode_CookTorrance:
             case aiShadingMode_Fresnel:
+            case aiShadingMode_PBR_BRDF: // Possibly should be Discreet3DS::Metal in some cases but this is undocumented
                 shading_mode_out = Discreet3DS::Phong;
                 break;
 
@@ -356,7 +357,10 @@ void Discreet3DSExporter::WriteMaterials() {
             writer.PutI2(1);
         }
 
-        WriteTexture(mat, aiTextureType_DIFFUSE, Discreet3DS::CHUNK_MAT_TEXTURE);
+        // Fallback to BASE_COLOR if no DIFFUSE
+        if (!WriteTexture(mat, aiTextureType_DIFFUSE, Discreet3DS::CHUNK_MAT_TEXTURE))
+            WriteTexture(mat, aiTextureType_BASE_COLOR, Discreet3DS::CHUNK_MAT_TEXTURE);
+
         WriteTexture(mat, aiTextureType_HEIGHT, Discreet3DS::CHUNK_MAT_BUMPMAP);
         WriteTexture(mat, aiTextureType_OPACITY, Discreet3DS::CHUNK_MAT_OPACMAP);
         WriteTexture(mat, aiTextureType_SHININESS, Discreet3DS::CHUNK_MAT_MAT_SHINMAP);
@@ -367,20 +371,21 @@ void Discreet3DSExporter::WriteMaterials() {
 }
 
 // ------------------------------------------------------------------------------------------------
-void Discreet3DSExporter::WriteTexture(const aiMaterial &mat, aiTextureType type, uint16_t chunk_flags) {
+// returns true if the texture existed
+bool Discreet3DSExporter::WriteTexture(const aiMaterial &mat, aiTextureType type, uint16_t chunk_flags) {
     aiString path;
     aiTextureMapMode map_mode[2] = {
         aiTextureMapMode_Wrap, aiTextureMapMode_Wrap
     };
     ai_real blend = 1.0;
     if (mat.GetTexture(type, 0, &path, nullptr, nullptr, &blend, nullptr, map_mode) != AI_SUCCESS || !path.length) {
-        return;
+        return false;
     }
 
     // TODO: handle embedded textures properly
     if (path.data[0] == '*') {
         ASSIMP_LOG_ERROR("Ignoring embedded texture for export: ", path.C_Str());
-        return;
+        return false;
     }
 
     ChunkWriter chunk(writer, chunk_flags);
@@ -402,6 +407,7 @@ void Discreet3DSExporter::WriteTexture(const aiMaterial &mat, aiTextureType type
         writer.PutU2(val);
     }
     // TODO: export texture transformation (i.e. UV offset, scale, rotation)
+    return true;
 }
 
 // ------------------------------------------------------------------------------------------------

+ 2 - 2
code/AssetLib/3DS/3DSExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -73,7 +73,7 @@ public:
 private:
     void WriteMeshes();
     void WriteMaterials();
-    void WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags);
+    bool WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags);
     void WriteFaceMaterialChunk(const aiMesh& mesh);
     int WriteHierarchy(const aiNode& node, int level, int sibling_level);
     void WriteString(const std::string& s);

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -53,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/light.h>
 #include <assimp/material.h>
 #include <assimp/qnan.h>
-#include <stdio.h> //sprintf
+#include <cstdio> //sprintf
 
 namespace Assimp {
 namespace D3DS {
@@ -259,7 +259,7 @@ namespace Discreet3DS {
         // Specifies the file name of a texture
         CHUNK_MAPFILE = 0xA300,
 
-        // Specifies whether a materail requires two-sided rendering
+        // Specifies whether a material requires two-sided rendering
         CHUNK_MAT_TWO_SIDE = 0xA081,
         // ********************************************************************
 

+ 5 - 16
code/AssetLib/3DS/3DSLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -111,20 +111,9 @@ Discreet3DSImporter::~Discreet3DSImporter() {
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool Discreet3DSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
-    std::string extension = GetExtension(pFile);
-    if (extension == "3ds" || extension == "prj") {
-        return true;
-    }
-
-    if (!extension.length() || checkSig) {
-        uint16_t token[3];
-        token[0] = 0x4d4d;
-        token[1] = 0x3dc2;
-        //token[2] = 0x3daa;
-        return CheckMagicToken(pIOHandler, pFile, token, 2, 0, 2);
-    }
-    return false;
+bool Discreet3DSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+    static const uint16_t token[] = { 0x4d4d, 0x3dc2 /*, 0x3daa */ };
+    return CheckMagicToken(pIOHandler, pFile, token, AI_COUNT_OF(token), 0, sizeof token[0]);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -449,7 +438,7 @@ void Discreet3DSImporter::ParseChunk(const char *name, unsigned int num) {
         // Read the lense angle
         camera->mHorizontalFOV = AI_DEG_TO_RAD(stream->GetF4());
         if (camera->mHorizontalFOV < 0.001f) {
-            camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f);
+            camera->mHorizontalFOV = float(AI_DEG_TO_RAD(45.f));
         }
 
         // Now check for further subchunks

+ 15 - 6
code/AssetLib/3DS/3DSLoader.h

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -46,11 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #ifndef AI_3DSIMPORTER_H_INC
 #define AI_3DSIMPORTER_H_INC
+#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
 #include <assimp/BaseImporter.h>
 #include <assimp/types.h>
 
-#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
 #include "3DSHelper.h"
 #include <assimp/StreamReader.h>
@@ -75,14 +75,14 @@ public:
      * See BaseImporter::CanRead() for details.
      */
     bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
-        bool checkSig) const;
+        bool checkSig) const override;
 
     // -------------------------------------------------------------------
     /** Called prior to ReadFile().
      * The function is a request to the importer to update its configuration
      * basing on the Importer's configuration property list.
      */
-    void SetupProperties(const Importer* pImp);
+    void SetupProperties(const Importer* pImp) override;
 
 protected:
 
@@ -90,14 +90,14 @@ protected:
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details
      */
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc* GetInfo () const override;
 
     // -------------------------------------------------------------------
     /** Imports the given file into the given scene structure.
      * See BaseImporter::InternReadFile() for details
      */
     void InternReadFile( const std::string& pFile, aiScene* pScene,
-        IOSystem* pIOHandler);
+        IOSystem* pIOHandler) override;
 
     // -------------------------------------------------------------------
     /** Converts a temporary material to the outer representation
@@ -208,6 +208,15 @@ protected:
     */
     void ReplaceDefaultMaterial();
 
+    bool ContainsTextures(unsigned int i) const {
+        return !mScene->mMaterials[i].sTexDiffuse.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexBump.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexOpacity.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexEmissive.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexSpecular.mMapName.empty() ||
+               !mScene->mMaterials[i].sTexShininess.mMapName.empty() ;
+    }
+
     // -------------------------------------------------------------------
     /** Convert the whole scene
     */

+ 165 - 0
code/AssetLib/3MF/3MFTypes.h

@@ -0,0 +1,165 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include <assimp/vector3.h>
+#include <assimp/matrix4x4.h>
+#include <assimp/ParsingUtils.h>
+#include <vector>
+#include <string>
+
+struct aiMaterial;
+struct aiMesh;
+
+namespace Assimp {
+namespace D3MF {
+
+enum class ResourceType {
+    RT_Object,
+    RT_BaseMaterials,
+    RT_EmbeddedTexture2D,
+    RT_Texture2DGroup,
+    RT_Unknown
+}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...)
+
+class Resource {
+public:
+    int mId;
+
+    Resource(int id) :
+            mId(id) {
+        // empty
+    }
+
+    virtual ~Resource() {
+        // empty
+    }
+
+    virtual ResourceType getType() const {
+        return ResourceType::RT_Unknown;
+    }
+};
+
+class EmbeddedTexture : public Resource {
+public:
+    std::string mPath;
+    std::string mContentType;
+    std::string mTilestyleU;
+    std::string mTilestyleV;
+    std::vector<char> mBuffer;
+
+    EmbeddedTexture(int id) :
+            Resource(id),
+            mPath(),
+            mContentType(),
+            mTilestyleU(),
+            mTilestyleV() {
+        // empty
+    }
+
+    ~EmbeddedTexture() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_EmbeddedTexture2D;
+    }
+};
+
+class Texture2DGroup : public Resource {
+public:
+    std::vector<aiVector2D> mTex2dCoords;
+    int mTexId;
+    Texture2DGroup(int id) :
+            Resource(id),
+            mTexId(-1) {
+        // empty
+    }
+
+    ~Texture2DGroup() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_Texture2DGroup;
+    }
+};
+
+class BaseMaterials : public Resource {
+public:
+    std::vector<unsigned int> mMaterialIndex;
+
+    BaseMaterials(int id) :
+            Resource(id),
+            mMaterialIndex() {
+        // empty
+    }
+
+    ~BaseMaterials() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_BaseMaterials;
+    }
+};
+
+struct Component {
+    int mObjectId;
+    aiMatrix4x4 mTransformation;
+};
+
+class Object : public Resource {
+public:
+    std::vector<aiMesh *> mMeshes;
+    std::vector<unsigned int> mMeshIndex;
+    std::vector<Component> mComponents;
+    std::string mName;
+
+    Object(int id) :
+            Resource(id),
+            mName(std::string("Object_") + ai_to_string(id)) {
+        // empty
+    }
+
+    ~Object() = default;
+
+    ResourceType getType() const override {
+        return ResourceType::RT_Object;
+    }
+};
+
+} // namespace D3MF
+} // namespace Assimp

+ 10 - 2
code/AssetLib/3MF/3MFXmlTags.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -80,13 +80,21 @@ namespace XmlTag {
     const char* const item = "item";
     const char* const objectid = "objectid";
     const char* const transform = "transform";
+    const char *const path = "path";
 
     // Material definitions
     const char* const basematerials = "basematerials";
-    const char* const basematerials_id = "id";
     const char* const basematerials_base = "base";
     const char* const basematerials_name = "name";
     const char* const basematerials_displaycolor = "displaycolor";
+    const char* const texture_2d = "m:texture2d";
+    const char *const texture_group = "m:texture2dgroup";
+    const char *const texture_content_type = "contenttype";
+    const char *const texture_tilestyleu = "tilestyleu";
+    const char *const texture_tilestylev = "tilestylev";
+    const char *const texture_2d_coord = "m:tex2coord";
+    const char *const texture_cuurd_u = "u";
+    const char *const texture_cuurd_v = "v";
 
     // Meta info tags
     const char* const CONTENT_TYPES_ARCHIVE = "[Content_Types].xml";

+ 1 - 1
code/AssetLib/3MF/D3MFExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

+ 8 - 5
code/AssetLib/3MF/D3MFExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -40,6 +40,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 #pragma once
 
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
+
 #include <memory>
 #include <sstream>
 #include <vector>
@@ -58,8 +61,6 @@ class IOStream;
 
 namespace D3MF {
 
-#ifndef ASSIMP_BUILD_NO_EXPORT
-#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
 
 struct OpcPackageRelationship;
 
@@ -100,9 +101,11 @@ private:
     std::vector<OpcPackageRelationship*> mRelations;
 };
 
-#endif // ASSIMP_BUILD_NO_3MF_EXPORTER
-#endif // ASSIMP_BUILD_NO_EXPORT
 
 } // Namespace D3MF
 } // Namespace Assimp
 
+#endif // ASSIMP_BUILD_NO_3MF_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT
+
+

+ 18 - 522
code/AssetLib/3MF/D3MFImporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "D3MFImporter.h"
 #include "3MFXmlTags.h"
 #include "D3MFOpcPackage.h"
+#include "XmlSerializer.h"
 
 #include <assimp/StringComparison.h>
 #include <assimp/StringUtils.h>
@@ -61,513 +62,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <string>
 #include <vector>
 #include <iomanip>
-#include <string.h>
+#include <cstring>
 
 namespace Assimp {
-namespace D3MF {
-
-enum class ResourceType {
-    RT_Object,
-    RT_BaseMaterials,
-    RT_Unknown
-}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...)
-
-class Resource {
-public:
-    int mId;
-
-    Resource(int id) :
-            mId(id) {
-        // empty
-    }
-
-    virtual ~Resource() {
-        // empty
-    }
-
-    virtual ResourceType getType() const {
-        return ResourceType::RT_Unknown;
-    }
-};
-
-class BaseMaterials : public Resource {
-public:
-    std::vector<aiMaterial *> mMaterials;
-    std::vector<unsigned int> mMaterialIndex;
-
-    BaseMaterials(int id) :
-            Resource(id),
-            mMaterials(),
-            mMaterialIndex() {
-        // empty
-    }
-
-    ~BaseMaterials() = default;
-
-    ResourceType getType() const override {
-        return ResourceType::RT_BaseMaterials;
-    }
-};
-
-struct Component {
-    int mObjectId;
-    aiMatrix4x4 mTransformation;
-};
-
-class Object : public Resource {
-public:
-    std::vector<aiMesh *> mMeshes;
-    std::vector<unsigned int> mMeshIndex;
-    std::vector<Component> mComponents;
-    std::string mName;
-
-    Object(int id) :
-            Resource(id),
-            mName(std::string("Object_") + ai_to_string(id)) {
-        // empty
-    }
-
-    ~Object() = default;
-
-    ResourceType getType() const override {
-        return ResourceType::RT_Object;
-    }
-};
-
-class XmlSerializer {
-public:
-    XmlSerializer(XmlParser *xmlParser) :
-            mResourcesDictionnary(),
-            mMaterialCount(0),
-            mMeshCount(0),
-            mXmlParser(xmlParser) {
-        // empty
-    }
-
-    ~XmlSerializer() {
-        for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it ) {
-            delete it->second;
-        }
-    }
-
-    void ImportXml(aiScene *scene) {
-        if (nullptr == scene) {
-            return;
-        }
-
-        scene->mRootNode = new aiNode(XmlTag::RootTag);
-
-        XmlNode node = mXmlParser->getRootNode().child(XmlTag::model);
-        if (node.empty()) {
-            return;
-        }
-        XmlNode resNode = node.child(XmlTag::resources);
-        for (auto &currentNode : resNode.children()) {
-            const std::string currentNodeName = currentNode.name();
-            if (currentNodeName == XmlTag::object) {
-                ReadObject(currentNode);
-            } else if (currentNodeName == XmlTag::basematerials) {
-                ReadBaseMaterials(currentNode);
-            } else if (currentNodeName == XmlTag::meta) {
-                ReadMetadata(currentNode);
-            }
-        }
-
-        XmlNode buildNode = node.child(XmlTag::build);
-        for (auto &currentNode : buildNode.children()) {
-            const std::string currentNodeName = currentNode.name();
-            if (currentNodeName == XmlTag::item) {
-                int objectId = -1;
-                std::string transformationMatrixStr;
-                aiMatrix4x4 transformationMatrix;
-                getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId);
-                bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr);
-
-                auto it = mResourcesDictionnary.find(objectId);
-                if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
-                    Object *obj = static_cast<Object *>(it->second);
-                    if (hasTransform) {
-                        transformationMatrix = parseTransformMatrix(transformationMatrixStr);
-                    }
-
-                    addObjectToNode(scene->mRootNode, obj, transformationMatrix);
-                }
-            }
-        }
-
-        // import the metadata
-        if (!mMetaData.empty()) {
-            const size_t numMeta = mMetaData.size();
-            scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta));
-            for (size_t i = 0; i < numMeta; ++i) {
-                aiString val(mMetaData[i].value);
-                scene->mMetaData->Set(static_cast<unsigned int>(i), mMetaData[i].name, val);
-            }
-        }
-
-        // import the meshes
-        scene->mNumMeshes = static_cast<unsigned int>(mMeshCount);
-        if (scene->mNumMeshes != 0) {
-            scene->mMeshes = new aiMesh *[scene->mNumMeshes]();
-            for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) {
-                if (it->second->getType() == ResourceType::RT_Object) {
-                    Object *obj = static_cast<Object *>(it->second);
-                    ai_assert(nullptr != obj);
-                    for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) {
-                        scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i];
-                    }
-                }
-            }
-        }
-
-        // import the materials
-        scene->mNumMaterials = mMaterialCount;
-        if (scene->mNumMaterials != 0) {
-            scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
-            for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) {
-                if (it->second->getType() == ResourceType::RT_BaseMaterials) {
-                    BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
-                    for (unsigned int i = 0; i < baseMaterials->mMaterials.size(); ++i) {
-                        scene->mMaterials[baseMaterials->mMaterialIndex[i]] = baseMaterials->mMaterials[i];
-                    }
-                }
-            }
-        }
-    }
-
-private:
-    void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) {
-        ai_assert(nullptr != obj);
-
-        aiNode *sceneNode = new aiNode(obj->mName);
-        sceneNode->mNumMeshes = static_cast<unsigned int>(obj->mMeshes.size());
-        sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes];
-        std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes);
-
-        sceneNode->mTransformation = nodeTransform;
-        if (nullptr != parent) {
-            parent->addChildren(1, &sceneNode);
-        }
-
-        for (size_t i = 0; i < obj->mComponents.size(); ++i) {
-            Component c = obj->mComponents[i];
-            auto it = mResourcesDictionnary.find(c.mObjectId);
-            if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
-                addObjectToNode(sceneNode, static_cast<Object *>(it->second), c.mTransformation);
-            }
-        }
-    }
-
-    bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) {
-        pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str());
-        if (!objectAttribute.empty()) {
-            value = objectAttribute.as_string();
-            return true;
-        }
-
-        return false;
-    }
-
-    bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) {
-        std::string strValue;
-        bool ret = getNodeAttribute(node, attribute, strValue);
-        if (ret) {
-            value = std::atoi(strValue.c_str());
-            return true;
-        }
-
-        return false;
-    }
-
-    aiMatrix4x4 parseTransformMatrix(std::string matrixStr) {
-        // split the string
-        std::vector<float> numbers;
-        std::string currentNumber;
-        for (size_t i = 0; i < matrixStr.size(); ++i) {
-            const char c = matrixStr[i];
-            if (c == ' ') {
-                if (currentNumber.size() > 0) {
-                    float f = std::stof(currentNumber);
-                    numbers.push_back(f);
-                    currentNumber.clear();
-                }
-            } else {
-                currentNumber.push_back(c);
-            }
-        }
-        if (currentNumber.size() > 0) {
-            const float f = std::stof(currentNumber);
-            numbers.push_back(f);
-        }
-
-        aiMatrix4x4 transformMatrix;
-        transformMatrix.a1 = numbers[0];
-        transformMatrix.b1 = numbers[1];
-        transformMatrix.c1 = numbers[2];
-        transformMatrix.d1 = 0;
-
-        transformMatrix.a2 = numbers[3];
-        transformMatrix.b2 = numbers[4];
-        transformMatrix.c2 = numbers[5];
-        transformMatrix.d2 = 0;
-
-        transformMatrix.a3 = numbers[6];
-        transformMatrix.b3 = numbers[7];
-        transformMatrix.c3 = numbers[8];
-        transformMatrix.d3 = 0;
-
-        transformMatrix.a4 = numbers[9];
-        transformMatrix.b4 = numbers[10];
-        transformMatrix.c4 = numbers[11];
-        transformMatrix.d4 = 1;
-
-        return transformMatrix;
-    }
-
-    void ReadObject(XmlNode &node) {
-        int id = -1, pid = -1, pindex = -1;
-        bool hasId = getNodeAttribute(node, XmlTag::id, id);
-        bool hasPid = getNodeAttribute(node, XmlTag::pid, pid);
-        bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex);
-        if (!hasId) {
-            return;
-        }
-
-        Object *obj = new Object(id);
-
-        for (XmlNode &currentNode : node.children()) {
-            const std::string &currentName = currentNode.name();
-            if (currentName == D3MF::XmlTag::mesh) {
-                auto mesh = ReadMesh(currentNode);
-                mesh->mName.Set(ai_to_string(id));
-
-                if (hasPid) {
-                    auto it = mResourcesDictionnary.find(pid);
-                    if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) {
-                        BaseMaterials *materials = static_cast<BaseMaterials *>(it->second);
-                        mesh->mMaterialIndex = materials->mMaterialIndex[pindex];
-                    }
-                }
-
-                obj->mMeshes.push_back(mesh);
-                obj->mMeshIndex.push_back(mMeshCount);
-                mMeshCount++;
-            } else if (currentName == D3MF::XmlTag::components) {
-                for (XmlNode &currentSubNode : currentNode.children()) {
-                    const std::string subNodeName = currentSubNode.name();
-                    if (subNodeName == D3MF::XmlTag::component) {
-                        int objectId = -1;
-                        std::string componentTransformStr;
-                        aiMatrix4x4 componentTransform;
-                        if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) {
-                            componentTransform = parseTransformMatrix(componentTransformStr);
-                        }
-
-                        if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) {
-                            obj->mComponents.push_back({ objectId, componentTransform });
-                        }
-                    }
-                }
-            }
-        }
-
-        mResourcesDictionnary.insert(std::make_pair(id, obj));
-    }
-
-    aiMesh *ReadMesh(XmlNode &node) {
-        aiMesh *mesh = new aiMesh();
-
-        for (XmlNode &currentNode : node.children()) {
-            const std::string currentName = currentNode.name();
-            if (currentName == XmlTag::vertices) {
-                ImportVertices(currentNode, mesh);
-            } else if (currentName == XmlTag::triangles) {
-                ImportTriangles(currentNode, mesh);
-            }
-        }
-
-        return mesh;
-    }
-
-    void ReadMetadata(XmlNode &node) {
-        pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name);
-        const std::string name = attribute.as_string();
-        const std::string value = node.value();
-        if (name.empty()) {
-            return;
-        }
-
-        MetaEntry entry;
-        entry.name = name;
-        entry.value = value;
-        mMetaData.push_back(entry);
-    }
-
-    void ImportVertices(XmlNode &node, aiMesh *mesh) {
-        std::vector<aiVector3D> vertices;
-        for (XmlNode &currentNode : node.children()) {
-            const std::string currentName = currentNode.name();
-            if (currentName == XmlTag::vertex) {
-                vertices.push_back(ReadVertex(currentNode));
-            }
-        }
-
-        mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
-        mesh->mVertices = new aiVector3D[mesh->mNumVertices];
-        std::copy(vertices.begin(), vertices.end(), mesh->mVertices);
-    }
-
-    aiVector3D ReadVertex(XmlNode &node) {
-        aiVector3D vertex;
-        vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr);
-        vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr);
-        vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr);
-
-        return vertex;
-    }
-
-    void ImportTriangles(XmlNode &node, aiMesh *mesh) {
-        std::vector<aiFace> faces;
-        for (XmlNode &currentNode : node.children()) {
-            const std::string currentName = currentNode.name();
-            if (currentName == XmlTag::triangle) {
-                aiFace face = ReadTriangle(currentNode);
-                faces.push_back(face);
-
-                int pid = 0, p1 = 0;
-                bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
-                bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1);
-
-                if (hasPid && hasP1) {
-                    auto it = mResourcesDictionnary.find(pid);
-                    if (it != mResourcesDictionnary.end()) {
-                        if (it->second->getType() == ResourceType::RT_BaseMaterials) {
-                            BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
-                            mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1];
-                        }
-                        // TODO: manage the separation into several meshes if the triangles of the mesh do not all refer to the same material
-                    }
-                }
-            }
-        }
-
-        mesh->mNumFaces = static_cast<unsigned int>(faces.size());
-        mesh->mFaces = new aiFace[mesh->mNumFaces];
-        mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
-
-        std::copy(faces.begin(), faces.end(), mesh->mFaces);
-    }
-
-    aiFace ReadTriangle(XmlNode &node) {
-        aiFace face;
-
-        face.mNumIndices = 3;
-        face.mIndices = new unsigned int[face.mNumIndices];
-        face.mIndices[0] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v1).as_string()));
-        face.mIndices[1] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v2).as_string()));
-        face.mIndices[2] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v3).as_string()));
-
-        return face;
-    }
-
-    void ReadBaseMaterials(XmlNode &node) {
-        int id = -1;
-        if (getNodeAttribute(node, D3MF::XmlTag::basematerials_id, id)) {
-            BaseMaterials *baseMaterials = new BaseMaterials(id);
-
-            for (XmlNode &currentNode : node.children()) {
-                const std::string currentName = currentNode.name();
-                if (currentName == XmlTag::basematerials_base) {
-                    baseMaterials->mMaterialIndex.push_back(mMaterialCount);
-                    baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id));
-                    ++mMaterialCount;
-                }
-            }
-
-            mResourcesDictionnary.insert(std::make_pair(id, baseMaterials));
-        }
-    }
-
-    bool parseColor(const char *color, aiColor4D &diffuse) {
-        if (nullptr == color) {
-            return false;
-        }
-
-        //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
-        const size_t len = strlen(color);
-        if (9 != len && 7 != len) {
-            return false;
-        }
-
-        const char *buf(color);
-        if ('#' != buf[0]) {
-            return false;
-        }
-
-        char r[3] = { buf[1], buf[2], '\0' };
-        diffuse.r = static_cast<ai_real>(strtol(r, nullptr, 16)) / ai_real(255.0);
-
-        char g[3] = { buf[3], buf[4], '\0' };
-        diffuse.g = static_cast<ai_real>(strtol(g, nullptr, 16)) / ai_real(255.0);
-
-        char b[3] = { buf[5], buf[6], '\0' };
-        diffuse.b = static_cast<ai_real>(strtol(b, nullptr, 16)) / ai_real(255.0);
-
-        if (7 == len)
-            return true;
-
-        char a[3] = { buf[7], buf[8], '\0' };
-        diffuse.a = static_cast<ai_real>(strtol(a, nullptr, 16)) / ai_real(255.0);
-
-        return true;
-    }
-
-    void assignDiffuseColor(XmlNode &node, aiMaterial *mat) {
-        const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string();
-        aiColor4D diffuse;
-        if (parseColor(color, diffuse)) {
-            mat->AddProperty<aiColor4D>(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
-        }
-    }
-
-    aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId) {
-        aiMaterial *material = new aiMaterial();
-        material->mNumProperties = 0;
-        std::string name;
-        bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name);
-
-        std::string stdMaterialName;
-        const std::string strId(ai_to_string(basematerialsId));
-        stdMaterialName += "id";
-        stdMaterialName += strId;
-        stdMaterialName += "_";
-        if (hasName) {
-            stdMaterialName += std::string(name);
-        } else {
-            stdMaterialName += "basemat_";
-            stdMaterialName += ai_to_string(mMaterialCount - basematerialsId);
-        }
-
-        aiString assimpMaterialName(stdMaterialName);
-        material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME);
-
-        assignDiffuseColor(node, material);
-
-        return material;
-    }
-
-private:
-    struct MetaEntry {
-        std::string name;
-        std::string value;
-    };
-    std::vector<MetaEntry> mMetaData;
-    std::map<unsigned int, Resource *> mResourcesDictionnary;
-    unsigned int mMaterialCount, mMeshCount;
-    XmlParser *mXmlParser;
-};
-
-} //namespace D3MF
 
 using namespace D3MF;
 
@@ -593,25 +90,15 @@ D3MFImporter::~D3MFImporter() {
     // empty
 }
 
-bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const {
-    const std::string extension(GetExtension(filename));
-    if (extension == desc.mFileExtensions) {
-        return true;
-    } else if (!extension.length() || checkSig) {
-        if (nullptr == pIOHandler) {
-            return false;
-        }
-        if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
-            return false;
-        }
-        D3MFOpcPackage opcPackage(pIOHandler, filename);
-        return opcPackage.validate();
+bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool /*checkSig*/) const {
+    if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
+        return false;
     }
-
-    return false;
+    D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
+    return opcPackage.validate();
 }
 
-void D3MFImporter::SetupProperties(const Importer * /*pImp*/) {
+void D3MFImporter::SetupProperties(const Importer*) {
     // empty
 }
 
@@ -626,6 +113,15 @@ void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene,
     if (xmlParser.parse(opcPackage.RootStream())) {
         XmlSerializer xmlSerializer(&xmlParser);
         xmlSerializer.ImportXml(pScene);
+
+        const std::vector<aiTexture*> &tex =  opcPackage.GetEmbeddedTextures();
+        if (!tex.empty()) {
+            pScene->mNumTextures = static_cast<unsigned int>(tex.size());
+            pScene->mTextures = new aiTexture *[pScene->mNumTextures];
+            for (unsigned int i = 0; i < pScene->mNumTextures; ++i) {
+                pScene->mTextures[i] = tex[i];
+            }
+        }
     }
 }
 

+ 33 - 7
code/AssetLib/3MF/D3MFImporter.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,23 +42,50 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_D3MFLOADER_H_INCLUDED
 #define AI_D3MFLOADER_H_INCLUDED
 
+#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
+
 #include <assimp/BaseImporter.h>
 
 namespace Assimp {
 
+// ---------------------------------------------------------------------------
 /// @brief  The 3MF-importer class.
+///
+/// Implements the basic topology import and embedded textures.
+// ---------------------------------------------------------------------------
 class D3MFImporter : public BaseImporter {
 public:
+    /// @brief The default class constructor.
     D3MFImporter();
-    ~D3MFImporter();
-    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const;
-    void SetupProperties(const Importer *pImp);
-    const aiImporterDesc *GetInfo() const;
+
+    ///	@brief  The class destructor.
+    ~D3MFImporter() override;
+
+    /// @brief Performs the data format detection.
+    /// @param pFile        The filename to check.
+    /// @param pIOHandler   The used IO-System.
+    /// @param checkSig     true for signature checking.
+    /// @return true for can be loaded, false for not.
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
+
+    /// @brief  Not used
+    /// @param pImp Not used
+    void SetupProperties(const Importer *pImp) override;
+
+    /// @brief The importer description getter.
+    /// @return The info
+    const aiImporterDesc *GetInfo() const override;
 
 protected:
-    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
+    /// @brief Internal read function, performs the file parsing.
+    /// @param pFile        The filename
+    /// @param pScene       The scene to load in.
+    /// @param pIOHandler   The io-system
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
 };
 
 } // Namespace Assimp
 
+#endif // #ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
+
 #endif // AI_D3MFLOADER_H_INCLUDED

+ 60 - 14
code/AssetLib/3MF/D3MFOpcPackage.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,20 +43,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "D3MFOpcPackage.h"
 #include <assimp/Exceptional.h>
-
 #include <assimp/XmlParser.h>
 #include <assimp/ZipArchiveIOSystem.h>
 #include <assimp/ai_assert.h>
 #include <assimp/DefaultLogger.hpp>
 #include <assimp/IOStream.hpp>
 #include <assimp/IOSystem.hpp>
-
+#include <assimp/texture.h>
 #include "3MFXmlTags.h"
+
 #include <algorithm>
 #include <cassert>
 #include <cstdlib>
 #include <map>
-#include <memory>
 #include <vector>
 
 namespace Assimp {
@@ -64,11 +63,12 @@ namespace Assimp {
 namespace D3MF {
 // ------------------------------------------------------------------------------------------------
 
-typedef std::shared_ptr<OpcPackageRelationship> OpcPackageRelationshipPtr;
+using OpcPackageRelationshipPtr = std::shared_ptr<OpcPackageRelationship>;
 
 class OpcPackageRelationshipReader {
 public:
-    OpcPackageRelationshipReader(XmlParser &parser) {
+    OpcPackageRelationshipReader(XmlParser &parser) :
+            m_relationShips() {
         XmlNode root = parser.getRootNode();
         ParseRootNode(root);
     }
@@ -91,6 +91,7 @@ public:
         if (relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty()) {
             return false;
         }
+
         return true;
     }
 
@@ -100,7 +101,7 @@ public:
         }
 
         for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
-            std::string name = currentNode.name();
+            const std::string name = currentNode.name();
             if (name == "Relationship") {
                 OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship());
                 relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID).as_string();
@@ -116,11 +117,23 @@ public:
     std::vector<OpcPackageRelationshipPtr> m_relationShips;
 };
 
+static bool IsEmbeddedTexture( const std::string &filename ) {
+    const std::string extension = BaseImporter::GetExtension(filename);
+    if (extension == "jpg" || extension == "png") {
+        std::string::size_type pos = filename.find("thumbnail");
+        if (pos == std::string::npos) {
+            return false;
+        }
+        return true;
+    }
+
+    return false;
+}
 // ------------------------------------------------------------------------------------------------
 D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
         mRootStream(nullptr),
         mZipArchive() {
-    mZipArchive.reset(new ZipArchiveIOSystem(pIOHandler, rFile));
+    mZipArchive = new ZipArchiveIOSystem(pIOHandler, rFile);
     if (!mZipArchive->isOpen()) {
         throw DeadlyImportError("Failed to open file ", rFile, ".");
     }
@@ -136,18 +149,18 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
 
             IOStream *fileStream = mZipArchive->Open(file.c_str());
             if (nullptr == fileStream) {
-                ai_assert(fileStream != nullptr);
+                ASSIMP_LOG_ERROR("Filestream is nullptr.");
                 continue;
             }
 
             std::string rootFile = ReadPackageRootRelationship(fileStream);
-            if (rootFile.size() > 0 && rootFile[0] == '/') {
+            if (!rootFile.empty() && rootFile[0] == '/') {
                 rootFile = rootFile.substr(1);
                 if (rootFile[0] == '/') {
                     // deal with zip-bug
                     rootFile = rootFile.substr(1);
                 }
-            }
+            } 
 
             ASSIMP_LOG_VERBOSE_DEBUG(rootFile);
 
@@ -158,9 +171,12 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
             if (nullptr == mRootStream) {
                 throw DeadlyImportError("Cannot open root-file in archive : " + rootFile);
             }
-
         } else if (file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
             ASSIMP_LOG_WARN("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES", file);
+        } else if (IsEmbeddedTexture(file)) {
+            IOStream *fileStream = mZipArchive->Open(file.c_str());
+            LoadEmbeddedTextures(fileStream, file);
+            mZipArchive->Close(fileStream);
         } else {
             ASSIMP_LOG_WARN("Ignored file of unknown type: ", file);
         }
@@ -169,20 +185,25 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) :
 
 D3MFOpcPackage::~D3MFOpcPackage() {
     mZipArchive->Close(mRootStream);
+    delete mZipArchive;
 }
 
 IOStream *D3MFOpcPackage::RootStream() const {
     return mRootStream;
 }
 
-static const std::string ModelRef = "3D/3dmodel.model";
+const std::vector<aiTexture *> &D3MFOpcPackage::GetEmbeddedTextures() const {
+    return mEmbeddedTextures;
+}
+
+static const char *const ModelRef = "3D/3dmodel.model";
 
 bool D3MFOpcPackage::validate() {
     if (nullptr == mRootStream || nullptr == mZipArchive) {
         return false;
     }
 
-    return mZipArchive->Exists(ModelRef.c_str());
+    return mZipArchive->Exists(ModelRef);
 }
 
 std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) {
@@ -204,6 +225,31 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) {
     return (*itr)->target;
 }
 
+void D3MFOpcPackage::LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename) {
+    if (nullptr == fileStream) {
+        return;
+    }
+
+    const size_t size = fileStream->FileSize();
+    if (0 == size) {
+        return;
+    }
+
+    unsigned char *data = new unsigned char[size];
+    fileStream->Read(data, 1, size);
+    aiTexture *texture = new aiTexture;
+    std::string embName = "*" + filename;
+    texture->mFilename.Set(embName.c_str());
+    texture->mWidth = static_cast<unsigned int>(size);
+    texture->mHeight = 0;
+    texture->achFormatHint[0] = 'p';
+    texture->achFormatHint[1] = 'n';
+    texture->achFormatHint[2] = 'g';
+    texture->achFormatHint[3] = '\0';
+    texture->pcData = (aiTexel*) data;
+    mEmbeddedTextures.emplace_back(texture);
+}
+
 } // Namespace D3MF
 } // Namespace Assimp
 

+ 12 - 6
code/AssetLib/3MF/D3MFOpcPackage.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -42,12 +42,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef D3MFOPCPACKAGE_H
 #define D3MFOPCPACKAGE_H
 
+#include <assimp/IOSystem.hpp>
 #include <memory>
 #include <string>
-#include <assimp/IOSystem.hpp>
+
+struct aiTexture;
 
 namespace Assimp {
-    class ZipArchiveIOSystem;
+
+class ZipArchiveIOSystem;
 
 namespace D3MF {
 
@@ -63,16 +66,19 @@ public:
     ~D3MFOpcPackage();
     IOStream* RootStream() const;
     bool validate();
+    const std::vector<aiTexture*> &GetEmbeddedTextures() const;
 
 protected:
     std::string ReadPackageRootRelationship(IOStream* stream);
+    void LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename);
 
 private:
     IOStream* mRootStream;
-    std::unique_ptr<ZipArchiveIOSystem> mZipArchive;
+    ZipArchiveIOSystem *mZipArchive;
+    std::vector<aiTexture *> mEmbeddedTextures;
 };
 
-} // Namespace D3MF
-} // Namespace Assimp
+} // namespace D3MF
+} // namespace Assimp
 
 #endif // D3MFOPCPACKAGE_H

+ 593 - 0
code/AssetLib/3MF/XmlSerializer.cpp

@@ -0,0 +1,593 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#include "XmlSerializer.h"
+#include "D3MFOpcPackage.h"
+#include "3MFXmlTags.h"
+#include "3MFTypes.h"
+#include <assimp/scene.h>
+
+namespace Assimp {
+namespace D3MF {
+
+static const int IdNotSet = -1;
+
+namespace {
+
+static const size_t ColRGBA_Len = 9;
+static const size_t ColRGB_Len = 7;
+
+// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1)
+bool validateColorString(const char *color) {
+    const size_t len = strlen(color);
+    if (ColRGBA_Len != len && ColRGB_Len != len) {
+        return false;
+    }
+
+    return true;
+}
+
+aiFace ReadTriangle(XmlNode &node) {
+    aiFace face;
+
+    face.mNumIndices = 3;
+    face.mIndices = new unsigned int[face.mNumIndices];
+    face.mIndices[0] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v1).as_string()));
+    face.mIndices[1] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v2).as_string()));
+    face.mIndices[2] = static_cast<unsigned int>(std::atoi(node.attribute(XmlTag::v3).as_string()));
+
+    return face;
+}
+
+aiVector3D ReadVertex(XmlNode &node) {
+    aiVector3D vertex;
+    vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr);
+    vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr);
+    vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr);
+
+    return vertex;
+}
+
+bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) {
+    pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str());
+    if (!objectAttribute.empty()) {
+        value = objectAttribute.as_string();
+        return true;
+    }
+
+    return false;
+}
+
+bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) {
+    std::string strValue;
+    const bool ret = getNodeAttribute(node, attribute, strValue);
+    if (ret) {
+        value = std::atoi(strValue.c_str());
+        return true;
+    }
+
+    return false;
+}
+
+aiMatrix4x4 parseTransformMatrix(std::string matrixStr) {
+    // split the string
+    std::vector<float> numbers;
+    std::string currentNumber;
+    for (char c : matrixStr) {
+        if (c == ' ') {
+            if (!currentNumber.empty()) {
+                float f = std::stof(currentNumber);
+                numbers.push_back(f);
+                currentNumber.clear();
+            }
+        } else {
+            currentNumber.push_back(c);
+        }
+    }
+    if (!currentNumber.empty()) {
+        const float f = std::stof(currentNumber);
+        numbers.push_back(f);
+    }
+
+    aiMatrix4x4 transformMatrix;
+    transformMatrix.a1 = numbers[0];
+    transformMatrix.b1 = numbers[1];
+    transformMatrix.c1 = numbers[2];
+    transformMatrix.d1 = 0;
+
+    transformMatrix.a2 = numbers[3];
+    transformMatrix.b2 = numbers[4];
+    transformMatrix.c2 = numbers[5];
+    transformMatrix.d2 = 0;
+
+    transformMatrix.a3 = numbers[6];
+    transformMatrix.b3 = numbers[7];
+    transformMatrix.c3 = numbers[8];
+    transformMatrix.d3 = 0;
+
+    transformMatrix.a4 = numbers[9];
+    transformMatrix.b4 = numbers[10];
+    transformMatrix.c4 = numbers[11];
+    transformMatrix.d4 = 1;
+
+    return transformMatrix;
+}
+
+bool parseColor(const char *color, aiColor4D &diffuse) {
+    if (nullptr == color) {
+        return false;
+    }
+
+    if (!validateColorString(color)) {
+        return false;
+    }
+
+    if ('#' != color[0]) {
+        return false;
+    }
+
+    char r[3] = { color[1], color[2], '\0' };
+    diffuse.r = static_cast<ai_real>(strtol(r, nullptr, 16)) / ai_real(255.0);
+
+    char g[3] = { color[3], color[4], '\0' };
+    diffuse.g = static_cast<ai_real>(strtol(g, nullptr, 16)) / ai_real(255.0);
+
+    char b[3] = { color[5], color[6], '\0' };
+    diffuse.b = static_cast<ai_real>(strtol(b, nullptr, 16)) / ai_real(255.0);
+    const size_t len = strlen(color);
+    if (ColRGB_Len == len) {
+        return true;
+    }
+
+    char a[3] = { color[7], color[8], '\0' };
+    diffuse.a = static_cast<ai_real>(strtol(a, nullptr, 16)) / ai_real(255.0);
+
+    return true;
+}
+
+void assignDiffuseColor(XmlNode &node, aiMaterial *mat) {
+    const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string();
+    aiColor4D diffuse;
+    if (parseColor(color, diffuse)) {
+        mat->AddProperty<aiColor4D>(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+    }
+}
+
+} // namespace
+
+XmlSerializer::XmlSerializer(XmlParser *xmlParser) :
+        mResourcesDictionnary(),
+        mMeshCount(0),
+        mXmlParser(xmlParser) {
+    ai_assert(nullptr != xmlParser);
+}
+
+XmlSerializer::~XmlSerializer() {
+    for (auto &it : mResourcesDictionnary) {
+        delete it.second;
+    }
+}
+
+void XmlSerializer::ImportXml(aiScene *scene) {
+    if (nullptr == scene) {
+        return;
+    }
+    
+    scene->mRootNode = new aiNode(XmlTag::RootTag);
+    XmlNode node = mXmlParser->getRootNode().child(XmlTag::model);
+    if (node.empty()) {
+        return;
+    }
+
+    XmlNode resNode = node.child(XmlTag::resources);
+    for (auto &currentNode : resNode.children()) {
+        const std::string currentNodeName = currentNode.name();
+        if (currentNodeName == XmlTag::texture_2d) {
+            ReadEmbeddecTexture(currentNode);
+        } else if (currentNodeName == XmlTag::texture_group) {
+            ReadTextureGroup(currentNode);
+        } else if (currentNodeName == XmlTag::object) {
+            ReadObject(currentNode);
+        } else if (currentNodeName == XmlTag::basematerials) {
+            ReadBaseMaterials(currentNode);
+        } else if (currentNodeName == XmlTag::meta) {
+            ReadMetadata(currentNode);
+        }
+    }
+    StoreMaterialsInScene(scene);
+    XmlNode buildNode = node.child(XmlTag::build);
+    if (buildNode.empty()) {
+        return;
+    }
+
+    for (auto &currentNode : buildNode.children()) {
+        const std::string currentNodeName = currentNode.name();
+        if (currentNodeName == XmlTag::item) {
+            int objectId = IdNotSet;
+            std::string transformationMatrixStr;
+            aiMatrix4x4 transformationMatrix;
+            getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId);
+            bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr);
+
+            auto it = mResourcesDictionnary.find(objectId);
+            if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
+                Object *obj = static_cast<Object *>(it->second);
+                if (hasTransform) {
+                    transformationMatrix = parseTransformMatrix(transformationMatrixStr);
+                }
+
+                addObjectToNode(scene->mRootNode, obj, transformationMatrix);
+            }
+        }
+    }
+
+    // import the metadata
+    if (!mMetaData.empty()) {
+        const size_t numMeta = mMetaData.size();
+        scene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta));
+        for (size_t i = 0; i < numMeta; ++i) {
+            aiString val(mMetaData[i].value);
+            scene->mMetaData->Set(static_cast<unsigned int>(i), mMetaData[i].name, val);
+        }
+    }
+
+    // import the meshes, materials are already stored
+    scene->mNumMeshes = static_cast<unsigned int>(mMeshCount);
+    if (scene->mNumMeshes != 0) {
+        scene->mMeshes = new aiMesh *[scene->mNumMeshes]();
+        for (auto &it : mResourcesDictionnary) {
+            if (it.second->getType() == ResourceType::RT_Object) {
+                Object *obj = static_cast<Object *>(it.second);
+                ai_assert(nullptr != obj);
+                for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) {
+                    scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i];
+                }
+            }
+        }
+    }
+}
+
+void XmlSerializer::addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) {
+    ai_assert(nullptr != obj);
+
+    aiNode *sceneNode = new aiNode(obj->mName);
+    sceneNode->mNumMeshes = static_cast<unsigned int>(obj->mMeshes.size());
+    sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes];
+    std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes);
+
+    sceneNode->mTransformation = nodeTransform;
+    if (nullptr != parent) {
+        parent->addChildren(1, &sceneNode);
+    }
+
+    for (Assimp::D3MF::Component c : obj->mComponents) {
+        auto it = mResourcesDictionnary.find(c.mObjectId);
+        if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) {
+            addObjectToNode(sceneNode, static_cast<Object *>(it->second), c.mTransformation);
+        }
+    }
+}
+
+void XmlSerializer::ReadObject(XmlNode &node) {
+    int id = IdNotSet, pid = IdNotSet, pindex = IdNotSet;
+    bool hasId = getNodeAttribute(node, XmlTag::id, id);
+    if (!hasId) {
+        return;
+    }
+
+    bool hasPid = getNodeAttribute(node, XmlTag::pid, pid);
+    bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex);
+
+    Object *obj = new Object(id);
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == D3MF::XmlTag::mesh) {
+            auto mesh = ReadMesh(currentNode);
+            mesh->mName.Set(ai_to_string(id));
+
+            if (hasPid) {
+                auto it = mResourcesDictionnary.find(pid);
+                if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) {
+                    BaseMaterials *materials = static_cast<BaseMaterials *>(it->second);
+                    mesh->mMaterialIndex = materials->mMaterialIndex[pindex];
+                }
+            }
+
+            obj->mMeshes.push_back(mesh);
+            obj->mMeshIndex.push_back(mMeshCount);
+            mMeshCount++;
+        } else if (currentName == D3MF::XmlTag::components) {
+            for (XmlNode &currentSubNode : currentNode.children()) {
+                const std::string subNodeName = currentSubNode.name();
+                if (subNodeName == D3MF::XmlTag::component) {
+                    int objectId = IdNotSet;
+                    std::string componentTransformStr;
+                    aiMatrix4x4 componentTransform;
+                    if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) {
+                        componentTransform = parseTransformMatrix(componentTransformStr);
+                    }
+
+                    if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) {
+                        obj->mComponents.push_back({ objectId, componentTransform });
+                    }
+                }
+            }
+        }
+    }
+
+    mResourcesDictionnary.insert(std::make_pair(id, obj));
+}
+
+aiMesh *XmlSerializer::ReadMesh(XmlNode &node) {
+    if (node.empty()) {
+        return nullptr;
+    }
+
+    aiMesh *mesh = new aiMesh();
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == XmlTag::vertices) {
+            ImportVertices(currentNode, mesh);
+        } else if (currentName == XmlTag::triangles) {
+            ImportTriangles(currentNode, mesh);
+        }
+    }
+
+    return mesh;
+}
+
+void XmlSerializer::ReadMetadata(XmlNode &node) {
+    pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name);
+    const std::string name = attribute.as_string();
+    const std::string value = node.value();
+    if (name.empty()) {
+        return;
+    }
+
+    MetaEntry entry;
+    entry.name = name;
+    entry.value = value;
+    mMetaData.push_back(entry);
+}
+
+void XmlSerializer::ImportVertices(XmlNode &node, aiMesh *mesh) {
+    ai_assert(nullptr != mesh);
+
+    std::vector<aiVector3D> vertices;
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == XmlTag::vertex) {
+            vertices.push_back(ReadVertex(currentNode));
+        }
+    }
+
+    mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
+    mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+    std::copy(vertices.begin(), vertices.end(), mesh->mVertices);
+}
+
+void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) {
+    std::vector<aiFace> faces;
+    for (XmlNode &currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        if (currentName == XmlTag::triangle) {
+            int pid = IdNotSet, p1 = IdNotSet;
+            bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid);
+            bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1);
+
+            if (hasPid && hasP1) {
+                auto it = mResourcesDictionnary.find(pid);
+                if (it != mResourcesDictionnary.end()) {
+                    if (it->second->getType() == ResourceType::RT_BaseMaterials) {
+                        BaseMaterials *baseMaterials = static_cast<BaseMaterials *>(it->second);
+                        mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1];
+                    } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) {
+                        if (mesh->mTextureCoords[0] == nullptr) {
+                            Texture2DGroup *group = static_cast<Texture2DGroup *>(it->second);
+                            const std::string name = ai_to_string(group->mTexId);
+                            for (size_t i = 0; i < mMaterials.size(); ++i) {
+                                if (name == mMaterials[i]->GetName().C_Str()) {
+                                    mesh->mMaterialIndex = static_cast<unsigned int>(i);
+                                }
+                            }
+                            mesh->mTextureCoords[0] = new aiVector3D[group->mTex2dCoords.size()];
+                            for (unsigned int i = 0; i < group->mTex2dCoords.size(); ++i) {
+                                mesh->mTextureCoords[0][i] = aiVector3D(group->mTex2dCoords[i].x, group->mTex2dCoords[i].y, 0);
+                            }
+                        }
+                    } 
+                }
+            }
+
+            aiFace face = ReadTriangle(currentNode);
+            faces.push_back(face);
+        }
+    }
+
+    mesh->mNumFaces = static_cast<unsigned int>(faces.size());
+    mesh->mFaces = new aiFace[mesh->mNumFaces];
+    mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+    std::copy(faces.begin(), faces.end(), mesh->mFaces);
+}
+
+void XmlSerializer::ReadBaseMaterials(XmlNode &node) {
+    int id = IdNotSet;
+    if (getNodeAttribute(node, D3MF::XmlTag::id, id)) {
+        BaseMaterials *baseMaterials = new BaseMaterials(id);
+
+        for (XmlNode &currentNode : node.children()) {
+            const std::string currentName = currentNode.name();
+            if (currentName == XmlTag::basematerials_base) {
+                baseMaterials->mMaterialIndex.push_back(static_cast<unsigned int>(mMaterials.size()));
+                mMaterials.push_back(readMaterialDef(currentNode, id));
+            }
+        }
+
+        mResourcesDictionnary.insert(std::make_pair(id, baseMaterials));
+    }
+}
+
+void XmlSerializer::ReadEmbeddecTexture(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    std::string value;
+    EmbeddedTexture *tex2D = nullptr;
+    if (XmlParser::getStdStrAttribute(node, XmlTag::id, value)) {
+        tex2D = new EmbeddedTexture(atoi(value.c_str()));
+    }
+    if (nullptr == tex2D) {
+        return;
+    }
+
+    if (XmlParser::getStdStrAttribute(node, XmlTag::path, value)) {
+        tex2D->mPath = value;
+    }
+    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_content_type, value)) {
+        tex2D->mContentType = value;
+    }
+    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestyleu, value)) {
+        tex2D->mTilestyleU = value;
+    }
+    if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestylev, value)) {
+        tex2D->mTilestyleV = value;
+    }
+    mEmbeddedTextures.emplace_back(tex2D);
+    StoreEmbeddedTexture(tex2D);
+}
+
+void XmlSerializer::StoreEmbeddedTexture(EmbeddedTexture *tex) {
+    aiMaterial *mat = new aiMaterial;
+    aiString s;
+    s.Set(ai_to_string(tex->mId).c_str());
+    mat->AddProperty(&s, AI_MATKEY_NAME);
+    const std::string name = "*" + tex->mPath;
+    s.Set(name);
+    mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+    aiColor3D col;
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_DIFFUSE);
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_AMBIENT);
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_EMISSIVE);
+    mat->AddProperty<aiColor3D>(&col, 1, AI_MATKEY_COLOR_SPECULAR);
+    mMaterials.emplace_back(mat);
+}
+
+void XmlSerializer::ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup) {
+    if (node.empty() || nullptr == tex2DGroup) {
+        return;
+    }
+
+    int id = IdNotSet;
+    if (XmlParser::getIntAttribute(node, "texid", id)) {
+        tex2DGroup->mTexId = id;
+    }
+
+    double value = 0.0;
+    for (XmlNode currentNode : node.children()) {
+        const std::string currentName = currentNode.name();
+        aiVector2D texCoord;
+        if (currentName == XmlTag::texture_2d_coord) {
+            XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_u, value);
+            texCoord.x = (ai_real)value;
+            XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_v, value);
+            texCoord.y = (ai_real)value;
+            tex2DGroup->mTex2dCoords.push_back(texCoord);
+        }
+    }
+}
+
+void XmlSerializer::ReadTextureGroup(XmlNode &node) {
+    if (node.empty()) {
+        return;
+    }
+
+    int id = IdNotSet;
+    if (!XmlParser::getIntAttribute(node, XmlTag::id, id)) {
+        return;
+    }
+
+    Texture2DGroup *group = new Texture2DGroup(id);
+    ReadTextureCoords2D(node, group);
+    mResourcesDictionnary.insert(std::make_pair(id, group));
+}
+
+aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basematerialsId) {
+    aiMaterial *material = new aiMaterial();
+    material->mNumProperties = 0;
+    std::string name;
+    bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name);
+
+    std::string stdMaterialName;
+    const std::string strId(ai_to_string(basematerialsId));
+    stdMaterialName += "id";
+    stdMaterialName += strId;
+    stdMaterialName += "_";
+    if (hasName) {
+        stdMaterialName += std::string(name);
+    } else {
+        stdMaterialName += "basemat_";
+        stdMaterialName += ai_to_string(mMaterials.size());
+    }
+
+    aiString assimpMaterialName(stdMaterialName);
+    material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME);
+
+    assignDiffuseColor(node, material);
+
+    return material;
+}
+
+void XmlSerializer::StoreMaterialsInScene(aiScene *scene) {
+    if (nullptr == scene || mMaterials.empty()) {
+        return;
+    }
+
+    scene->mNumMaterials = static_cast<unsigned int>(mMaterials.size());
+    scene->mMaterials = new aiMaterial *[scene->mNumMaterials];
+    for (size_t i = 0; i < mMaterials.size(); ++i) {
+        scene->mMaterials[i] = mMaterials[i];
+    }
+}
+
+} // namespace D3MF
+} // namespace Assimp

+ 96 - 0
code/AssetLib/3MF/XmlSerializer.h

@@ -0,0 +1,96 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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.
+
+----------------------------------------------------------------------
+*/
+#pragma once
+
+#include <assimp/XmlParser.h>
+#include <assimp/mesh.h>
+#include <vector>
+#include <map>
+
+struct aiNode;
+struct aiMesh;
+struct aiMaterial;
+
+namespace Assimp {
+namespace D3MF {
+
+class Resource;
+class D3MFOpcPackage;
+class Object;
+class Texture2DGroup;
+class EmbeddedTexture;
+
+class XmlSerializer {
+public:
+    XmlSerializer(XmlParser *xmlParser);
+    ~XmlSerializer();
+    void ImportXml(aiScene *scene);
+
+private:
+    void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform);
+    void ReadObject(XmlNode &node);
+    aiMesh *ReadMesh(XmlNode &node);
+    void ReadMetadata(XmlNode &node);
+    void ImportVertices(XmlNode &node, aiMesh *mesh);
+    void ImportTriangles(XmlNode &node, aiMesh *mesh);
+    void ReadBaseMaterials(XmlNode &node);
+    void ReadEmbeddecTexture(XmlNode &node);
+    void StoreEmbeddedTexture(EmbeddedTexture *tex);
+    void ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup);
+    void ReadTextureGroup(XmlNode &node);
+    aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId);
+    void StoreMaterialsInScene(aiScene *scene);
+
+private:
+    struct MetaEntry {
+        std::string name;
+        std::string value;
+    };
+    std::vector<MetaEntry> mMetaData;
+    std::vector<EmbeddedTexture *> mEmbeddedTextures;
+    std::vector<aiMaterial *> mMaterials;
+    std::map<unsigned int, Resource *> mResourcesDictionnary;
+    unsigned int mMeshCount;
+    XmlParser *mXmlParser;
+};
+
+} // namespace D3MF
+} // namespace Assimp

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

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -152,18 +152,9 @@ AC3DImporter::~AC3DImporter() {
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
-    std::string extension = GetExtension(pFile);
-
-    // fixme: are acc and ac3d *really* used? Some sources say they are
-    if (extension == "ac" || extension == "ac3d" || extension == "acc") {
-        return true;
-    }
-    if (!extension.length() || checkSig) {
-        uint32_t token = AI_MAKE_MAGIC("AC3D");
-        return CheckMagicToken(pIOHandler, pFile, &token, 1, 0);
-    }
-    return false;
+bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+    static const uint32_t tokens[] = { AI_MAKE_MAGIC("AC3D") };
+    return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
 }
 
 // ------------------------------------------------------------------------------------------------

+ 6 - 6
code/AssetLib/AC/ACLoader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -63,7 +63,7 @@ namespace Assimp {
 class AC3DImporter : public BaseImporter {
 public:
     AC3DImporter();
-    ~AC3DImporter();
+    ~AC3DImporter() override;
 
     // Represents an AC3D material
     struct Material {
@@ -185,25 +185,25 @@ public:
      * See BaseImporter::CanRead() for details.
      */
     bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
-            bool checkSig) const;
+            bool checkSig) const override;
 
 protected:
     // -------------------------------------------------------------------
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details */
-    const aiImporterDesc *GetInfo() const;
+    const aiImporterDesc *GetInfo() const override;
 
     // -------------------------------------------------------------------
     /** Imports the given file into the given scene structure.
      * See BaseImporter::InternReadFile() for details*/
     void InternReadFile(const std::string &pFile, aiScene *pScene,
-            IOSystem *pIOHandler);
+            IOSystem *pIOHandler) override;
 
     // -------------------------------------------------------------------
     /** Called prior to ReadFile().
     * The function is a request to the importer to update its configuration
     * basing on the Importer's configuration property list.*/
-    void SetupProperties(const Importer *pImp);
+    void SetupProperties(const Importer *pImp) override;
 
 private:
     // -------------------------------------------------------------------

+ 4 - 14
code/AssetLib/AMF/AMFImporter.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -503,19 +503,9 @@ void AMFImporter::ParseNode_Metadata(XmlNode &node) {
     mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
 }
 
-bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const {
-    const std::string extension = GetExtension(pFile);
-
-    if (extension == "amf") {
-        return true;
-    }
-
-    if (extension.empty() || pCheckSig) {
-        const char *tokens[] = { "<amf" };
-        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
-    }
-
-    return false;
+bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*pCheckSig*/) const {
+    static const char *tokens[] = { "<amf" };
+    return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
 }
 
 const aiImporterDesc *AMFImporter::GetInfo() const {

+ 5 - 5
code/AssetLib/AMF/AMFImporter.hpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -267,7 +267,7 @@ public:
     AMFImporter() AI_NO_EXCEPT;
 
     /// Default destructor.
-    ~AMFImporter();
+    ~AMFImporter() override;
 
     /// Parse AMF file and fill scene graph. The function has no return value. Result can be found by analyzing the generated graph.
     /// Also exception can be thrown if trouble will found.
@@ -276,9 +276,9 @@ public:
     void ParseFile(const std::string &pFile, IOSystem *pIOHandler);
     void ParseHelper_Node_Enter(AMFNodeElementBase *child);
     void ParseHelper_Node_Exit();
-    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const;
-    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
-    const aiImporterDesc *GetInfo() const;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const override;
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
+    const aiImporterDesc *GetInfo() const override;
     bool Find_NodeElement(const std::string &pID, const AMFNodeElementBase::EType pType, AMFNodeElementBase **pNodeElement) const;
     bool Find_ConvertedNode(const std::string &pID, NodeArray &nodeArray, aiNode **pNode) const;
     bool Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const;

+ 1 - 1
code/AssetLib/AMF/AMFImporter_Geometry.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

+ 1 - 1
code/AssetLib/AMF/AMFImporter_Material.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

+ 5 - 6
code/AssetLib/AMF/AMFImporter_Node.hpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -48,15 +48,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef INCLUDED_AI_AMF_IMPORTER_NODE_H
 #define INCLUDED_AI_AMF_IMPORTER_NODE_H
 
-// Header files, stdlib.
+// Header files, Assimp.
+#include <assimp/scene.h>
+#include <assimp/types.h>
+
 #include <list>
 #include <string>
 #include <vector>
 
-// Header files, Assimp.
-#include "assimp/scene.h"
-#include "assimp/types.h"
-
 /// \class CAMFImporter_NodeElement
 /// Base class for elements of nodes.
 class AMFNodeElementBase {

+ 2 - 2
code/AssetLib/AMF/AMFImporter_Postprocess.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -690,7 +690,7 @@ void AMFImporter::Postprocess_BuildConstellation(AMFConstellation &pConstellatio
         if (ne->Type == AMFNodeElementBase::ENET_Metadata) continue;
         if (ne->Type != AMFNodeElementBase::ENET_Instance) throw DeadlyImportError("Only <instance> nodes can be in <constellation>.");
 
-        // create alias for conveniance
+        // create alias for convenience
         AMFInstance &als = *((AMFInstance *)ne);
         // find referenced object
         if (!Find_ConvertedNode(als.ObjectID, nodeArray, &found_node)) Throw_ID_NotFound(als.ObjectID);

+ 5 - 17
code/AssetLib/ASE/ASELoader.cpp

@@ -3,9 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
-
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -97,19 +95,9 @@ ASEImporter::~ASEImporter() {
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool ASEImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const {
-    // check file extension
-    const std::string extension = GetExtension(pFile);
-
-    if (extension == "ase" || extension == "ask") {
-        return true;
-    }
-
-    if ((!extension.length() || cs) && pIOHandler) {
-        const char *tokens[] = { "*3dsmax_asciiexport" };
-        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
-    }
-    return false;
+bool ASEImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+    static const char *tokens[] = { "*3dsmax_asciiexport" };
+    return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -683,7 +671,7 @@ void ASEImporter::BuildNodes(std::vector<BaseNode *> &nodes) {
         }
     }
 
-    // Are there ane orphaned nodes?
+    // Are there any orphaned nodes?
     if (!aiList.empty()) {
         std::vector<aiNode *> apcNodes;
         apcNodes.reserve(aiList.size() + pcScene->mRootNode->mNumChildren);

+ 6 - 21
code/AssetLib/ASE/ASELoader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -62,42 +62,37 @@ namespace Assimp {
 class ASEImporter : public BaseImporter {
 public:
     ASEImporter();
-    ~ASEImporter();
+    ~ASEImporter() override;
 
     // -------------------------------------------------------------------
     /** Returns whether the class can handle the format of the given file.
      * See BaseImporter::CanRead() for details.
      */
     bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
-        bool checkSig) const;
+        bool checkSig) const override;
 
 protected:
-
     // -------------------------------------------------------------------
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details
      */
-    const aiImporterDesc* GetInfo () const;
-
+    const aiImporterDesc* GetInfo () const override;
 
     // -------------------------------------------------------------------
     /** Imports the given file into the given scene structure.
     * See BaseImporter::InternReadFile() for details
     */
     void InternReadFile( const std::string& pFile, aiScene* pScene,
-        IOSystem* pIOHandler);
-
+        IOSystem* pIOHandler) override;
 
     // -------------------------------------------------------------------
     /** Called prior to ReadFile().
     * The function is a request to the importer to update its configuration
     * basing on the Importer's configuration property list.
     */
-    void SetupProperties(const Importer* pImp);
-
+    void SetupProperties(const Importer* pImp) override;
 
 private:
-
     // -------------------------------------------------------------------
     /** Generate normal vectors basing on smoothing groups
      * (in some cases the normal are already contained in the file)
@@ -106,7 +101,6 @@ private:
      */
     bool GenerateNormals(ASE::Mesh& mesh);
 
-
     // -------------------------------------------------------------------
     /** Create valid vertex/normal/UV/color/face lists.
      *  All elements are unique, faces have only one set of indices
@@ -115,51 +109,43 @@ private:
      */
     void BuildUniqueRepresentation(ASE::Mesh& mesh);
 
-
     /** Create one-material-per-mesh meshes ;-)
      * \param mesh Mesh to work with
      *  \param Receives the list of all created meshes
      */
     void ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOut);
 
-
     // -------------------------------------------------------------------
     /** Convert a material to a aiMaterial object
      * \param mat Input material
      */
     void ConvertMaterial(ASE::Material& mat);
 
-
     // -------------------------------------------------------------------
     /** Setup the final material indices for each mesh
      */
     void BuildMaterialIndices();
 
-
     // -------------------------------------------------------------------
     /** Build the node graph
      */
     void BuildNodes(std::vector<ASE::BaseNode*>& nodes);
 
-
     // -------------------------------------------------------------------
     /** Build output cameras
      */
     void BuildCameras();
 
-
     // -------------------------------------------------------------------
     /** Build output lights
      */
     void BuildLights();
 
-
     // -------------------------------------------------------------------
     /** Build output animations
      */
     void BuildAnimations(const std::vector<ASE::BaseNode*>& nodes);
 
-
     // -------------------------------------------------------------------
     /** Add sub nodes to a node
      *  \param pcParent parent node to be filled
@@ -183,7 +169,6 @@ private:
     void GenerateDefaultMaterial();
 
 protected:
-
     /** Parser instance */
     ASE::Parser* mParser;
 

+ 8 - 6
code/AssetLib/ASE/ASEParser.cpp

@@ -3,9 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
-
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -114,6 +112,7 @@ using namespace Assimp::ASE;
 // ------------------------------------------------------------------------------------------------
 Parser::Parser(const char *szFile, unsigned int fileFormatDefault) {
     ai_assert(nullptr != szFile);
+
     filePtr = szFile;
     iFileFormat = fileFormatDefault;
 
@@ -490,6 +489,7 @@ void Parser::ParseLV1MaterialListBlock() {
                 if (iIndex >= iMaterialCount) {
                     LogWarning("Out of range: material index is too large");
                     iIndex = iMaterialCount - 1;
+                    return;
                 }
 
                 // get a reference to the material
@@ -906,7 +906,6 @@ void Parser::ParseLV2LightSettingsBlock(ASE::Light &light) {
         }
         AI_ASE_HANDLE_SECTION("2", "LIGHT_SETTINGS");
     }
-    return;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -1783,7 +1782,9 @@ void Parser::ParseLV4MeshFace(ASE::Face &out) {
 
     // *MESH_MTLID  is optional, too
     while (true) {
-        if ('*' == *filePtr) break;
+        if ('*' == *filePtr) {
+            break;
+        }
         if (IsLineEnd(*filePtr)) {
             return;
         }
@@ -1832,8 +1833,9 @@ void Parser::ParseLV4MeshFloatTriple(ai_real *apOut, unsigned int &rIndexOut) {
 void Parser::ParseLV4MeshFloatTriple(ai_real *apOut) {
     ai_assert(nullptr != apOut);
 
-    for (unsigned int i = 0; i < 3; ++i)
+    for (unsigned int i = 0; i < 3; ++i) {
         ParseLV4MeshFloat(apOut[i]);
+    }
 }
 // ------------------------------------------------------------------------------------------------
 void Parser::ParseLV4MeshFloat(ai_real &fOut) {

+ 1 - 1
code/AssetLib/ASE/ASEParser.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 1 - 1
code/AssetLib/Assbin/AssbinExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

+ 5 - 3
code/AssetLib/Assbin/AssbinExporter.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,16 +42,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file AssbinExporter.h
  * ASSBIN Exporter Main Header
  */
+#pragma once
 #ifndef AI_ASSBINEXPORTER_H_INC
 #define AI_ASSBINEXPORTER_H_INC
 
 #include <assimp/defs.h>
 
+#ifndef ASSIMP_BUILD_NO_EXPORT
+
 // nothing really needed here - reserved for future use like properties
 namespace Assimp {
 
 void ASSIMP_API ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/);
 
 }
-
+#endif 
 #endif // AI_ASSBINEXPORTER_H_INC

+ 15 - 14
code/AssetLib/Assbin/AssbinFileWriter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,13 +43,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "AssbinFileWriter.h"
-
 #include "Common/assbin_chunks.h"
 #include "PostProcessing/ProcessHelper.h"
 
 #include <assimp/Exceptional.h>
 #include <assimp/version.h>
-#include <assimp/Exporter.hpp>
 #include <assimp/IOStream.hpp>
 
 #ifdef ASSIMP_BUILD_NO_OWN_ZLIB
@@ -58,7 +56,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "../contrib/zlib/zlib.h"
 #endif
 
-#include <time.h>
+#include <ctime>
 
 #if _MSC_VER
 #pragma warning(push)
@@ -184,6 +182,8 @@ inline size_t Write<aiVertexWeight>(IOStream *stream, const aiVertexWeight &v) {
     return t + Write<float>(stream, v.mWeight);
 }
 
+constexpr size_t MatrixSize = 64;
+
 // -----------------------------------------------------------------------------------
 // Serialize a mat4x4
 template <>
@@ -194,7 +194,7 @@ inline size_t Write<aiMatrix4x4>(IOStream *stream, const aiMatrix4x4 &m) {
         }
     }
 
-    return 64;
+    return MatrixSize;
 }
 
 // -----------------------------------------------------------------------------------
@@ -277,7 +277,7 @@ public:
         // empty
     }
 
-    virtual ~AssbinChunkWriter() {
+    ~AssbinChunkWriter() override {
         if (container) {
             container->Write(&magic, sizeof(uint32_t), 1);
             container->Write(&cursor, sizeof(uint32_t), 1);
@@ -288,26 +288,27 @@ public:
 
     void *GetBufferPointer() { return buffer; }
 
-    // -------------------------------------------------------------------
-    virtual size_t Read(void * /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) {
+    size_t Read(void * /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) override {
         return 0;
     }
-    virtual aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) {
+    
+    aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) override {
         return aiReturn_FAILURE;
     }
-    virtual size_t Tell() const {
+    
+    size_t Tell() const override {
         return cursor;
     }
-    virtual void Flush() {
+    
+    void Flush() override {
         // not implemented
     }
 
-    virtual size_t FileSize() const {
+    size_t FileSize() const override {
         return cursor;
     }
 
-    // -------------------------------------------------------------------
-    virtual size_t Write(const void *pvBuffer, size_t pSize, size_t pCount) {
+    size_t Write(const void *pvBuffer, size_t pSize, size_t pCount) override {
         pSize *= pCount;
         if (cursor + pSize > cur_size) {
             Grow(cursor + pSize);

+ 1 - 1
code/AssetLib/Assbin/AssbinFileWriter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

+ 2 - 2
code/AssetLib/Assbin/AssbinLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -406,7 +406,7 @@ void AssbinImporter::ReadBinaryMesh(IOStream *stream, aiMesh *mesh) {
             f.mIndices = new unsigned int[f.mNumIndices];
 
             for (unsigned int a = 0; a < f.mNumIndices; ++a) {
-                // Check if unsigned  short ( 16 bit  ) are big enought for the indices
+                // Check if unsigned  short ( 16 bit  ) are big enough for the indices
                 if (fitsIntoUI16(mesh->mNumVertices)) {
                     f.mIndices[a] = Read<uint16_t>(stream);
                 } else {

+ 6 - 13
code/AssetLib/Assbin/AssbinLoader.h

@@ -3,8 +3,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -74,17 +73,11 @@ private:
     bool compressed;
 
 public:
-    virtual bool CanRead(
-        const std::string& pFile,
-        IOSystem* pIOHandler,
-        bool checkSig
-    ) const;
-    virtual const aiImporterDesc* GetInfo() const;
-    virtual void InternReadFile(
-    const std::string& pFile,
-        aiScene* pScene,
-        IOSystem* pIOHandler
-    );
+    bool CanRead(const std::string& pFile,
+        IOSystem* pIOHandler, bool checkSig) const override;
+    const aiImporterDesc* GetInfo() const override;
+    void InternReadFile(
+    const std::string& pFile,aiScene* pScene,IOSystem* pIOHandler) override;
     void ReadHeader();
     void ReadBinaryScene( IOStream * stream, aiScene* pScene );
     void ReadBinaryNode( IOStream * stream, aiNode** mRootNode, aiNode* parent );

+ 27 - 15
code/AssetLib/Assjson/json_exporter.cpp

@@ -41,12 +41,17 @@ public:
     enum {
         Flag_DoNotIndent = 0x1,
         Flag_WriteSpecialFloats = 0x2,
+        Flag_SkipWhitespaces = 0x4
     };
-
+    
     JSONWriter(Assimp::IOStream &out, unsigned int flags = 0u) :
-            out(out), first(), flags(flags) {
+            out(out), indent (""), newline("\n"), space(" "), buff (), first(false), flags(flags) {
         // make sure that all formatting happens using the standard, C locale and not the user's current locale
         buff.imbue(std::locale("C"));
+        if (flags & Flag_SkipWhitespaces) {
+            newline = "";
+            space = "";
+        }
     }
 
     ~JSONWriter() {
@@ -70,7 +75,7 @@ public:
     void Key(const std::string &name) {
         AddIndentation();
         Delimit();
-        buff << '\"' + name + "\": ";
+        buff << '\"' + name + "\":" << space;
     }
 
     template <typename Literal>
@@ -78,12 +83,12 @@ public:
         AddIndentation();
         Delimit();
 
-        LiteralToString(buff, name) << '\n';
+        LiteralToString(buff, name) << newline;
     }
 
     template <typename Literal>
     void SimpleValue(const Literal &s) {
-        LiteralToString(buff, s) << '\n';
+        LiteralToString(buff, s) << newline;
     }
 
     void SimpleValue(const void *buffer, size_t len) {
@@ -102,7 +107,7 @@ public:
             }
         }
 
-        buff << '\"' << cur_out << "\"\n";
+        buff << '\"' << cur_out << "\"" << newline;
         delete[] cur_out;
     }
 
@@ -115,7 +120,7 @@ public:
             }
         }
         first = true;
-        buff << "{\n";
+        buff << "{" << newline;
         PushIndent();
     }
 
@@ -123,7 +128,7 @@ public:
         PopIndent();
         AddIndentation();
         first = false;
-        buff << "}\n";
+        buff << "}" << newline;
     }
 
     void StartArray(bool is_element = false) {
@@ -135,19 +140,19 @@ public:
             }
         }
         first = true;
-        buff << "[\n";
+        buff << "[" << newline;
         PushIndent();
     }
 
     void EndArray() {
         PopIndent();
         AddIndentation();
-        buff << "]\n";
+        buff << "]" << newline;
         first = false;
     }
 
     void AddIndentation() {
-        if (!(flags & Flag_DoNotIndent)) {
+        if (!(flags & Flag_DoNotIndent) && !(flags & Flag_SkipWhitespaces)) {
             buff << indent;
         }
     }
@@ -156,7 +161,7 @@ public:
         if (!first) {
             buff << ',';
         } else {
-            buff << ' ';
+            buff << space;
             first = false;
         }
     }
@@ -227,7 +232,9 @@ private:
 
 private:
     Assimp::IOStream &out;
-    std::string indent, newline;
+    std::string indent;
+    std::string newline;
+    std::string space;
     std::stringstream buff;
     bool first;
 
@@ -765,7 +772,7 @@ void Write(JSONWriter &out, const aiScene &ai) {
     out.EndObj();
 }
 
-void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *) {
+void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *pProperties) {
     std::unique_ptr<Assimp::IOStream> str(io->Open(file, "wt"));
     if (!str) {
         throw DeadlyExportError("could not open output file");
@@ -782,7 +789,12 @@ void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *sc
         splitter.Execute(scenecopy_tmp);
 
         // XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters
-        JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats);
+
+        unsigned int flags = JSONWriter::Flag_WriteSpecialFloats;
+        if (pProperties->GetPropertyBool("JSON_SKIP_WHITESPACES", false)) {
+            flags |= JSONWriter::Flag_SkipWhitespaces;
+        }
+        JSONWriter s(*str, flags);
         Write(s, *scenecopy_tmp);
 
     } catch (...) {

+ 6 - 7
code/AssetLib/Assjson/mesh_splitter.cpp

@@ -69,13 +69,12 @@ void MeshSplitter::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh
 	for (unsigned int i = 0, end = pcNode->mNumChildren; i < end;++i)	{
 		UpdateNode ( pcNode->mChildren[i], source_mesh_map );
 	}
-	return;
 }
 
-#define WAS_NOT_COPIED 0xffffffff
+static const unsigned int WAS_NOT_COPIED = 0xffffffff;
 
-typedef std::pair <unsigned int,float> PerVertexWeight;
-typedef std::vector	<PerVertexWeight> VertexWeightTable;
+using PerVertexWeight = std::pair <unsigned int,float>;
+using VertexWeightTable = std::vector	<PerVertexWeight>;
 
 // ------------------------------------------------------------------------------------------------
 VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) {
@@ -89,7 +88,7 @@ VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) {
 		aiBone* bone = pMesh->mBones[i];
 		for (unsigned int a = 0; a < bone->mNumWeights;++a)	{
 			const aiVertexWeight& weight = bone->mWeights[a];
-			avPerVertexWeights[weight.mVertexId].push_back( std::make_pair(i,weight.mWeight) );
+			avPerVertexWeights[weight.mVertexId].emplace_back(i,weight.mWeight);
 		}
 	}
 	return avPerVertexWeights;
@@ -100,7 +99,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std:
 	// TODO: should better use std::(multi)set for source_mesh_map.
 
 	if (in_mesh->mNumVertices <= LIMIT)	{
-		source_mesh_map.push_back(std::make_pair(in_mesh,a));
+		source_mesh_map.emplace_back(in_mesh,a);
 		return;
 	}
 
@@ -187,7 +186,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std:
 				break;
 			}
 
-			vFaces.push_back(aiFace());
+			vFaces.emplace_back();
 			aiFace& rFace = vFaces.back();
 
 			// setup face type and number of indices

+ 13 - 22
code/AssetLib/Assjson/mesh_splitter.h

@@ -24,38 +24,29 @@ struct aiNode;
 /** Splits meshes of unique vertices into meshes with no more vertices than
  *  a given, configurable threshold value.
  */
-class MeshSplitter
-{
-
+class MeshSplitter {
 public:
+    unsigned int LIMIT;
 
-	void SetLimit(unsigned int l) {
-		LIMIT = l;
-	}
-
-	unsigned int GetLimit() const {
-		return LIMIT;
-	}
+    void SetLimit(unsigned int l) {
+        LIMIT = l;
+    }
 
-public:
+    unsigned int GetLimit() const {
+        return LIMIT;
+    }
 
-	// -------------------------------------------------------------------
-	/** Executes the post processing step on the given imported data.
+    // -------------------------------------------------------------------
+    /** Executes the post processing step on the given imported data.
 	 * At the moment a process is not supposed to fail.
 	 * @param pScene The imported data to work at.
 	 */
-	void Execute( aiScene* pScene);
-
+    void Execute(aiScene *pScene);
 
 private:
+    void UpdateNode(aiNode *pcNode, const std::vector<std::pair<aiMesh *, unsigned int>> &source_mesh_map);
+    void SplitMesh(unsigned int index, aiMesh *mesh, std::vector<std::pair<aiMesh *, unsigned int>> &source_mesh_map);
 
-	void UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map);
-	void SplitMesh (unsigned int index, aiMesh* mesh, std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map);
-
-public:
-
-	unsigned int LIMIT;
 };
 
 #endif // INCLUDED_MESH_SPLITTER
-

+ 1 - 1
code/AssetLib/Assxml/AssxmlExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 2 - 1
code/AssetLib/Assxml/AssxmlExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file AssxmlExporter.h
  * ASSXML Exporter Main Header
  */
+#pragma once
 #ifndef AI_ASSXMLEXPORTER_H_INC
 #define AI_ASSXMLEXPORTER_H_INC
 

+ 3 - 3
code/AssetLib/Assxml/AssxmlFileWriter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -168,7 +168,7 @@ static void WriteNode(const aiNode *node, IOStream *io, unsigned int depth) {
 }
 
 // -----------------------------------------------------------------------------------
-// Some chuncks of text will need to be encoded for XML
+// Some chunks of text will need to be encoded for XML
 // http://stackoverflow.com/questions/5665231/most-efficient-way-to-escape-xml-html-in-c-string#5665377
 static std::string encodeXML(const std::string &data) {
     std::string buffer;
@@ -601,7 +601,7 @@ static void WriteDump(const char *pFile, const char *cmd, const aiScene *scene,
                 ioprintf(io, "\t\t<TextureCoords num=\"%u\" set=\"%u\" name=\"%s\" num_components=\"%u\"> \n",
                          mesh->mNumVertices,
                          a,
-                         mesh->mTextureCoordsNames[a].C_Str(),
+                         (mesh->HasTextureCoordsName(a) ? mesh->GetTextureCoordsName(a)->C_Str() : ""),
                          mesh->mNumUVComponents[a]);
 
                 if (!shortened) {

+ 2 - 3
code/AssetLib/Assxml/AssxmlFileWriter.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,7 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file AssxmlFileWriter.h
  *  @brief Declaration of Assxml file writer.
  */
-
+#pragma once
 #ifndef AI_ASSXMLFILEWRITER_H_INC
 #define AI_ASSXMLFILEWRITER_H_INC
 

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

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -81,7 +81,7 @@ static const aiImporterDesc desc = {
 
 //#define DEBUG_B3D
 
-template <typename T>
+template<typename T>
 void DeleteAllBarePointers(std::vector<T> &x) {
     for (auto p : x) {
         delete p;
@@ -89,11 +89,11 @@ void DeleteAllBarePointers(std::vector<T> &x) {
 }
 
 B3DImporter::~B3DImporter() {
+    // empty
 }
 
 // ------------------------------------------------------------------------------------------------
 bool B3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
-
     size_t pos = pFile.find_last_of('.');
     if (pos == string::npos) {
         return false;

+ 9 - 11
code/AssetLib/B3D/B3DImporter.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -40,8 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
-/** @file Definition of the .b3d importer class. */
-
+/**
+ *  @file Definition of the .b3d importer class.
+ */
+#pragma once
 #ifndef AI_B3DIMPORTER_H_INC
 #define AI_B3DIMPORTER_H_INC
 
@@ -62,14 +63,12 @@ namespace Assimp{
 class B3DImporter : public BaseImporter{
 public:
     B3DImporter() = default;
-    virtual ~B3DImporter();
-
-    virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+    ~B3DImporter() override;
+    bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override;
 
 protected:
-
-    virtual const aiImporterDesc* GetInfo () const;
-    virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+    const aiImporterDesc* GetInfo () const override;
+    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
 
 private:
 
@@ -113,7 +112,6 @@ private:
     void ReadBB3D( aiScene *scene );
 
     size_t _pos;
-//  unsigned _size;
     std::vector<unsigned char> _buf;
     std::vector<size_t> _stack;
 

+ 5 - 14
code/AssetLib/BVH/BVHLoader.cpp

@@ -4,7 +4,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 
@@ -92,18 +92,9 @@ BVHLoader::~BVHLoader() {}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool BVHLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const {
-    // check file extension
-    const std::string extension = GetExtension(pFile);
-
-    if (extension == "bvh")
-        return true;
-
-    if ((!extension.length() || cs) && pIOHandler) {
-        const char *tokens[] = { "HIERARCHY" };
-        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
-    }
-    return false;
+bool BVHLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+    static const char *tokens[] = { "HIERARCHY" };
+    return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -178,7 +169,7 @@ void BVHLoader::ReadHierarchy(aiScene *pScene) {
 }
 
 // ------------------------------------------------------------------------------------------------
-// Reads a node and recursively its childs and returns the created node;
+// Reads a node and recursively its children and returns the created node;
 aiNode *BVHLoader::ReadNode() {
     // first token is name
     std::string nodeName = GetNextToken();

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

@@ -4,7 +4,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -112,7 +112,7 @@ protected:
     /** Reads the hierarchy */
     void ReadHierarchy(aiScene *pScene);
 
-    /** Reads a node and recursively its childs and returns the created node. */
+    /** Reads a node and recursively its children and returns the created node. */
     aiNode *ReadNode();
 
     /** Reads an end node and returns the created node. */

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,

+ 1 - 1
code/AssetLib/Blender/BlenderBMesh.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 2 - 2
code/AssetLib/Blender/BlenderDNA.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -476,7 +476,7 @@ public:
      *  in BlenderScene.cpp and is machine-generated.
      *  Converters are used to quickly handle objects whose
      *  exact data type is a runtime-property and not yet
-     *  known at compile time (consier Object::data).*/
+     *  known at compile time (consider Object::data).*/
     void RegisterConverters();
 
     // --------------------------------------------------------

+ 1 - 1
code/AssetLib/Blender/BlenderDNA.inl

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

+ 1 - 1
code/AssetLib/Blender/BlenderIntermediate.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 80 - 79
code/AssetLib/Blender/BlenderLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -66,11 +66,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // zlib is needed for compressed blend files
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
-#  ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+#include "Common/Compression.h"
+/* #ifdef ASSIMP_BUILD_NO_OWN_ZLIB
 #    include <zlib.h>
 #  else
 #    include "../contrib/zlib/zlib.h"
-#  endif
+#  endif*/
 #endif
 
 namespace Assimp {
@@ -113,23 +114,15 @@ BlenderImporter::~BlenderImporter() {
     delete modifier_cache;
 }
 
-static const char *Tokens[] = { "BLENDER" };
-static const char *TokensForSearch[] = { "blender" };
+static const char * const Tokens[] = { "BLENDER" };
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
-    const std::string &extension = GetExtension(pFile);
-    if (extension == "blend") {
-        return true;
-    }
-
-    if ((!extension.length() || checkSig) && pIOHandler) {
-        // note: this won't catch compressed files
-        return SearchFileHeaderForToken(pIOHandler, pFile, TokensForSearch, 1);
-    }
+bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+    // note: this won't catch compressed files
+    static const char *tokens[] = { "<BLENDER", "blender" };
 
-    return false;
+    return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -149,7 +142,7 @@ void BlenderImporter::SetupProperties(const Importer * /*pImp*/) {
 void BlenderImporter::InternReadFile(const std::string &pFile,
         aiScene *pScene, IOSystem *pIOHandler) {
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
-    std::vector<Bytef> uncompressed;
+    std::vector<char> uncompressed;
 #endif
 
     FileDatabase file;
@@ -167,7 +160,6 @@ void BlenderImporter::InternReadFile(const std::string &pFile,
 #ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND
         ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?");
 #else
-
         if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) {
             ThrowException("BLENDER magic bytes are missing, couldn't find GZIP header either");
         }
@@ -181,42 +173,12 @@ void BlenderImporter::InternReadFile(const std::string &pFile,
         stream->Seek(0L, aiOrigin_SET);
         std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
 
-        // build a zlib stream
-        z_stream zstream;
-        zstream.opaque = Z_NULL;
-        zstream.zalloc = Z_NULL;
-        zstream.zfree = Z_NULL;
-        zstream.data_type = Z_BINARY;
-
-        // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib
-        inflateInit2(&zstream, 16 + MAX_WBITS);
-
-        zstream.next_in = reinterpret_cast<Bytef *>(reader->GetPtr());
-        zstream.avail_in = (uInt)reader->GetRemainingSize();
-
-        size_t total = 0l;
-
-        // TODO: be smarter about this, decompress directly into heap buffer
-        // and decompress the data .... do 1k chunks in the hope that we won't kill the stack
-#define MYBLOCK 1024
-        Bytef block[MYBLOCK];
-        int ret;
-        do {
-            zstream.avail_out = MYBLOCK;
-            zstream.next_out = block;
-            ret = inflate(&zstream, Z_NO_FLUSH);
-
-            if (ret != Z_STREAM_END && ret != Z_OK) {
-                ThrowException("Failure decompressing this file using gzip, seemingly it is NOT a compressed .BLEND file");
-            }
-            const size_t have = MYBLOCK - zstream.avail_out;
-            total += have;
-            uncompressed.resize(total);
-            memcpy(uncompressed.data() + total - have, block, have);
-        } while (ret != Z_STREAM_END);
-
-        // terminate zlib
-        inflateEnd(&zstream);
+        size_t total = 0;
+        Compression compression;
+        if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, 16 + Compression::MaxWBits)) {
+            total = compression.decompress((unsigned char *)reader->GetPtr(), reader->GetRemainingSize(), uncompressed);
+            compression.close();
+        }
 
         // replace the input stream with a memory stream
         stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t *>(uncompressed.data()), total));
@@ -319,41 +281,80 @@ void BlenderImporter::ExtractScene(Scene &out, const FileDatabase &file) {
 }
 
 // ------------------------------------------------------------------------------------------------
-void BlenderImporter::ConvertBlendFile(aiScene *out, const Scene &in, const FileDatabase &file) {
-    ConversionData conv(file);
+void BlenderImporter::ParseSubCollection(const Blender::Scene &in, aiNode *root, std::shared_ptr<Collection> collection, ConversionData &conv_data) {
 
-    // FIXME it must be possible to take the hierarchy directly from
-    // the file. This is terrible. Here, we're first looking for
-    // all objects which don't have parent objects at all -
-    std::deque<const Object *> no_parents;
-    for (std::shared_ptr<Base> cur = std::static_pointer_cast<Base>(in.base.first); cur; cur = cur->next) {
-        if (cur->object) {
-            if (!cur->object->parent) {
-                no_parents.push_back(cur->object.get());
-            } else {
-                conv.objects.insert(cur->object.get());
-            }
+    std::deque<Object *> root_objects;
+    // Count number of objects
+    for (std::shared_ptr<CollectionObject> cur = std::static_pointer_cast<CollectionObject>(collection->gobject.first); cur; cur = cur->next) {
+        if (cur->ob) {
+            root_objects.push_back(cur->ob);
         }
     }
-    for (std::shared_ptr<Base> cur = in.basact; cur; cur = cur->next) {
-        if (cur->object) {
-            if (cur->object->parent) {
-                conv.objects.insert(cur->object.get());
-            }
+    std::deque<Collection *> root_children;
+    // Count number of child nodes
+    for (std::shared_ptr<CollectionChild> cur = std::static_pointer_cast<CollectionChild>(collection->children.first); cur; cur = cur->next) {
+        if (cur->collection) {
+            root_children.push_back(cur->collection.get());
         }
     }
+    root->mNumChildren = static_cast<unsigned int>(root_objects.size() + root_children.size());
+    root->mChildren = new aiNode *[root->mNumChildren]();
+
+    for (unsigned int i = 0; i < static_cast<unsigned int>(root_objects.size()); ++i) {
+        root->mChildren[i] = ConvertNode(in, root_objects[i], conv_data, aiMatrix4x4());
+        root->mChildren[i]->mParent = root;
+    }
 
-    if (no_parents.empty()) {
-        ThrowException("Expected at least one object with no parent");
+    // For each subcollection create a new node to represent it
+    unsigned int iterator = static_cast<unsigned int>(root_objects.size());
+    for (std::shared_ptr<CollectionChild> cur = std::static_pointer_cast<CollectionChild>(collection->children.first); cur; cur = cur->next) {
+        if (cur->collection) {
+            root->mChildren[iterator] = new aiNode(cur->collection->id.name + 2); // skip over the name prefix 'OB'
+            root->mChildren[iterator]->mParent = root;
+            ParseSubCollection(in, root->mChildren[iterator], cur->collection, conv_data);
+        }
+        iterator += 1;
     }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ConvertBlendFile(aiScene *out, const Scene &in, const FileDatabase &file) {
+    ConversionData conv(file);
 
     aiNode *root = out->mRootNode = new aiNode("<BlenderRoot>");
+    // Iterate over all objects directly under master_collection,
+    // If in.master_collection == null, then we're parsing something older.
+    if (in.master_collection) {
+        ParseSubCollection(in, root, in.master_collection, conv);
+    } else {
+        std::deque<const Object *> no_parents;
+        for (std::shared_ptr<Base> cur = std::static_pointer_cast<Base>(in.base.first); cur; cur = cur->next) {
+            if (cur->object) {
+                if (!cur->object->parent) {
+                    no_parents.push_back(cur->object.get());
+                } else {
+                    conv.objects.insert(cur->object.get());
+                }
+            }
+        }
+        for (std::shared_ptr<Base> cur = in.basact; cur; cur = cur->next) {
+            if (cur->object) {
+                if (cur->object->parent) {
+                    conv.objects.insert(cur->object.get());
+                }
+            }
+        }
 
-    root->mNumChildren = static_cast<unsigned int>(no_parents.size());
-    root->mChildren = new aiNode *[root->mNumChildren]();
-    for (unsigned int i = 0; i < root->mNumChildren; ++i) {
-        root->mChildren[i] = ConvertNode(in, no_parents[i], conv, aiMatrix4x4());
-        root->mChildren[i]->mParent = root;
+        if (no_parents.empty()) {
+            ThrowException("Expected at least one object with no parent");
+        }
+
+        root->mNumChildren = static_cast<unsigned int>(no_parents.size());
+        root->mChildren = new aiNode *[root->mNumChildren]();
+        for (unsigned int i = 0; i < root->mNumChildren; ++i) {
+            root->mChildren[i] = ConvertNode(in, no_parents[i], conv, aiMatrix4x4());
+            root->mChildren[i]->mParent = root;
+        }
     }
 
     BuildMaterials(conv);

+ 97 - 105
code/AssetLib/Blender/BlenderLoader.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file  BlenderLoader.h
  *  @brief Declaration of the Blender 3D (*.blend) importer class.
  */
+#pragma once
 #ifndef INCLUDED_AI_BLEND_LOADER_H
 #define INCLUDED_AI_BLEND_LOADER_H
 
@@ -56,149 +56,141 @@ struct aiLight;
 struct aiCamera;
 struct aiMaterial;
 
-namespace Assimp    {
-
-    // TinyFormatter.h
-    namespace Formatter {
-        template <typename T,typename TR, typename A> class basic_formatter;
-        typedef class basic_formatter< char, std::char_traits<char>, std::allocator<char> > format;
-    }
-
-    // BlenderDNA.h
-    namespace Blender {
-        class  FileDatabase;
-        struct ElemBase;
-    }
-
-    // BlenderScene.h
-    namespace Blender {
-        struct Scene;
-        struct Object;
-        struct Mesh;
-        struct Camera;
-        struct Lamp;
-        struct MTex;
-        struct Image;
-        struct Material;
-    }
-
-    // BlenderIntermediate.h
-    namespace Blender {
-        struct ConversionData;
-        template <template <typename,typename> class TCLASS, typename T> struct TempArray;
-    }
-
-    // BlenderModifier.h
-    namespace Blender {
-        class BlenderModifierShowcase;
-        class BlenderModifier;
-    }
-
-
+namespace Assimp {
+
+// TinyFormatter.h
+namespace Formatter {
+
+template <typename T, typename TR, typename A>
+class basic_formatter;
+
+typedef class basic_formatter<char, std::char_traits<char>, std::allocator<char>> format;
+
+} // namespace Formatter
+
+// BlenderDNA.h
+namespace Blender {
+class FileDatabase;
+struct ElemBase;
+} // namespace Blender
+
+// BlenderScene.h
+namespace Blender {
+struct Scene;
+struct Object;
+struct Collection;
+struct Mesh;
+struct Camera;
+struct Lamp;
+struct MTex;
+struct Image;
+struct Material;
+} // namespace Blender
+
+// BlenderIntermediate.h
+namespace Blender {
+struct ConversionData;
+template <template <typename, typename> class TCLASS, typename T>
+struct TempArray;
+} // namespace Blender
+
+// BlenderModifier.h
+namespace Blender {
+class BlenderModifierShowcase;
+class BlenderModifier;
+} // namespace Blender
 
 // -------------------------------------------------------------------------------------------
 /** Load blenders official binary format. The actual file structure (the `DNA` how they
  *  call it is outsourced to BlenderDNA.cpp/BlenderDNA.h. This class only performs the
  *  conversion from intermediate format to aiScene. */
 // -------------------------------------------------------------------------------------------
-class BlenderImporter : public BaseImporter, public LogFunctions<BlenderImporter>
-{
+class BlenderImporter : public BaseImporter, public LogFunctions<BlenderImporter> {
 public:
     BlenderImporter();
-    ~BlenderImporter();
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+    ~BlenderImporter() override;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
 
 protected:
-    const aiImporterDesc* GetInfo () const;
-    void SetupProperties(const Importer* pImp);
-    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
-    void ParseBlendFile(Blender::FileDatabase& out, std::shared_ptr<IOStream> stream);
-    void ExtractScene(Blender::Scene& out, const Blender::FileDatabase& file);
-    void ConvertBlendFile(aiScene* out, const Blender::Scene& in, const Blender::FileDatabase& file);
+    const aiImporterDesc *GetInfo() const override;
+    void SetupProperties(const Importer *pImp) override;
+    void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
+    void ParseBlendFile(Blender::FileDatabase &out, std::shared_ptr<IOStream> stream);
+    void ExtractScene(Blender::Scene &out, const Blender::FileDatabase &file);
+    void ParseSubCollection(const Blender::Scene &in, aiNode *root, std::shared_ptr<Blender::Collection> collection, Blender::ConversionData &conv_data);
+    void ConvertBlendFile(aiScene *out, const Blender::Scene &in, const Blender::FileDatabase &file);
 
 private:
-    aiNode* ConvertNode(const Blender::Scene& in,
-        const Blender::Object* obj,
-        Blender::ConversionData& conv_info,
-        const aiMatrix4x4& parentTransform
-    );
+    aiNode *ConvertNode(const Blender::Scene &in,
+            const Blender::Object *obj,
+            Blender::ConversionData &conv_info,
+            const aiMatrix4x4 &parentTransform);
 
     // --------------------
-    void ConvertMesh(const Blender::Scene& in,
-        const Blender::Object* obj,
-        const Blender::Mesh* mesh,
-        Blender::ConversionData& conv_data,
-        Blender::TempArray<std::vector,aiMesh>& temp
-    );
+    void ConvertMesh(const Blender::Scene &in,
+            const Blender::Object *obj,
+            const Blender::Mesh *mesh,
+            Blender::ConversionData &conv_data,
+            Blender::TempArray<std::vector, aiMesh> &temp);
 
     // --------------------
-    aiLight* ConvertLight(const Blender::Scene& in,
-        const Blender::Object* obj,
-        const Blender::Lamp* mesh,
-        Blender::ConversionData& conv_data
-    );
+    aiLight *ConvertLight(const Blender::Scene &in,
+            const Blender::Object *obj,
+            const Blender::Lamp *mesh,
+            Blender::ConversionData &conv_data);
 
     // --------------------
-    aiCamera* ConvertCamera(const Blender::Scene& in,
-        const Blender::Object* obj,
-        const Blender::Camera* mesh,
-        Blender::ConversionData& conv_data
-    );
+    aiCamera *ConvertCamera(const Blender::Scene &in,
+            const Blender::Object *obj,
+            const Blender::Camera *mesh,
+            Blender::ConversionData &conv_data);
 
     // --------------------
     void BuildDefaultMaterial(
-        Blender::ConversionData& conv_data
-    );
+            Blender::ConversionData &conv_data);
 
+    // --------------------
     void AddBlendParams(
-        aiMaterial* result,
-        const Blender::Material* source
-    );
+            aiMaterial *result,
+            const Blender::Material *source);
 
+    // --------------------
     void BuildMaterials(
-        Blender::ConversionData& conv_data
-    );
+            Blender::ConversionData &conv_data);
 
     // --------------------
     void ResolveTexture(
-        aiMaterial* out,
-        const Blender::Material* mat,
-        const Blender::MTex* tex,
-        Blender::ConversionData& conv_data
-    );
+            aiMaterial *out,
+            const Blender::Material *mat,
+            const Blender::MTex *tex,
+            Blender::ConversionData &conv_data);
 
     // --------------------
     void ResolveImage(
-        aiMaterial* out,
-        const Blender::Material* mat,
-        const Blender::MTex* tex,
-        const Blender::Image* img,
-        Blender::ConversionData& conv_data
-    );
+            aiMaterial *out,
+            const Blender::Material *mat,
+            const Blender::MTex *tex,
+            const Blender::Image *img,
+            Blender::ConversionData &conv_data);
 
+    // --------------------
     void AddSentinelTexture(
-        aiMaterial* out,
-        const Blender::Material* mat,
-        const Blender::MTex* tex,
-        Blender::ConversionData& conv_data
-    );
+            aiMaterial *out,
+            const Blender::Material *mat,
+            const Blender::MTex *tex,
+            Blender::ConversionData &conv_data);
 
 private: // static stuff, mostly logging and error reporting.
-
     // --------------------
-    static void CheckActualType(const Blender::ElemBase* dt,
-        const char* check
-    );
+    static void CheckActualType(const Blender::ElemBase *dt,
+            const char *check);
 
     // --------------------
-    static void NotSupportedObjectType(const Blender::Object* obj,
-        const char* type
-    );
-
+    static void NotSupportedObjectType(const Blender::Object *obj,
+            const char *type);
 
 private:
-
-    Blender::BlenderModifierShowcase* modifier_cache;
+    Blender::BlenderModifierShowcase *modifier_cache;
 
 }; // !class BlenderImporter
 

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 1 - 1
code/AssetLib/Blender/BlenderModifier.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 55 - 2
code/AssetLib/Blender/BlenderScene.cpp

@@ -49,8 +49,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "BlenderDNA.h"
 #include "BlenderSceneGen.h"
 
-using namespace Assimp;
-using namespace Assimp::Blender;
+namespace Assimp {
+namespace Blender {
 
 //--------------------------------------------------------------------------------
 template <>
@@ -94,6 +94,52 @@ void Structure ::Convert<Group>(
     db.reader->IncPtr(size);
 }
 
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<CollectionObject>(
+        CollectionObject &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
+    {
+        //std::shared_ptr<CollectionObject> prev;
+        //ReadFieldPtr<ErrorPolicy_Fail>(prev, "*prev", db);
+        //dest.prev = prev.get();
+
+        std::shared_ptr<Object> ob;
+        ReadFieldPtr<ErrorPolicy_Igno>(ob, "*ob", db);
+        dest.ob = ob.get();
+    }
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<CollectionChild>(
+        CollectionChild &dest,
+        const FileDatabase &db) const {
+
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.prev, "*prev", db);
+    ReadFieldPtr<ErrorPolicy_Fail>(dest.next, "*next", db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.collection, "*collection", db);
+
+    db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <>
+void Structure::Convert<Collection>(
+        Collection &dest,
+        const FileDatabase &db) const {
+
+    ReadField<ErrorPolicy_Fail>(dest.id, "id", db);
+    ReadField<ErrorPolicy_Fail>(dest.gobject, "gobject", db);
+    ReadField<ErrorPolicy_Fail>(dest.children, "children", db);
+
+    db.reader->IncPtr(size);
+}
+
 //--------------------------------------------------------------------------------
 template <>
 void Structure ::Convert<MTex>(
@@ -660,6 +706,7 @@ void Structure ::Convert<Scene>(
     ReadFieldPtr<ErrorPolicy_Warn>(dest.camera, "*camera", db);
     ReadFieldPtr<ErrorPolicy_Warn>(dest.world, "*world", db);
     ReadFieldPtr<ErrorPolicy_Warn>(dest.basact, "*basact", db);
+    ReadFieldPtr<ErrorPolicy_Warn>(dest.master_collection, "*master_collection", db);
     ReadField<ErrorPolicy_Igno>(dest.base, "base", db);
 
     db.reader->IncPtr(size);
@@ -833,6 +880,12 @@ void DNA::RegisterConverters() {
     converters["Image"] = DNA::FactoryPair(&Structure::Allocate<Image>, &Structure::Convert<Image>);
     converters["CustomData"] = DNA::FactoryPair(&Structure::Allocate<CustomData>, &Structure::Convert<CustomData>);
     converters["CustomDataLayer"] = DNA::FactoryPair(&Structure::Allocate<CustomDataLayer>, &Structure::Convert<CustomDataLayer>);
+    converters["Collection"] = DNA::FactoryPair(&Structure::Allocate<Collection>, &Structure::Convert<Collection>);
+    converters["CollectionChild"] = DNA::FactoryPair(&Structure::Allocate<CollectionChild>, &Structure::Convert<CollectionChild>);
+    converters["CollectionObject"] = DNA::FactoryPair(&Structure::Allocate<CollectionObject>, &Structure::Convert<CollectionObject>);
 }
 
+} // namespace Blender
+} //namespace Assimp
+
 #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 24 - 2
code/AssetLib/Blender/BlenderScene.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -107,6 +107,7 @@ namespace Blender {
 struct Object;
 struct MTex;
 struct Image;
+struct Collection;
 
 #include <memory>
 
@@ -147,6 +148,26 @@ struct Group : ElemBase {
     std::shared_ptr<GroupObject> gobject;
 };
 
+// -------------------------------------------------------------------------------
+struct CollectionObject : ElemBase {
+    //CollectionObject* prev;
+    std::shared_ptr<CollectionObject> next;
+    Object *ob;
+};
+
+// -------------------------------------------------------------------------------
+struct CollectionChild : ElemBase {
+    std::shared_ptr<CollectionChild> next, prev;
+    std::shared_ptr<Collection> collection;
+};
+
+// -------------------------------------------------------------------------------
+struct Collection : ElemBase {
+    ID id FAIL;
+    ListBase gobject; // CollectionObject
+    ListBase children; // CollectionChild
+};
+
 // -------------------------------------------------------------------------------
 struct World : ElemBase {
     ID id FAIL;
@@ -729,11 +750,12 @@ struct Scene : ElemBase {
     std::shared_ptr<Object> camera WARN;
     std::shared_ptr<World> world WARN;
     std::shared_ptr<Base> basact WARN;
+    std::shared_ptr<Collection> master_collection WARN;
 
     ListBase base;
 
     Scene() :
-            ElemBase(), camera(), world(), basact() {
+            ElemBase(), camera(), world(), basact(), master_collection() {
         // empty
     }
 };

+ 6 - 0
code/AssetLib/Blender/BlenderSceneGen.h

@@ -62,6 +62,12 @@ template <> void Structure :: Convert<Group> (
     ) const
 ;
 
+template <> void Structure::Convert<Collection>(
+    Collection& dest,
+    const FileDatabase& db
+    ) const
+;
+
 template <> void Structure :: Convert<MTex> (
     MTex& dest,
     const FileDatabase& db

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 1 - 1
code/AssetLib/Blender/BlenderTessellator.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 13 - 2
code/AssetLib/C4D/C4DImporter.cpp

@@ -106,14 +106,25 @@ static const aiImporterDesc desc = {
 
 
 // ------------------------------------------------------------------------------------------------
-bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
+C4DImporter::C4DImporter()
+: BaseImporter() {
+    // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+C4DImporter::~C4DImporter() {
+    // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+bool C4DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const {
     const std::string& extension = GetExtension(pFile);
     if (extension == "c4d") {
         return true;
     } else if ((!extension.length() || checkSig) && pIOHandler)   {
         // TODO
     }
-
+    
     return false;
 }
 

+ 7 - 15
code/AssetLib/COB/COBLoader.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -103,17 +103,9 @@ COBImporter::~COBImporter() {
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool COBImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
-    const std::string &extension = GetExtension(pFile);
-    if (extension == "cob" || extension == "scn" || extension == "COB" || extension == "SCN") {
-        return true;
-    }
-
-    else if ((!extension.length() || checkSig) && pIOHandler) {
-        const char *tokens[] = { "Caligary" };
-        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
-    }
-    return false;
+bool COBImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+    static const char *tokens[] = { "Caligary" };
+    return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -586,7 +578,7 @@ void COBImporter::ReadUnit_Ascii(Scene &out, LineSplitter &splitter, const Chunk
         return;
     }
 
-    // parent chunks preceede their childs, so we should have the
+    // parent chunks preceede their children, so we should have the
     // corresponding chunk already.
     for (std::shared_ptr<Node> &nd : out.nodes) {
         if (nd->id == nfo.parent_id) {
@@ -668,7 +660,7 @@ void COBImporter::ReadCame_Ascii(Scene &out, LineSplitter &splitter, const Chunk
 
     ReadBasicNodeInfo_Ascii(msh, ++splitter, nfo);
 
-    // skip the next line, we don't know this differenciation between a
+    // skip the next line, we don't know this differentiation between a
     // standard camera and a panoramic camera.
     ++splitter;
 }
@@ -1169,7 +1161,7 @@ void COBImporter::ReadUnit_Binary(COB::Scene &out, StreamReaderLE &reader, const
 
     const chunk_guard cn(nfo, reader);
 
-    // parent chunks preceede their childs, so we should have the
+    // parent chunks preceede their children, so we should have the
     // corresponding chunk already.
     for (std::shared_ptr<Node> &nd : out.nodes) {
         if (nd->id == nfo.parent_id) {

+ 53 - 57
code/AssetLib/COB/COBLoader.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -51,104 +51,100 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 struct aiNode;
 
-namespace Assimp    {
-    class LineSplitter;
+namespace Assimp {
+class LineSplitter;
 
-    // TinyFormatter.h
-    namespace Formatter {
-        template <typename T,typename TR, typename A> class basic_formatter;
-        typedef class basic_formatter< char, std::char_traits<char>, std::allocator<char> > format;
-    }
+// TinyFormatter.h
+namespace Formatter {
+template <typename T, typename TR, typename A>
+class basic_formatter;
+typedef class basic_formatter<char, std::char_traits<char>, std::allocator<char>> format;
+} // namespace Formatter
 
-    // COBScene.h
-    namespace COB {
-        struct ChunkInfo;
-        struct Node;
-        struct Scene;
-    }
+// COBScene.h
+namespace COB {
+struct ChunkInfo;
+struct Node;
+struct Scene;
+} // namespace COB
 
 // -------------------------------------------------------------------------------------------
 /** Importer class to load TrueSpace files (cob,scn) up to v6.
  *
  *  Currently relatively limited, loads only ASCII files and needs more test coverage. */
 // -------------------------------------------------------------------------------------------
-class COBImporter : public BaseImporter
-{
+class COBImporter : public BaseImporter {
 public:
     COBImporter();
-    ~COBImporter();
+    ~COBImporter() override;
 
     // --------------------
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
-        bool checkSig) const;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
+            bool checkSig) const override;
 
 protected:
-
     // --------------------
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc *GetInfo() const override;
 
     // --------------------
-    void SetupProperties(const Importer* pImp);
+    void SetupProperties(const Importer *pImp) override;
 
     // --------------------
-    void InternReadFile( const std::string& pFile, aiScene* pScene,
-        IOSystem* pIOHandler);
+    void InternReadFile(const std::string &pFile, aiScene *pScene,
+            IOSystem *pIOHandler) override;
 
 private:
-
     // -------------------------------------------------------------------
     /** Prepend 'COB: ' and throw msg.*/
-    AI_WONT_RETURN static void ThrowException(const std::string& msg) AI_WONT_RETURN_SUFFIX;
+    AI_WONT_RETURN static void ThrowException(const std::string &msg) AI_WONT_RETURN_SUFFIX;
 
     // -------------------------------------------------------------------
     /** @brief Read from an ascii scene/object file
      *  @param out Receives output data.
      *  @param stream Stream to read from. */
-    void ReadAsciiFile(COB::Scene& out, StreamReaderLE* stream);
+    void ReadAsciiFile(COB::Scene &out, StreamReaderLE *stream);
 
     // -------------------------------------------------------------------
     /** @brief Read from a binary scene/object file
      *  @param out Receives output data.
      *  @param stream Stream to read from.  */
-    void ReadBinaryFile(COB::Scene& out, StreamReaderLE* stream);
+    void ReadBinaryFile(COB::Scene &out, StreamReaderLE *stream);
 
     // Conversion to Assimp output format
-
-    aiNode* BuildNodes(const COB::Node& root,const COB::Scene& scin,aiScene* fill);
+    aiNode *BuildNodes(const COB::Node &root, const COB::Scene &scin, aiScene *fill);
 
 private:
     // ASCII file support
 
-    void UnsupportedChunk_Ascii(LineSplitter& splitter, const COB::ChunkInfo& nfo, const char* name);
-    void ReadChunkInfo_Ascii(COB::ChunkInfo& out, const LineSplitter& splitter);
-    void ReadBasicNodeInfo_Ascii(COB::Node& msh, LineSplitter& splitter, const COB::ChunkInfo& nfo);
-    template <typename T> void ReadFloat3Tuple_Ascii(T& fill, const char** in);
-
-    void ReadPolH_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
-    void ReadBitM_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
-    void ReadMat1_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
-    void ReadGrou_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
-    void ReadBone_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
-    void ReadCame_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
-    void ReadLght_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
-    void ReadUnit_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
-    void ReadChan_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
-
+    void UnsupportedChunk_Ascii(LineSplitter &splitter, const COB::ChunkInfo &nfo, const char *name);
+    void ReadChunkInfo_Ascii(COB::ChunkInfo &out, const LineSplitter &splitter);
+    void ReadBasicNodeInfo_Ascii(COB::Node &msh, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    template <typename T>
+    void ReadFloat3Tuple_Ascii(T &fill, const char **in);
+
+    void ReadPolH_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadBitM_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadMat1_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadGrou_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadBone_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadCame_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadLght_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadUnit_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
+    void ReadChan_Ascii(COB::Scene &out, LineSplitter &splitter, const COB::ChunkInfo &nfo);
 
     // Binary file support
 
-    void UnsupportedChunk_Binary(StreamReaderLE& reader, const COB::ChunkInfo& nfo, const char* name);
-    void ReadString_Binary(std::string& out, StreamReaderLE& reader);
-    void ReadBasicNodeInfo_Binary(COB::Node& msh, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
-
-    void ReadPolH_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
-    void ReadBitM_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
-    void ReadMat1_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
-    void ReadCame_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
-    void ReadLght_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
-    void ReadGrou_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
-    void ReadUnit_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
-
+    void UnsupportedChunk_Binary(StreamReaderLE &reader, const COB::ChunkInfo &nfo, const char *name);
+    void ReadString_Binary(std::string &out, StreamReaderLE &reader);
+    void ReadBasicNodeInfo_Binary(COB::Node &msh, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+
+    void ReadPolH_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadBitM_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadMat1_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadCame_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadLght_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadGrou_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
+    void ReadUnit_Binary(COB::Scene &out, StreamReaderLE &reader, const COB::ChunkInfo &nfo);
 
 }; // !class COBImporter
 

+ 5 - 6
code/AssetLib/COB/COBScene.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,16 +42,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file  COBScene.h
 *  @brief Utilities for the COB importer.
 */
+#pragma once
 #ifndef INCLUDED_AI_COB_SCENE_H
 #define INCLUDED_AI_COB_SCENE_H
 
-#include <memory>
-#include <deque>
-#include <map>
-
 #include <assimp/BaseImporter.h>
 #include <assimp/material.h>
 
+#include <deque>
+#include <map>
+
 namespace Assimp {
 namespace COB {
 

+ 4 - 13
code/AssetLib/CSM/CSMLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 
@@ -90,19 +90,10 @@ CSMImporter::~CSMImporter()
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const
 {
-    // check file extension
-    const std::string extension = GetExtension(pFile);
-
-    if( extension == "csm")
-        return true;
-
-    if ((checkSig || !extension.length()) && pIOHandler) {
-        const char* tokens[] = {"$Filename"};
-        return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
-    }
-    return false;
+    static const char* tokens[] = {"$Filename"};
+    return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens));
 }
 
 // ------------------------------------------------------------------------------------------------

+ 12 - 16
code/AssetLib/CSM/CSMLoader.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -48,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <assimp/BaseImporter.h>
 
-namespace Assimp    {
+namespace Assimp {
 
 // ---------------------------------------------------------------------------
 /** Importer class to load MOCAPs in CharacterStudio Motion format.
@@ -59,35 +58,32 @@ namespace Assimp    {
  *  Link to file format specification:
  *  <max_8_dvd>\samples\Motion\Docs\CSM.rtf
 */
-class CSMImporter : public BaseImporter
-{
+class CSMImporter : public BaseImporter {
 public:
     CSMImporter();
-    ~CSMImporter();
-
+    ~CSMImporter() override;
 
-public:
     // -------------------------------------------------------------------
-    bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
-        bool checkSig) const;
+    bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
+            bool checkSig) const override;
 
 protected:
-
     // -------------------------------------------------------------------
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc *GetInfo() const override;
 
     // -------------------------------------------------------------------
-    void SetupProperties(const Importer* pImp);
+    void SetupProperties(const Importer *pImp) override;
 
     // -------------------------------------------------------------------
-    void InternReadFile( const std::string& pFile, aiScene* pScene,
-        IOSystem* pIOHandler);
+    void InternReadFile(const std::string &pFile, aiScene *pScene,
+            IOSystem *pIOHandler) override;
 
 private:
-
     bool noSkeletonMesh;
 
 }; // end of class CSMImporter
+
 } // end of namespace Assimp
+
 #endif // AI_AC3DIMPORTER_H_INC
 

+ 1 - 1
code/AssetLib/Collada/ColladaExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 1 - 1
code/AssetLib/Collada/ColladaExporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 1 - 1
code/AssetLib/Collada/ColladaHelper.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

+ 1 - 1
code/AssetLib/Collada/ColladaHelper.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

+ 14 - 36
code/AssetLib/Collada/ColladaLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -47,7 +47,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "ColladaParser.h"
 #include <assimp/ColladaMetaData.h>
 #include <assimp/CreateAnimMesh.h>
-#include <assimp/Defines.h>
 #include <assimp/ParsingUtils.h>
 #include <assimp/SkeletonMeshBuilder.h>
 #include <assimp/ZipArchiveIOSystem.h>
@@ -117,36 +116,15 @@ ColladaLoader::~ColladaLoader() {
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
-    // check file extension
-    const std::string extension = GetExtension(pFile);
-    const bool readSig = checkSig && (pIOHandler != nullptr);
-    if (!readSig) {
-        if (extension == "dae" || extension == "zae") {
-            return true;
-        }
-    } else {
-        // Look for a DAE file inside, but don't extract it
-        ZipArchiveIOSystem zip_archive(pIOHandler, pFile);
-        if (zip_archive.isOpen()) {
-            return !ColladaParser::ReadZaeManifest(zip_archive).empty();
-        }
-    }
-
-    // XML - too generic, we need to open the file and search for typical keywords
-    if (extension == "xml" || !extension.length() || checkSig) {
-        /*  If CanRead() is called in order to check whether we
-         *  support a specific file extension in general pIOHandler
-         *  might be nullptr and it's our duty to return true here.
-         */
-        if (!pIOHandler) {
-            return true;
-        }
-        static const char *tokens[] = { "<collada" };
-        return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
+bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
+    // Look for a DAE file inside, but don't extract it
+    ZipArchiveIOSystem zip_archive(pIOHandler, pFile);
+    if (zip_archive.isOpen()) {
+        return !ColladaParser::ReadZaeManifest(zip_archive).empty();
     }
 
-    return false;
+    static const char *tokens[] = { "<collada" };
+    return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -379,9 +357,9 @@ void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node
             out->mAngleInnerCone = AI_DEG_TO_RAD(srcLight->mFalloffAngle);
 
             // ... some extension magic.
-            if (srcLight->mOuterAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET * (1 - 1e-6f)) {
+            if (srcLight->mOuterAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET * (1 - ai_epsilon)) {
                 // ... some deprecation magic.
-                if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET * (1 - 1e-6f)) {
+                if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET * (1 - ai_epsilon)) {
                     // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess ....
                     // epsilon chosen to be 0.1
                     float f = 1.0f;
@@ -573,7 +551,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node
 
     // now place all mesh references we gathered in the target node
     pTarget->mNumMeshes = static_cast<unsigned int>(newMeshRefs.size());
-    if (newMeshRefs.size()) {
+    if (!newMeshRefs.empty()) {
         struct UIntTypeConverter {
             unsigned int operator()(const size_t &v) const {
                 return static_cast<unsigned int>(v);
@@ -1087,7 +1065,7 @@ void insertMorphTimeValue(std::vector<MorphTimeValues> &values, float time, floa
         return;
     }
     for (unsigned int i = 0; i < values.size(); i++) {
-        if (std::abs(time - values[i].mTime) < 1e-6f) {
+        if (std::abs(time - values[i].mTime) < ai_epsilon) {
             values[i].mKeys.push_back(k);
             return;
         } else if (time > values[i].mTime && time < values[i + 1].mTime) {
@@ -1544,7 +1522,7 @@ void ColladaLoader::AddTexture(aiMaterial &mat,
         map = -1;
         for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) {
             if (IsNumeric(*it)) {
-                map = strtoul10(&(*it));
+                map = strtoul10(&(*it));        
                 break;
             }
         }
@@ -1686,7 +1664,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/)
 
         // store the material
         mMaterialIndexByName[matIt->first] = newMats.size();
-        newMats.push_back(std::pair<Effect *, aiMaterial *>(&effect, mat));
+        newMats.emplace_back(&effect, mat);
     }
     // ScenePreprocessor generates a default material automatically if none is there.
     // All further code here in this loader works well without a valid material so

+ 1 - 1
code/AssetLib/Collada/ColladaLoader.h

@@ -4,7 +4,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 65 - 47
code/AssetLib/Collada/ColladaParser.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -331,7 +331,16 @@ void ColladaParser::ReadAssetInfo(XmlNode &node) {
         const std::string &currentName = currentNode.name();
         if (currentName == "unit") {
             mUnitSize = 1.f;
-            XmlParser::getRealAttribute(currentNode, "meter", mUnitSize);
+            std::string tUnitSizeString;
+            if (XmlParser::getStdStrAttribute(currentNode, "meter", tUnitSizeString)) {
+                try {
+                    fast_atoreal_move<ai_real>(tUnitSizeString.data(), mUnitSize);
+                } catch (const DeadlyImportError& die) {
+                    std::string warning("Collada: Failed to parse meter parameter to real number. Exception:\n");
+                    warning.append(die.what());
+                    ASSIMP_LOG_WARN(warning.data());
+                }
+            }
         } else if (currentName == "up_axis") {
             std::string v;
             if (!XmlParser::getValueAsString(currentNode, v)) {
@@ -914,7 +923,7 @@ void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) {
         if (currentName == "instance_effect") {
             std::string url;
             readUrlAttribute(currentNode, url);
-            pMaterial.mEffect = url.c_str();
+            pMaterial.mEffect = url;
         }
     }
 }
@@ -924,6 +933,8 @@ void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) {
 void ColladaParser::ReadLight(XmlNode &node, Collada::Light &pLight) {
     XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode);
     XmlNode currentNode;
+    // TODO: Check the current technique and skip over unsupported extra techniques
+
     while (xmlIt.getNext(currentNode)) {
         const std::string &currentName = currentNode.name();
         if (currentName == "spot") {
@@ -949,33 +960,34 @@ void ColladaParser::ReadLight(XmlNode &node, Collada::Light &pLight) {
             content = fast_atoreal_move<ai_real>(content, (ai_real &)pLight.mColor.b);
             SkipSpacesAndLineEnd(&content);
         } else if (currentName == "constant_attenuation") {
-            XmlParser::getRealAttribute(currentNode, "constant_attenuation", pLight.mAttConstant);
+            XmlParser::getValueAsFloat(currentNode, pLight.mAttConstant);
         } else if (currentName == "linear_attenuation") {
-            XmlParser::getRealAttribute(currentNode, "linear_attenuation", pLight.mAttLinear);
+            XmlParser::getValueAsFloat(currentNode, pLight.mAttLinear);
         } else if (currentName == "quadratic_attenuation") {
-            XmlParser::getRealAttribute(currentNode, "quadratic_attenuation", pLight.mAttQuadratic);
+            XmlParser::getValueAsFloat(currentNode, pLight.mAttQuadratic);
         } else if (currentName == "falloff_angle") {
-            XmlParser::getRealAttribute(currentNode, "falloff_angle", pLight.mFalloffAngle);
+            XmlParser::getValueAsFloat(currentNode, pLight.mFalloffAngle);
         } else if (currentName == "falloff_exponent") {
-            XmlParser::getRealAttribute(currentNode, "falloff_exponent", pLight.mFalloffExponent);
+            XmlParser::getValueAsFloat(currentNode, pLight.mFalloffExponent);
         }
         // FCOLLADA extensions
         // -------------------------------------------------------
         else if (currentName == "outer_cone") {
-            XmlParser::getRealAttribute(currentNode, "outer_cone", pLight.mOuterAngle);
-        } else if (currentName == "penumbra_angle") { // ... and this one is even deprecated
-            XmlParser::getRealAttribute(currentNode, "penumbra_angle", pLight.mPenumbraAngle);
+            XmlParser::getValueAsFloat(currentNode, pLight.mOuterAngle);
+        } else if (currentName == "penumbra_angle") { // this one is deprecated, now calculated using outer_cone
+            XmlParser::getValueAsFloat(currentNode, pLight.mPenumbraAngle);
         } else if (currentName == "intensity") {
-            XmlParser::getRealAttribute(currentNode, "intensity", pLight.mIntensity);
-        } else if (currentName == "falloff") {
-            XmlParser::getRealAttribute(currentNode, "falloff", pLight.mOuterAngle);
+            XmlParser::getValueAsFloat(currentNode, pLight.mIntensity);
+        }
+        else if (currentName == "falloff") {
+            XmlParser::getValueAsFloat(currentNode, pLight.mOuterAngle);
         } else if (currentName == "hotspot_beam") {
-            XmlParser::getRealAttribute(currentNode, "hotspot_beam", pLight.mFalloffAngle);
+            XmlParser::getValueAsFloat(currentNode, pLight.mFalloffAngle);
         }
         // OpenCOLLADA extensions
         // -------------------------------------------------------
         else if (currentName == "decay_falloff") {
-            XmlParser::getRealAttribute(currentNode, "decay_falloff", pLight.mOuterAngle);
+            XmlParser::getValueAsFloat(currentNode, pLight.mOuterAngle);
         }
     }
 }
@@ -1109,7 +1121,7 @@ void ColladaParser::ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEff
         // GOOGLEEARTH/OKINO extensions
         // -------------------------------------------------------
         else if (currentName == "double_sided")
-            XmlParser::getBoolAttribute(currentNode, currentName.c_str(), pEffect.mDoubleSided);
+            XmlParser::getValueAsBool(currentNode, pEffect.mDoubleSided);
 
         // FCOLLADA extensions
         // -------------------------------------------------------
@@ -1121,9 +1133,9 @@ void ColladaParser::ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEff
         // MAX3D extensions
         // -------------------------------------------------------
         else if (currentName == "wireframe") {
-            XmlParser::getBoolAttribute(currentNode, currentName.c_str(), pEffect.mWireframe);
+            XmlParser::getValueAsBool(currentNode, pEffect.mWireframe);
         } else if (currentName == "faceted") {
-            XmlParser::getBoolAttribute(currentNode, currentName.c_str(), pEffect.mFaceted);
+            XmlParser::getValueAsBool(currentNode, pEffect.mFaceted);
         }
     }
 }
@@ -1142,23 +1154,23 @@ void ColladaParser::ReadSamplerProperties(XmlNode &node, Sampler &out) {
         // MAYA extensions
         // -------------------------------------------------------
         if (currentName == "wrapU") {
-            XmlParser::getBoolAttribute(currentNode, currentName.c_str(), out.mWrapU);
+            XmlParser::getValueAsBool(currentNode, out.mWrapU);
         } else if (currentName == "wrapV") {
-            XmlParser::getBoolAttribute(currentNode, currentName.c_str(), out.mWrapV);
+            XmlParser::getValueAsBool(currentNode, out.mWrapV);
         } else if (currentName == "mirrorU") {
-            XmlParser::getBoolAttribute(currentNode, currentName.c_str(), out.mMirrorU);
+            XmlParser::getValueAsBool(currentNode, out.mMirrorU);
         } else if (currentName == "mirrorV") {
-            XmlParser::getBoolAttribute(currentNode, currentName.c_str(), out.mMirrorV);
+            XmlParser::getValueAsBool(currentNode, out.mMirrorV);
         } else if (currentName == "repeatU") {
-            XmlParser::getRealAttribute(currentNode, currentName.c_str(), out.mTransform.mScaling.x);
+            XmlParser::getValueAsFloat(currentNode, out.mTransform.mScaling.x);
         } else if (currentName == "repeatV") {
-            XmlParser::getRealAttribute(currentNode, currentName.c_str(), out.mTransform.mScaling.y);
+            XmlParser::getValueAsFloat(currentNode, out.mTransform.mScaling.y);
         } else if (currentName == "offsetU") {
-            XmlParser::getRealAttribute(currentNode, currentName.c_str(), out.mTransform.mTranslation.x);
+            XmlParser::getValueAsFloat(currentNode, out.mTransform.mTranslation.x);
         } else if (currentName == "offsetV") {
-            XmlParser::getRealAttribute(currentNode, currentName.c_str(), out.mTransform.mTranslation.y);
+            XmlParser::getValueAsFloat(currentNode, out.mTransform.mTranslation.y);
         } else if (currentName == "rotateUV") {
-            XmlParser::getRealAttribute(currentNode, currentName.c_str(), out.mTransform.mRotation);
+            XmlParser::getValueAsFloat(currentNode, out.mTransform.mRotation);
         } else if (currentName == "blend_mode") {
             std::string v;
             XmlParser::getValueAsString(currentNode, v);
@@ -1178,14 +1190,14 @@ void ColladaParser::ReadSamplerProperties(XmlNode &node, Sampler &out) {
         // OKINO extensions
         // -------------------------------------------------------
         else if (currentName == "weighting") {
-            XmlParser::getRealAttribute(currentNode, currentName.c_str(), out.mWeighting);
+            XmlParser::getValueAsFloat(currentNode, out.mWeighting);
         } else if (currentName == "mix_with_previous_layer") {
-            XmlParser::getRealAttribute(currentNode, currentName.c_str(), out.mMixWithPrevious);
+            XmlParser::getValueAsFloat(currentNode, out.mMixWithPrevious);
         }
         // MAX3D extensions
         // -------------------------------------------------------
         else if (currentName == "amount") {
-            XmlParser::getRealAttribute(currentNode, currentName.c_str(), out.mWeighting);
+            XmlParser::getValueAsFloat(currentNode, out.mWeighting);
         }
     }
 }
@@ -2204,8 +2216,8 @@ void ColladaParser::ReadMaterialVertexInputBinding(XmlNode &node, Collada::Seman
 
 void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive) {
     // Attempt to load any undefined Collada::Image in ImageLibrary
-    for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) {
-        Collada::Image &image = (*it).second;
+    for (auto &it : mImageLibrary) {
+        Collada::Image &image = it.second;
 
         if (image.mImageData.empty()) {
             std::unique_ptr<IOStream> image_file(zip_archive.Open(image.mFileName.c_str()));
@@ -2239,20 +2251,26 @@ void ColladaParser::ReadNodeGeometry(XmlNode &node, Node *pNode) {
         if (currentName == "bind_material") {
             XmlNode techNode = currentNode.child("technique_common");
             if (techNode) {
-                XmlNode instanceMatNode = techNode.child("instance_material");
-                // read ID of the geometry subgroup and the target material
-                std::string group;
-                XmlParser::getStdStrAttribute(instanceMatNode, "symbol", group);
-                XmlParser::getStdStrAttribute(instanceMatNode, "target", url);
-                const char *urlMat = url.c_str();
-                Collada::SemanticMappingTable s;
-                if (urlMat[0] == '#')
-                    urlMat++;
-
-                s.mMatName = urlMat;
-                // store the association
-                instance.mMaterials[group] = s;
-                ReadMaterialVertexInputBinding(instanceMatNode, s);
+                for (XmlNode instanceMatNode = techNode.child("instance_material"); instanceMatNode; instanceMatNode = instanceMatNode.next_sibling())
+                {
+                    const std::string &instance_name = instanceMatNode.name();
+                    if (instance_name == "instance_material")
+                    {
+                        // read ID of the geometry subgroup and the target material
+                        std::string group;
+                        XmlParser::getStdStrAttribute(instanceMatNode, "symbol", group);
+                        XmlParser::getStdStrAttribute(instanceMatNode, "target", url);
+                        const char *urlMat = url.c_str();
+                        Collada::SemanticMappingTable s;
+                        if (urlMat[0] == '#')
+                            urlMat++;
+
+                        s.mMatName = urlMat;
+                        // store the association
+                        instance.mMaterials[group] = s;
+                        ReadMaterialVertexInputBinding(instanceMatNode, s);
+                    }
+                }
             }
         }
     }

+ 2 - 1
code/AssetLib/Collada/ColladaParser.h

@@ -2,7 +2,7 @@
  Open Asset Import Library (assimp)
  ----------------------------------------------------------------------
 
- Copyright (c) 2006-2021, assimp team
+ Copyright (c) 2006-2022, assimp team
 
  All rights reserved.
 
@@ -43,6 +43,7 @@
  *  @brief Defines the parser helper class for the collada loader
  */
 
+#pragma once
 #ifndef AI_COLLADAPARSER_H_INC
 #define AI_COLLADAPARSER_H_INC
 

+ 1 - 1
code/AssetLib/DXF/DXFHelper.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 9 - 13
code/AssetLib/DXF/DXFLoader.cpp

@@ -3,7 +3,7 @@
 Open Asset Import Library (assimp)
 ---------------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -123,18 +123,9 @@ DXFImporter::~DXFImporter() {
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
-bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool checkSig ) const {
-    const std::string& extension = GetExtension( filename );
-    if ( extension == desc.mFileExtensions ) {
-        return true;
-    }
-
-    if ( extension.empty() || checkSig ) {
-        const char *pTokens[] = { "SECTION", "HEADER", "ENDSEC", "BLOCKS" };
-        return BaseImporter::SearchFileHeaderForToken(pIOHandler, filename, pTokens, 4, 32 );
-    }
-
-    return false;
+bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool /*checkSig*/ ) const {
+    static const char *tokens[] = { "SECTION", "HEADER", "ENDSEC", "BLOCKS" };
+    return SearchFileHeaderForToken(pIOHandler, filename, tokens, AI_COUNT_OF(tokens), 32);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -378,6 +369,11 @@ void DXFImporter::ExpandBlockReferences(DXF::Block& bl,const DXF::BlockMap& bloc
         const DXF::Block& bl_src = *(*it).second;
 
         for (std::shared_ptr<const DXF::PolyLine> pl_in : bl_src.lines) {
+            if (!pl_in) {
+                ASSIMP_LOG_ERROR("DXF: PolyLine instance is nullptr, skipping.");
+                continue;
+            }
+            
             std::shared_ptr<DXF::PolyLine> pl_out = std::shared_ptr<DXF::PolyLine>(new DXF::PolyLine(*pl_in));
 
             if (bl_src.base.Length() || insert.scale.x!=1.f || insert.scale.y!=1.f || insert.scale.z!=1.f || insert.angle || insert.pos.Length()) {

+ 7 - 10
code/AssetLib/DXF/DXFLoader.h

@@ -2,8 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
-
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -43,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /** @file  DXFLoader.h
  *  @brief Declaration of the .dxf importer class.
  */
+#pragma once
 #ifndef AI_DXFLOADER_H_INCLUDED
 #define AI_DXFLOADER_H_INCLUDED
 
@@ -59,7 +59,7 @@ namespace DXF {
     struct Block;
     struct InsertBlock;
 
-    typedef std::map<std::string, const DXF::Block*> BlockMap;
+    using BlockMap = std::map<std::string, const DXF::Block*>;
 }
 
 // ---------------------------------------------------------------------------
@@ -69,29 +69,26 @@ namespace DXF {
 class DXFImporter : public BaseImporter {
 public:
     DXFImporter();
-    ~DXFImporter();
+    ~DXFImporter() override;
 
     // -------------------------------------------------------------------
     /** Returns whether the class can handle the format of the given file.
     * See BaseImporter::CanRead() for details.  */
     bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
-        bool checkSig) const;
+        bool checkSig) const override;
 
 protected:
     // -------------------------------------------------------------------
     /** Return importer meta information.
      * See #BaseImporter::GetInfo for the details*/
-    const aiImporterDesc* GetInfo () const;
+    const aiImporterDesc* GetInfo () const override;
 
     // -------------------------------------------------------------------
     /** Imports the given file into the given scene structure.
      * See BaseImporter::InternReadFile() for details */
-    void InternReadFile( const std::string& pFile,
-        aiScene* pScene,
-        IOSystem* pIOHandler);
+    void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
 
 private:
-
     // -----------------------------------------------------
     void SkipSection(DXF::LineReader& reader);
 

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 3 - 1
code/AssetLib/FBX/FBXCommon.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -80,8 +80,10 @@ enum TransformInheritance {
 
     TransformInheritance_MAX // end-of-enum sentinel
 };
+
 } // namespace FBX
 } // namespace Assimp
+
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 
 #endif // AI_FBXCOMMON_H_INC

+ 1 - 1
code/AssetLib/FBX/FBXCompileConfig.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 103 - 21
code/AssetLib/FBX/FBXConverter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -79,7 +79,7 @@ using namespace Util;
 
 #define MAGIC_NODE_TAG "_$AssimpFbx$"
 
-#define CONVERT_FBX_TIME(time) (static_cast<double>(time) * 1000.0 / 46186158000LL)
+#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
 
 FBXConverter::FBXConverter(aiScene *out, const Document &doc, bool removeEmptyBones) :
         defaultMaterialIndex(),
@@ -653,7 +653,7 @@ bool FBXConverter::NeedsComplexTransformationChain(const Model &model) {
     const PropertyTable &props = model.Props();
     bool ok;
 
-    const float zero_epsilon = 1e-6f;
+    const float zero_epsilon = ai_epsilon;
     const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
     for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
         const TransformationComp comp = static_cast<TransformationComp>(i);
@@ -917,8 +917,10 @@ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root
         } else if (line) {
             const std::vector<unsigned int> &indices = ConvertLine(*line, root_node);
             std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
-        } else {
+        } else if (geo) {
             FBXImporter::LogWarn("ignoring unrecognized geometry: ", geo->Name());
+        } else {
+            FBXImporter::LogWarn("skipping null geometry");
         }
     }
 
@@ -1126,7 +1128,7 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c
             *out_uv++ = aiVector3D(v.x, v.y, 0.0f);
         }
 
-        out_mesh->mTextureCoordsNames[i] = mesh.GetTextureCoordChannelName(i);
+        out_mesh->SetTextureCoordsName(i, aiString(mesh.GetTextureCoordChannelName(i)));
 
         out_mesh->mNumUVComponents[i] = 2;
     }
@@ -1265,7 +1267,7 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co
     const std::vector<aiVector3D> &normals = mesh.GetNormals();
     if (normals.size()) {
         ai_assert(normals.size() == vertices.size());
-        out_mesh->mNormals = new aiVector3D[vertices.size()];
+        out_mesh->mNormals = new aiVector3D[count_vertices];
     }
 
     // allocate tangents, binormals.
@@ -1293,8 +1295,8 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co
             ai_assert(tangents.size() == vertices.size());
             ai_assert(binormals->size() == vertices.size());
 
-            out_mesh->mTangents = new aiVector3D[vertices.size()];
-            out_mesh->mBitangents = new aiVector3D[vertices.size()];
+            out_mesh->mTangents = new aiVector3D[count_vertices];
+            out_mesh->mBitangents = new aiVector3D[count_vertices];
         }
     }
 
@@ -1306,7 +1308,7 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co
             break;
         }
 
-        out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
+        out_mesh->mTextureCoords[i] = new aiVector3D[count_vertices];
         out_mesh->mNumUVComponents[i] = 2;
     }
 
@@ -1318,7 +1320,7 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co
             break;
         }
 
-        out_mesh->mColors[i] = new aiColor4D[vertices.size()];
+        out_mesh->mColors[i] = new aiColor4D[count_vertices];
     }
 
     unsigned int cursor = 0, in_cursor = 0;
@@ -1593,7 +1595,7 @@ void FBXConverter::ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const
         bone_map.insert(std::pair<const std::string, aiBone *>(deformer_name, bone));
     }
 
-    ASSIMP_LOG_DEBUG("bone research: Indicies size: ", out_indices.size());
+    ASSIMP_LOG_DEBUG("bone research: Indices size: ", out_indices.size());
 
     // lookup must be populated in case something goes wrong
     // this also allocates bones to mesh instance outside
@@ -2068,6 +2070,7 @@ void FBXConverter::SetTextureProperties(aiMaterial *out_mat, const LayeredTextur
     TrySetTextureProperties(out_mat, layeredTextures, "ShininessExponent", aiTextureType_SHININESS, mesh);
     TrySetTextureProperties(out_mat, layeredTextures, "EmissiveFactor", aiTextureType_EMISSIVE, mesh);
     TrySetTextureProperties(out_mat, layeredTextures, "TransparencyFactor", aiTextureType_OPACITY, mesh);
+    TrySetTextureProperties(out_mat, layeredTextures, "ReflectionFactor", aiTextureType_METALNESS, mesh);
 }
 
 aiColor3D FBXConverter::GetColorPropertyFactored(const PropertyTable &props, const std::string &colorName,
@@ -2616,7 +2619,7 @@ void FBXConverter::ConvertAnimationStack(const AnimationStack &st) {
                     meshMorphAnim->mKeys[j].mNumValuesAndWeights = numValuesAndWeights;
                     meshMorphAnim->mKeys[j].mValues = new unsigned int[numValuesAndWeights];
                     meshMorphAnim->mKeys[j].mWeights = new double[numValuesAndWeights];
-                    meshMorphAnim->mKeys[j].mTime = CONVERT_FBX_TIME(animIt.first);
+                    meshMorphAnim->mKeys[j].mTime = CONVERT_FBX_TIME(animIt.first) * anim_fps;
                     for (unsigned int k = 0; k < numValuesAndWeights; k++) {
                         meshMorphAnim->mKeys[j].mValues[k] = keyData->values.at(k);
                         meshMorphAnim->mKeys[j].mWeights[k] = keyData->weights.at(k);
@@ -2634,8 +2637,8 @@ void FBXConverter::ConvertAnimationStack(const AnimationStack &st) {
         return;
     }
 
-    double start_time_fps = has_local_startstop ? CONVERT_FBX_TIME(start_time) : min_time;
-    double stop_time_fps = has_local_startstop ? CONVERT_FBX_TIME(stop_time) : max_time;
+    double start_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(start_time) * anim_fps) : min_time;
+    double stop_time_fps = has_local_startstop ? (CONVERT_FBX_TIME(stop_time) * anim_fps) : max_time;
 
     // adjust relative timing for animation
     for (unsigned int c = 0; c < anim->mNumChannels; c++) {
@@ -3125,7 +3128,12 @@ aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name,
         if (chain[i] == iterEnd)
             continue;
 
-        keyframeLists[i] = GetKeyframeList((*chain[i]).second, start, stop);
+        if (i == TransformationComp_Rotation || i == TransformationComp_PreRotation
+                || i == TransformationComp_PostRotation || i == TransformationComp_GeometricRotation) {
+            keyframeLists[i] = GetRotationKeyframeList((*chain[i]).second, start, stop);
+        } else {
+            keyframeLists[i] = GetKeyframeList((*chain[i]).second, start, stop);
+        }
 
         for (KeyFrameListList::const_iterator it = keyframeLists[i].begin(); it != keyframeLists[i].end(); ++it) {
             const KeyTimeList& times = *std::get<0>(*it);
@@ -3155,7 +3163,7 @@ aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name,
         InterpolateKeys(outTranslations, keytimes, keyframeLists[TransformationComp_Translation], defTranslate, maxTime, minTime);
     } else {
         for (size_t i = 0; i < keyCount; ++i) {
-            outTranslations[i].mTime = CONVERT_FBX_TIME(keytimes[i]);
+            outTranslations[i].mTime = CONVERT_FBX_TIME(keytimes[i]) * anim_fps;
             outTranslations[i].mValue = defTranslate;
         }
     }
@@ -3164,7 +3172,7 @@ aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name,
         InterpolateKeys(outRotations, keytimes, keyframeLists[TransformationComp_Rotation], defRotation, maxTime, minTime, rotOrder);
     } else {
         for (size_t i = 0; i < keyCount; ++i) {
-            outRotations[i].mTime = CONVERT_FBX_TIME(keytimes[i]);
+            outRotations[i].mTime = CONVERT_FBX_TIME(keytimes[i]) * anim_fps;
             outRotations[i].mValue = defQuat;
         }
     }
@@ -3173,13 +3181,14 @@ aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name,
         InterpolateKeys(outScales, keytimes, keyframeLists[TransformationComp_Scaling], defScale, maxTime, minTime);
     } else {
         for (size_t i = 0; i < keyCount; ++i) {
-            outScales[i].mTime = CONVERT_FBX_TIME(keytimes[i]);
+            outScales[i].mTime = CONVERT_FBX_TIME(keytimes[i]) * anim_fps;
             outScales[i].mValue = defScale;
         }
     }
 
     bool ok = false;
-    const float zero_epsilon = 1e-6f;
+    
+    const float zero_epsilon = ai_epsilon;
 
     const aiVector3D& preRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
     if (ok && preRotation.SquareLength() > zero_epsilon) {
@@ -3272,6 +3281,79 @@ FBXConverter::KeyFrameListList FBXConverter::GetKeyframeList(const std::vector<c
     return inputs; // pray for NRVO :-)
 }
 
+FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std::vector<const AnimationCurveNode *> &nodes,
+                                                                     int64_t start, int64_t stop) {
+    KeyFrameListList inputs;
+    inputs.reserve(nodes.size() * 3);
+
+    //give some breathing room for rounding errors
+    const int64_t adj_start = start - 10000;
+    const int64_t adj_stop = stop + 10000;
+
+    for (const AnimationCurveNode *node : nodes) {
+        ai_assert(node);
+
+        const AnimationCurveMap &curves = node->Curves();
+        for (const AnimationCurveMap::value_type &kv : curves) {
+
+            unsigned int mapto;
+            if (kv.first == "d|X") {
+                mapto = 0;
+            } else if (kv.first == "d|Y") {
+                mapto = 1;
+            } else if (kv.first == "d|Z") {
+                mapto = 2;
+            } else {
+                FBXImporter::LogWarn("ignoring scale animation curve, did not recognize target component");
+                continue;
+            }
+
+            const AnimationCurve *const curve = kv.second;
+            ai_assert(curve->GetKeys().size() == curve->GetValues().size());
+            ai_assert(curve->GetKeys().size());
+
+            //get values within the start/stop time window
+            std::shared_ptr<KeyTimeList> Keys(new KeyTimeList());
+            std::shared_ptr<KeyValueList> Values(new KeyValueList());
+            const size_t count = curve->GetKeys().size();
+
+            int64_t tp = curve->GetKeys().at(0);
+            float vp = curve->GetValues().at(0);
+            Keys->push_back(tp);
+            Values->push_back(vp);
+            if (count > 1) {
+                int64_t tc = curve->GetKeys().at(1);
+                float vc = curve->GetValues().at(1);
+                for (size_t n = 1; n < count; n++) {
+                    while (std::abs(vc - vp) >= 180.0f) {
+                        float step = std::floor(float(tc - tp) / (vc - vp) * 179.0f);
+                        int64_t tnew = tp + int64_t(step);
+                        float vnew = vp + (vc - vp) * step / float(tc - tp);
+                        if (tnew >= adj_start && tnew <= adj_stop) {
+                            Keys->push_back(tnew);
+                            Values->push_back(vnew);
+                        }
+                        tp = tnew;
+                        vp = vnew;
+                    }
+                    if (tc >= adj_start && tc <= adj_stop) {
+                        Keys->push_back(tc);
+                        Values->push_back(vc);
+                    }
+                    if (n + 1 < count) {
+                        tp = tc;
+                        vp = vc;
+                        tc = curve->GetKeys().at(n + 1);
+                        vc = curve->GetValues().at(n + 1);
+                    }
+                }
+            }
+            inputs.push_back(std::make_tuple(Keys, Values, mapto));
+        }
+    }
+    return inputs;
+}
+
 KeyTimeList FBXConverter::GetKeyTimeList(const KeyFrameListList &inputs) {
     ai_assert(!inputs.empty());
 
@@ -3362,7 +3444,7 @@ void FBXConverter::InterpolateKeys(aiVectorKey *valOut, const KeyTimeList &keys,
         }
 
         // magic value to convert fbx times to seconds
-        valOut->mTime = CONVERT_FBX_TIME(time);
+        valOut->mTime = CONVERT_FBX_TIME(time) * anim_fps;
 
         min_time = std::min(min_time, valOut->mTime);
         max_time = std::max(max_time, valOut->mTime);
@@ -3462,7 +3544,7 @@ void FBXConverter::ConvertRotationKeys(aiNodeAnim *na, const std::vector<const A
     ai_assert(nodes.size());
 
     // XXX see notes in ConvertScaleKeys()
-    const std::vector<KeyFrameList> &inputs = GetKeyframeList(nodes, start, stop);
+    const std::vector<KeyFrameList> &inputs = GetRotationKeyframeList(nodes, start, stop);
     const KeyTimeList &keys = GetKeyTimeList(inputs);
 
     na->mNumRotationKeys = static_cast<unsigned int>(keys.size());

+ 2 - 1
code/AssetLib/FBX/FBXConverter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -361,6 +361,7 @@ private:
 
     // ------------------------------------------------------------------------------------------------
     KeyFrameListList GetKeyframeList(const std::vector<const AnimationCurveNode*>& nodes, int64_t start, int64_t stop);
+    KeyFrameListList GetRotationKeyframeList(const std::vector<const AnimationCurveNode*>& nodes, int64_t start, int64_t stop);
 
     // ------------------------------------------------------------------------------------------------
     KeyTimeList GetKeyTimeList(const KeyFrameListList& inputs);

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

+ 2 - 2
code/AssetLib/FBX/FBXDocument.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.
@@ -693,7 +693,7 @@ private:
 typedef std::vector<int64_t> KeyTimeList;
 typedef std::vector<float> KeyValueList;
 
-/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */
+/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefore) */
 class AnimationCurve : public Object {
 public:
     AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc);

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 
 All rights reserved.

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

+ 1 - 1
code/AssetLib/FBX/FBXExportNode.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

+ 1 - 1
code/AssetLib/FBX/FBXExportProperty.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 

+ 4 - 4
code/AssetLib/FBX/FBXExporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2021, assimp team
+Copyright (c) 2006-2022, assimp team
 
 All rights reserved.
 
@@ -1709,7 +1709,7 @@ void FBXExporter::WriteObjects ()
             //p.AddP70string("UVSet", ""); // TODO: how should this work?
             p.AddP70bool("UseMaterial", 1);
             tnode.AddChild(p);
-            // can't easily detrmine which texture path will be correct,
+            // can't easily determine which texture path will be correct,
             // so just store what we have in every field.
             // these being incorrect is a common problem with FBX anyway.
             tnode.AddChild("FileName", texture_path);
@@ -1803,7 +1803,7 @@ void FBXExporter::WriteObjects ()
             blendchannel_uid, blendshape_name + FBX::SEPARATOR + "SubDeformer", "BlendShapeChannel"
         );
         sdnode.AddChild("Version", int32_t(100));
-        sdnode.AddChild("DeformPercent", float_t(0.0));
+        sdnode.AddChild("DeformPercent", float(0.0));
         FBX::Node p("Properties70");
         p.AddP70numberA("DeformPercent", 0.0);
         sdnode.AddChild(p);
@@ -1915,7 +1915,7 @@ void FBXExporter::WriteObjects ()
             // mark all parent nodes as skeleton as well,
             // up until we find the root node,
             // or else the node containing the mesh,
-            // or else the parent of a node containig the mesh.
+            // or else the parent of a node containing the mesh.
             for (
                 const aiNode* parent = n->mParent;
                 parent && parent != mScene->mRootNode;

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно